README.md in timber-1.0.13 vs README.md in timber-1.1.0

- old
+ new

@@ -1,200 +1,357 @@ -# Timber +# 🌲 Timber - Master your Ruby apps with structured logging <p align="center" style="background: #140f2a;"> <a href="http://github.com/timberio/timber-ruby"><img src="http://files.timber.io/images/ruby-library-readme-header.gif" height="469" /></a> </p> +[![ISC License](https://img.shields.io/badge/license-ISC-ff69b4.svg)](LICENSE.md) [![CircleCI](https://circleci.com/gh/timberio/timber-ruby.svg?style=shield&circle-token=:circle-token)](https://circleci.com/gh/timberio/timber-ruby/tree/master) [![Coverage Status](https://coveralls.io/repos/github/timberio/timber-ruby/badge.svg?branch=master)](https://coveralls.io/github/timberio/timber-ruby?branch=master) [![Code Climate](https://codeclimate.com/github/timberio/timber-ruby/badges/gpa.svg)](https://codeclimate.com/github/timberio/timber-ruby) [![View docs](https://img.shields.io/badge/docs-viewdocs-blue.svg?style=flat-square "Viewdocs")](http://www.rubydoc.info/github/timberio/timber-ruby) -**Timber is in beta testing. If interested, please email beta@timber.io** +--- +👉 **Timber is in beta testing, if interested in joining, please email us at +[beta@timber.io](mailto:beta@timber.io)** -1. [What is timber?](#what-is-timber) -2. [How it works](#how-it-works) -3. [Why timber?](#why-timber) -4. [Logging Custom Events](#logging-custom-events) -5. [The Timber Console / Pricing](#the-timber-console--pricing) -6. [Install](#install) +--- +Still logging raw text? Timber is a complete *structured* logging solution that you can setup in +minutes. It goes beyond traditional log management by focusing on data quality and modern +developer standards. -## What is Timber? +High quality logs, [a modern UX-first interface](https://timber.io), simple setup, +zero-maintenance, 6-month retention, and sane prices are just a few of the benefits Timber +offers. -Using your logs shouldn't be a time consuming, frustrating process! If you were like us, it goes -something like this: +To learn more, checkout out [timber.io](https://timber.io) or the +["why we built Timber"](http://moss-ibex2.cloudvent.net/blog/why-were-building-timber/) +blog post. -> I need to centralize my logs, where should I send them? Which provider should I use? -> Wow, this is expensive. Why is searching so difficult and time consuming? -> Would structuring my logs help? Which format should I use? Will they still be human readable? -> What if the structure changes? What about logs from 3rd party libraries? Ahhh!!! -Timber solves this by providing a complete, managed, end-to-end logging solution that marries -a beautiful, *fast*, console with libraries that automatically structure and enrich your logs. +## Overview +<details><summary><strong>What are the benefits of using Timber?</strong></summary><p> -## How it works +1. **Data quality.** The usefulness of your logs starts here. This is why we ship libraries that + structure logs from *within* your application; a fundamental difference from parsing. Not only + is it much more stable, but we can include data you couldn't obtain otherwise. +2. **Human readability.** Structuring your logs doesn't mean they have to be unreadable. Timber + *augments* your logs with structured data. Meaning we do not alter the original log message, + we simply attach metadata to it. And our console is specifically designed to give you access + to this data, without compromising readability. 😮 +3. **Reliable downstream consumption.** All log events adhere to a + [normalized, shared, schema](https://github.com/timberio/log-event-json-schema) that follows + [semantic versioning](http://semver.org/) and goes through a [standard release process](https://github.com/timberio/log-event-json-schema/releases). + This means you can *rely* on the structure of your logs and interact consistently with them + across apps of any language: queries, graphs, alerts, and other downstream consumers. +4. **Zero risk of code debt or lock-in.** Logging is a standard that has been around since the dawn + of computers. It's built into every language, framework, and library. Timber adheres strictly + to the default `Logger` interface. There are no special APIs, and no need to pepper your app + with Timber specific code. It's just better logging. If you choose to stop using Timber, you + can do so without consequence. +5. **Long term retention.** Timber is designed on modern big-data principles. As a result, we can + offer 6+ months of retention at prices cheaper than alternatives offering <1 month. + This allows you to unlock your logs for purposes beyond debugging. +--- -The Timber ruby library takes care of all of the log structuring madness. For example, -it turns this Rails log line: +</p></details> +<details><summary><strong>What specifically does the Timber library do?</strong></summary><p> + +1. Captures and structures your framework and 3rd party logs. (see next question) +2. Adds useful context to every log line. (see next question) +3. Provides a [framework for logging custom structured events](#what-about-custom-events). +4. Offers transport strategies to [send your logs](#send-your-logs) to the Timber service. + +--- + +</p></details> + +<details><summary><strong>What events does Timber capture & structure for me?</strong></summary><p> + +Out of the box you get everything in the [`Timber::Events`](lib/timber/events) namespace: + +1. [Controller Call Event](lib/timber/events/controller_call.rb) +2. [Exception Event](lib/timber/events/exception.rb) +3. [HTTP Client Request Event (net/http outgoing)](lib/timber/events/http_client_request.rb) +4. [HTTP Client Response Event (resposne from net/http outgoing)](lib/timber/events/http_client_response.rb) +5. [HTTP Server Request Event (incoming client request)](lib/timber/events/http_server_request.rb) +6. [HTTP Server Response Event (response to incoming client request)](lib/timber/events/http_server_response.rb) +7. [SQL Query Event](lib/timber/events/sql_query.rb) +8. [Template Render Event](lib/timber/events/template_render.rb) +9. ...more coming soon, [file an issue](https://github.com/timberio/timber-ruby/issues) to request. + +We also add context to every log, everything in the [`Timber::Contexts`](lib/timber/contexts) +namespace. Context is structured data representing the current environment when the log line was +written. It is included in every log line. Think of it like join data for your logs: + +1. [HTTP Context](lib/timber/contexts/http.rb) +2. [Organization Context](lib/timber/contexts/organization.rb) +3. [Process Context](lib/timber/contexts/process.rb) +4. [Server Context](lib/timber/contexts/server.rb) +5. [Runtime Context](lib/timber/contexts/runtime.rb) +5. [User Context](lib/timber/contexts/user.rb) +6. ...more coming soon, [file an issue](https://github.com/timberio/timber-ruby/issues) to request. + +--- + +</p></details> + +<details><summary><strong>What about my current log statements?</strong></summary><p> + +They'll continue to work as expected. Timber adheres strictly to the default `::Logger` interface +and will never deviate in *any* way. + +In fact, traditional log statements for non-meaningful events, debug statements, etc, are +encouraged. In cases where the data is meaningful, consider [logging a custom event](#usage). + +</p></details> + +## Usage + +<details><summary><strong>Basic logging</strong></summary><p> + +Use `Logger` as normal: + +```ruby +logger.info("My log message") + +# My log message @metadata {"level": "info", "context": {...}} ``` -Completed 200 OK in 117ms (Views: 85.2ms | ActiveRecord: 25.3ms) + +Timber will never deviate from the public `::Logger` interface in *any* way. + +--- + +</p></details> + +<details><summary><strong>Tagging logs</strong></summary><p> + +Need a quick and easy way to identify a log? Use tags!: + +```ruby +logger.info(message: "My log message", tag: "tag") + +# My log message @metadata {"level": "info", "tags": ["tag"], "context": {...}} ``` -Into this: +Multiple tags: -```javascript -{ - "dt": "2016-12-01T02:23:12.236543Z", - "level": "info", - "message": "Completed 200 OK in 117ms (Views: 85.2ms | ActiveRecord: 25.3ms)", - "context": { - "http": { - "method": "GET", - "path": "/checkout", - "remote_addr": "123.456.789.10", - "request_id": "abcd1234" - }, - "user": { // <---- http://i.giphy.com/EldfH1VJdbrwY.gif - "id": 2, - "name": "Ben Johnson", - "email": "ben@johnson.com" - } - }, - "event": { - "http_response": { - "status": 200, - "time_ms": 117 - } - } -} +```ruby +logger.info(message: "My log message", tags: ["tag1", "tag2"]) + +# My log message @metadata {"level": "info", "tags": ["tag1", "tag2"], "context": {...}} ``` -Notice we preserve the original log message. When viewing this event in the -[Timber Console](https://timber.io), you'll see the simple, human readable line with the -ability to view, and use, the attached structured data! Also, notice how rich the event is. -Beecause we're inside your application, we can capture data beyond what's in the log line. +Using `ActiveSupport::TaggedLogging`? It works with that as well: -(for a full list see [`Timber::Events`](lib/timber/events)) +```ruby +logger.tagged("tag") do + logger.info(message: "My log message", tags: ["important", "slow"]) +end +# My log message @metadata {"level": "info", "tags": ["tag"], "context": {...}} +``` -## Why Timber? +</p></details> -Glad you asked! :) +<details><summary><strong>Custom events</strong></summary><p> -1. Human readable logs *and* rich structured data. You don't have to choose. -2. Data beyond what's in the log line itself making your logs exceptionally useful. -3. Normalized log data. Timber enforces a strict schema, meaning your log data, across all apps - and languages will be normalized. -4. Absolutely no lock-in or risk of code debt. No fancy API, no proprietary data format locked - away in our servers, Timber is just good ol' loggin'. -5. Fully managed, all the way from the log messages to the console you use. No fragile parsing - rules or complicated interfaces. +1. Log a structured Hash (simplest) + ```ruby + Logger.warn message: "Payment rejected", payment_rejected: {customer_id: "abcd1234", amount: 100, reason: "Card expired"} -## Logging Custom Events + # Payment rejected @metadata {"level": "warn", "event": {"payment_rejected": {"customer_id": "abcd1234", "amount": 100, "reason": "Card expired"}}, "context": {...}} + ``` -> Another service? More lock-in? :*( + * The hash can *only* have a `:message` and "event type" key, where `:payment_rejected` is the event type in the above example. -Nope! Logging custom events is Just Logging™. Check it out: +2. Log a Struct (recommended) + Defining structs for your important events just feels oh so good :) It creates a strong contract + with down stream consumers and gives you compile time guarantees. + + ```ruby + PaymentRejectedEvent = Struct.new(:customer_id, :amount, :reason) do + def message; "Payment rejected for #{customer_id}"; end + def type; :payment_rejected; end + end + Logger.warn PaymentRejectedEvent.new("abcd1234", 100, "Card expired") + + # Payment rejected @metadata {"level": "warn", "event": {"payment_rejected": {"customer_id": "abcd1234", "amount": 100, "reason": "Card expired"}}, "context": {...}} + ``` + +* `:type` is how Timber classifies the event, it creates a namespace for the data you send. +* For more advanced examples see [`Timber::Logger`](lib/timber.logger.rb). +* Also, notice there is no mention of Timber in the above code. Just plain old logging. + +#### What about regular Hashes, JSON, or logfmt? + +Go for it! Timber will parse the data server side, but we *highly* recommend the above examples. +Providing a `:type` allows timber to classify the event, create a namespace for the data you +send, and make it easier to search, graph, alert, etc. + ```ruby -# Simple string (original Logger interface remains untouched) -Logger.warn "Payment rejected for customer abcd1234, reason: Card expired" +logger.info({key: "value"}) +# {"key": "value"} @metadata {"level": "info", "context": {...}} -# Structured hash -Logger.warn message: "Payment rejected", type: :payment_rejected, - data: {customer_id: "abcd1234", amount: 100, reason: "Card expired"} +logger.info('{"key": "value"}') +# {"key": "value"} @metadata {"level": "info", "context": {...}} -# Using a Struct -PaymentRejectedEvent = Struct.new(:customer_id, :amount, :reason) do - def message; "Payment rejected for #{customer_id}"; end - def type; :payment_rejected; end -end -Logger.warn PaymentRejectedEvent.new("abcd1234", 100, "Card expired") +logger.info('key=value') +# key=value @metadata {"level": "info", "context": {...}} ``` -(for more examples, see [the `Timber::Logger` docs](lib/timber/logger.rb)) +--- -No mention of Timber anywhere! +</p></details> +<details><summary><strong>Custom contexts</strong></summary><p> -## The Timber Console / Pricing +Context is structured data representing the current environment when the log line was written. +It is included in every log line. Think of it like join data for your logs. For example, the +`http.request_id` field is included in the context, allowing you to find all log lines related +to that request ID, if desired. This is in contrast to *only* showing log lines that contain this +value. -> What good is structured log data if you can't search and visualize it? +1. Add a Hash (simplest) -The [Timber Console](https://timber.io) is *fast*, modern, and beautiful console designed -specifically for this library. + ```ruby + Timber::CurrentContext.with({build: {version: "1.0.0"}}) do + logger.info("My log message") + end -A few example queries: + # My log message @metadata {"level": "info", "context": {"build": {"version": "1.0.0"}}} + ``` - 1. `context.user.email:ben@johnson.com` - Tail a specific user! - 2. `context.http.request_id:1234` - View *all* logs for a given HTTP request! - 3. `event.http_reponse.time_ms>3000` - Easily find outliers and have the proper context to resolve them! - 4. `level:warn` - Log levels in your logs. Imagine that! + This adds data to the context keyspaced by `build`. -> This is all gravy, but wouldn't the extra data get expensive? +2. Add a Struct (recommended) -If you opt to use the [Timber Console](https://timber.io), we only charge for -the size of the `message`, `dt`, and `event.custom` attributes. Everything else is -stored at no cost to you. [Say wha?!](http://i.giphy.com/l0HlL2vlfpWI0meJi.gif). This ensures -pricing remains predictable. We charge per GB sent to us and retained. No user limits, -no weird feature matrixes, just data. Finally, the data is yours, in a simple -non-proprietary JSON format that you can export to S3, Redshift, or any of our other integrations. + Just like events, we recommend defining your custom contexts. It makes a stronger contract + with downstream consumers. -For more details checkout out [timber.io](https://timber.io). + ```ruby + BuildContext = Struct.new(:version) do + def type; :build; end + end + build_context = BuildContext.new("1.0.0") + Timber::CurrentContext.with(build_context) do + logger.info("My log message") + end -## Install + # My log message @metadata {"level": "info", "context": {"build": {"version": "1.0.0"}}} + ``` -**Timber is in beta testing. If interested, please email beta@timber.io** +</p></details> -### 1. Install the gem: + +## Installation + ```ruby # Gemfile gem 'timber' ``` -### 2. Install the logger: -#### Heroku: +## Setup +<details><summary><strong>Rails >= 3.0</strong></summary><p> + +*Replace* any existing `config.logger=` calls in `config/environments/production.rb` with: + ```ruby -# config/environments/production.rb (or staging, etc) -config.logger = Timber::Logger.new(STDOUT) +# config/environments/production.rb + +config.logger = ActiveSupport::TaggedLogging.new(Timber::Logger.new(STDOUT)) ``` -The command to add your log drain will be displayed in the [Timber app](https://app.timber.io) -after you add your application. +* Prefer examples? Checkout our [Ruby / Rails example app](https://github.com/timberio/ruby-rails-example-app), + you can see all changes by [search for "timber-change"](https://github.com/timberio/ruby-rails-example-app/search?utf8=%E2%9C%93&q=timber-change&type=Code). -#### Non-Heroku: +--- -```ruby -# config/environments/production.rb (or staging, etc) -log_device = Timber::LogDevices::HTTP.new(ENV['TIMBER_KEY']) # key can be obtained by signing up at https://timber.io -config.logger = Timber::Logger.new(log_device) -``` +</p></details> -Your Timber application key will be displayed in the [Timber app](https://app.timber.io) -after you add your application. +<details><summary><strong>Other</strong></summary><p> +1. *Insert* the Timber probes: -*Other transport methods coming soon!* + This should be executed *immediately after* you have required your dependencies. + ```ruby + Timber::Probes.insert! + ``` -#### Rails TaggedLogging? +2. *Add* the Rack middlewares: -No probs! Use it as normal, Timber will even pull out the tags and include them in the `context`. + This should be included where you build your `Rack` application. Usually `config.ru`: -```ruby -config.logger = ActiveSupport::TaggedLogging.new(Timber::Logger.new(STDOUT)) -``` + ```ruby + # Most likely config.ru + Timber::RackMiddlewares.middlewares.each do |m| + use m + end + ``` + +2. *Instantiate* the Timber logger: + + This should be *globally* available to your application: + + ```ruby + logger = Timber::Logger.new(STDOUT) + ``` + +</p></details> + + +## Send your logs + +<details><summary><strong>Heroku (log drains)</strong></summary><p> + +The recommended strategy for Heroku is to setup a +[log drain](https://devcenter.heroku.com/articles/log-drains). To get your Timber log drain URL: + +👉 **[Add your app to Timber](https://app.timber.io)** + --- -<p align="center" style="background: #140f2a;"> +</p></details> + +<details><summary><strong>All other platforms (Network / HTTP)</strong></summary><p> + +1. *Specify* the Timber Network logger backend in `config/environments/production.rb`: + + Replace any existing `config.logger =` calls with: + + ```ruby + # config/environments/production.rb (or staging, etc) + + network_log_device = Timber::LogDevices::Network.new(ENV['TIMBER_LOGS_KEY']) + config.logger = Timber::Logger.new(network_log_device) # <-- Use network_log_device instead of STDOUT + ``` + +2. Obtain your Timber API :key: by **[adding your app in Timber](https://app.timber.io)**. + +3. Assign your API key to the `TIMBER_LOGS_KEY` environment variable. + +</p></details> + +<details><summary><strong>Advanced setup (syslog, file tailing agent, etc)</strong></summary><p> + +Checkout our [docs](https://timber.io/docs) for a comprehensive list of install instructions. + +</p></details> + + +--- + +<p align="center" style="background: #221f40;"> <a href="http://github.com/timberio/timber-ruby"><img src="http://files.timber.io/images/ruby-library-readme-log-truth.png" height="947" /></a> -</p> +</p> \ No newline at end of file