README.md in timber-2.0.24 vs README.md in timber-2.1.0.rc1

- old
+ new

@@ -1,9 +1,10 @@ -# 🌲 Timber - Log Better. Solve Problems Faster. +# 🌲 Timber - Automatic Ruby Structured Logging [![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) +[![Build Status](https://travis-ci.org/timberio/timber-ruby.svg?branch=master)](https://travis-ci.org/timberio/timber-ruby) +[![Build Status](https://travis-ci.org/timberio/timber-ruby.svg?branch=master)](https://travis-ci.org/timberio/timber-ruby) [![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 website](https://timber.io) * [Timber docs](https://timber.io/docs) @@ -11,66 +12,133 @@ * [Support](mailto:support@timber.io) ## Overview -Timber for Ruby is an optional upgrade you can install for Ruby apps on the -[Timber.io logging platform](https://timber.io). Instead of completely replacing your log messages, -Timber efficiently augments your logs with critical metadata. Turning them into -[rich events with context](https://timber.io/docs/ruby/events-and-context). This preserves the -readability of your logs while still adding the critical context needed to properly analyze your -logs. +Timber solves ruby structured logging so you don't have to. Go from raw text logs to rich +structured events in seconds. Spend more time focusing on your app and less time +focusing on logging. +1. **Easy setup.** - `bundle exec timber install`, [get setup in seconds](#installation). +2. **Automatically structures yours logs.** - Third-party and in-app logs are all structured + in a consistent format. See [how it works](#how-it-works) below. + +3. **Seamlessly integrates with popular libraries and frameworks.** - Rails, Rack, Devise, + Omniauth, etc. [Automatically captures user context, HTTP context, and event data.](#third-party-support) + +4. **Pairs with a modern console.** - Designed specifically for this librariy, instantly + usable, zero configuration. + + +## Installation + +1. In `Gemfile`, add the `timber` gem: + + ```ruby + gem 'timber', '~> 2.0' + ``` + +2. In your `shell`, run `bundle install` + +3. In your `shell`, run `bundle exec timber install` + + ## How it works -For example, Timber turns this familiar raw text log: +Let's start with an example. Timber turns this familiar raw text log line: ``` -Sent 200 in 45.ms +Sent 200 in 45.2ms ``` Into a rich [`http_server_response` event](https://timber.io/docs/ruby/events-and-context/http-server-response-event/). ``` -Sent 200 in 45.2ms @metadata {"dt": "2017-02-02T01:33:21.154345Z", "level": "info", "context": {"user": {"id": 1, "name": "Ben Johnson"}, "http": {"method": "GET", "host": "timber.io", "path": "/path", "request_id": "abcd1234"}}, "event": {"http_server_response": {"status": 200, "time_ms": 45.2}}} +Sent 200 in 45.2ms @metadata {"dt": "2017-02-02T01:33:21.154345Z", "level": "info", "context": {"user": {"id": 1, "name": "Ben Johnson"}, "http": {"method": "GET", "host": "timber.io", "path": "/path", "request_id": "abcd1234"}, "system": {"hostname": "1.server.com", "pid": "254354"}}, "event": {"http_server_response": {"status": 200, "time_ms": 45.2}}} ``` -In the [Timber console](https://app.timber.io) simply -[click the line to view this metdata](https://timber.io/docs/app/tutorials/view-metadata/). -Moreover, this data allows you to run powerful queries like: +Notice that instead of completely replacing your log messages, +Timber _augments_ your logs with structured metadata. Turning them into +[rich events with context](https://timber.io/docs/ruby/events-and-context) without sacrificing +readability. -1. `context.request_id:abcd1234` - View all logs generated for a specific request. -2. `context.user.id:1` - View logs generated by a specific user. -3. `type:http_server_response` - View specific events (exceptions, sql queries, etc) -4. `http_server_response.time_ms:>=1000` - View slow responses with the ability to zoom out and view them in context (request, user, etc). -5. `level:info` - Levels in your logs! +This is all accomplished by using the +[Timber::Logger](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Logger): -For a complete overview, see the [Timber for Ruby docs](https://timber.io/docs/ruby/overview/). +```ruby +logger = Timber::Logger.new(STDOUT) +logger.info("Sent 200 in 45.2ms") +``` +Here's a better look at the metadata: -## Installation +```json +{ + "dt": "2017-02-02T01:33:21.154345Z", + "level": "info", + "context": { + "user": { + "id": 1, + "name": "Ben Johnson" + }, + "http": { + "method": "GET", + "host": "timber.io", + "path": "/path", + "request_id": "abcd1234" + }, + "system": { + "hostname": "1.server.com", + "pid": "254354" + } + }, + "event": { + "http_server_response": { + "status": 200, + "time_ms": 45.2 + } + } +} +``` -1. In `Gemfile`, add the `timber` gem: +This structure isn't arbitrary either, it follows the +[simple log event JSON schema](https://github.com/timberio/log-event-json-schema), which +formalizes the data structure, creates a contract with downstream consumers, and +improves stability. - ```ruby - gem 'timber', '~> 2.0' - ``` +So what can you do with this data? -2. In your `shell`, run `bundle install` +1. [**Tail a user** - `user.id:1`](https://timber.io/docs/app/tutorials/tail-a-user/) +2. [**Trace a request** - `http.request_id:abcd1234`](https://timber.io/docs/app/tutorials/view-in-request-context/) +3. **Narrow by host** - `system.hostname:1.server.com` +4. **View slow responses** - `http_server_response.time_ms:>=1000` +5. **Filter by log level** - `level:error` +6. **Quickly find exceptions** - `is:exception` -3. In your `shell`, run `bundle exec timber install` +For a complete overview, see the [Timber for Ruby docs](https://timber.io/docs/ruby/overview/). +## Third-party support + +1. **Rails**: Structures ([HTTP requests](https://timber.io/docs/ruby/events-and-context/http-server-request-event/), [HTTP respones](https://timber.io/docs/ruby/events-and-context/http-server-response-event/), [controller calls](https://timber.io/docs/ruby/events-and-context/controller-call-event/), [template renders](https://timber.io/docs/ruby/events-and-context/template-render-event/), and [sql queries](https://timber.io/docs/ruby/events-and-context/sql-query-event/)). +2. **Rack**: Structures [exceptions](https://timber.io/docs/ruby/events-and-context/exception-event/), captures [HTTP context](https://timber.io/docs/ruby/events-and-context/http-context/), captures [user context](https://timber.io/docs/ruby/events-and-context/user-context/), captures [session context](https://timber.io/docs/ruby/events-and-context/session-context/). +3. **Devise, Omniauth, Clearance**: captures [user context](https://timber.io/docs/ruby/events-and-context/user-context/) +5. **Heroku**: Captures [release context](https://timber.io/docs/ruby/events-and-context/release-context/) via [Heroku dyno metadata](https://devcenter.heroku.com/articles/dyno-metadata). + +...and more. Timber will continue to evolve and support more libraries. + + ## Usage <details><summary><strong>Basic logging</strong></summary><p> -Use `Logger` as normal: +Use the `Timber::Logger` just like you would `::Logger`: ```ruby -logger.info("My log message") +logger = Timber::Logger.new(STDOUT) +logger.info("My log message") # use warn, error, debug, etc. # => My log message @metadata {"level": "info", "context": {...}} ``` --- @@ -81,10 +149,11 @@ Custom events allow you to extend beyond events already defined in the [`Timber::Events`](lib/timber/events) namespace. ```ruby +logger = Timber::Logger.new(STDOUT) logger.warn "Payment rejected", payment_rejected: {customer_id: "abcd1234", amount: 100, reason: "Card expired"} # => Payment rejected @metadata {"level": "warn", "event": {"payment_rejected": {"customer_id": "abcd1234", "amount": 100, "reason": "Card expired"}}, "context": {...}} ``` @@ -97,14 +166,16 @@ </p></details> <details><summary><strong>Custom contexts</strong></summary><p> Context is additional data shared across log lines. Think of it like log join data. +This is how a query like `context.user.id:1` can show you all logs generated by that user. Custom contexts allow you to extend beyond contexts already defined in the [`Timber::Contexts`](lib/timber/contexts) namespace. ```ruby +logger = Timber::Logger.new(STDOUT) logger.with_context(build: {version: "1.0.0"}) do logger.info("My log message") end # => My log message @metadata {"level": "info", "context": {"build": {"version": "1.0.0"}}} @@ -112,61 +183,245 @@ * Notice the `:build` root key. Timber will classify this context as such. * In the [Timber console](https://app.timber.io) use queries like: `build.version:1.0.0` * See more details on our [custom contexts docs page](https://timber.io/docs/ruby/custom-contexts/) +--- + </p></details> -<details><summary><strong>Metrics</strong></summary><p> +<details><summary><strong>Metrics & Timings</strong></summary><p> -Logging metrics is accomplished by logging custom events. Please see our -[metrics docs page](https://timber.io/docs/ruby/metrics/) for a more detailed explanation -with examples. +Aggregates destroy details, and with Timber capturing metrics and timings is just logging events. +Timber is built on modern big-data principles, it can calculate aggregates across terrabytes of +data in seconds. Don't reduce the quality of your data because the system processing +your data is limited. +Here's a timing example. Notice how Timber automatically calculates the time and adds the timing +to the message. + +```ruby +logger = Timber::Logger.new(STDOUT) +timer = Timber::Timer.start +# ... code to time ... +logger.info("Processed background job", background_job: {time_ms: timer}) + +# => Processed background job in 54.2ms @metadata {"level": "info", "event": {"background_job": {"time_ms": 54.2}}} +``` + +Or capture any metric you want: + +```ruby +logger = Timber::Logger.new(STDOUT) +logger.info("Credit card charged", credit_card_charge: {amount: 123.23}) + +# => Credit card charged @metadata {"level": "info", "event": {"credit_card_charge": {"amount": 123.23}}} +``` + +In Timber you can easily sum, average, min, and max the `amount` attribute across any interval +you desire. + </p></details> +## Configuration + +Below are a few popular configuration options, for a comprehensive list, see +[Timber::Config](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Config). + +<details><summary><strong>Logrageify. Silence noisy logs (sql query, template renders)</strong></summary><p> + +Timber allows you to silence noisy logs that aren't of value to you, just like +[lograge](https://github.com/roidrage/lograge). In fact, we've provided a convenience method +for anyone transitioning from lograge: + +```ruby +# config/initializers/timber.rb + +config = Timber::Config.instance +config.logrageify!() +``` + +It turns this: + +``` +Started GET "/" for 127.0.0.1 at 2012-03-10 14:28:14 +0100 +Processing by HomeController#index as HTML + Rendered text template within layouts/application (0.0ms) + Rendered layouts/_assets.html.erb (2.0ms) + Rendered layouts/_top.html.erb (2.6ms) + Rendered layouts/_about.html.erb (0.3ms) + Rendered layouts/_google_analytics.html.erb (0.4ms) +Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms) +``` + +Into this: + +``` +Get "/" sent 200 OK in 79ms @metadata {...} +``` + +Internally this is equivalent to: + +```ruby +# config/initializers/timber.rb + +config = Timber::Config.instance +config.integrations.action_controller.silence = true +config.integrations.action_view.silence = true +config.integrations.active_record.silence = true +config.integrations.rack.http_events.collapse_into_single_event = true +``` + +Feel free to deviate and customize which logs you silence. We recommend a slight deviation +from lograge with the following settings: + +```ruby +# config/initializers/timber.rb + +config = Timber::Config.instance +config.integrations.action_view.silence = true +config.integrations.active_record.silence = true +config.integrations.rack.http_events.collapse_into_single_event = true +``` + +This does _not_ silence the controller call log event. This is because Timber captures the +parameters passed to the controller, which is very valuable when debugging. + +For a full list of integrations and settings, see +[Timber::Integrations](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Integrations) + +--- + +</p></details> + +<details><summary><strong>Silence specific requests (LB health checks, etc)</strong></summary><p> + +The following will silence all `[GET] /_health` requests: + +```ruby +# config/initializers/timber.rb + +config = Timber::Config.instance +config.integrations.rack.http_events.silence_request = lambda do |rack_env, rack_request| + rack_request.path == "/_health" +end +``` + +We require a block because it gives you complete control over how you want to silence requests. +The first parameter being the traditional Rack env hash, the second being a +[Rack Request](http://www.rubydoc.info/gems/rack/Rack/Request) object. + +--- + +</p></details> + +<details><summary><strong>Change log formats</strong></summary><p> + +Simply set the formatter like you would with any other logger: + +```ruby +# This is set in your various environment files +logger = Timber::Logger.new(STDOUT) +logger.formatter = Timber::Logger::JSONFormatter.new +``` + +Your options are: + +1. [`Timber::Logger::AugmentedFormatter`](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Logger/AugmentedFormatter) - + (default) A human readable format that _appends_ metadata to the original log line. + Ex: `My log message @metadata {"level":"info","dt":"2017-01-01T01:02:23.234321Z"}` + +2. [`Timber::Logger::JSONFormatter`](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Logger/JSONFormatter) - + Ex: `{"level":"info","message":"My log message","dt":"2017-01-01T01:02:23.234321Z"}` + +3. [`Timber::Logger::MessageOnlyFormatter`](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Logger/MessageOnlyFormatter) - + For use in development / test. Prints logs as strings with no metadata attached. + Ex: `My log message` + +--- + +</p></details> + +<details><summary><strong>Capture custom user context</strong></summary><p> + +By default Timber automatically captures user context for most of the popular authentication +libraries (Devise, Omniauth, and Clearance). See +[Timber::Integrations::Rack::UserContext](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Integrations/Rack/UserContext) +for a complete list. + +In cases where you Timber doesn't support your strategy, or you want to customize it further, +you can do so like: + +```ruby +# config/initializers/timber.rb + +config = Timber::Config.instance +config.integrations.rack.user_context.custom_user_hash = lambda do |rack_env| + user = rack_env['warden'].user + if user + { + id: user.id, # unique identifier for the user, can be an integer or string, + name: user.name, # identifiable name for the user, + email: user.email, # user's email address + } + else + nil + end +end +``` + +*All* of the user hash keys are optional, but you must provide at least one. + +--- + +</p></details> + +<details><summary><strong>Capture release / deploy context</strong></summary><p> + +[Timber::Contexts::Release](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Contexts/Release) +tracks the current application release and version. If you are on Heroku, simply enable the +[dyno metadata](https://devcenter.heroku.com/articles/dyno-metadata) feature to get this +automatically. If you are not, simply add the follow environment variables to have the context +set automatically. + +1. `RELEASE_COMMIT` - `2c3a0b24069af49b3de35b8e8c26765c1dba9ff0` +2. `RELEASE_CREATED_AT` - `2015-04-02T18:00:42Z` +3. `RELEASE_VERSION` - `v2.3.1` + +--- + +</p></details> + + ## Jibber-Jabber -<details><summary><strong>Which log events does Timber structure for me?</strong></summary><p> +<details><summary><strong>Which events and context does Timber capture for me?</strong></summary><p> -Out of the box you get everything in the [`Timber::Events`](lib/timber/events) namespace. +Out of the box you get everything in the +[`Timber::Events`](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Events) namespace. -We also add context to every log, everything in the [`Timber::Contexts`](lib/timber/contexts) +We also add context to every log, everything in the +[`Timber::Contexts`](http://www.rubydoc.info/github/timberio/timber-ruby/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. +was written. It is included in every log line. Think of it like join data for your logs. It's +how Timber is able to accomplished tailing users (`context.user.id:1`). +Lastly, you can checkout the specific integrations in +[`Timber::Integrations`](lib/timber/integrations). + --- </p></details> <details><summary><strong>What about my current log statements?</strong></summary><p> -They'll continue to work as expected. Timber adheres to the default `Logger` interface. -Your previous logger calls will work as they always do. +They'll continue to work as expected. Timber adheres to the default `::Logger` interface. +Your previous logger calls will work as they always do. Just swap in `Timber::Logger` and +you're good to go. 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> - -<details><summary><strong>How is Timber different?</strong></summary><p> - -1. **It's just _better_ logging**. Nothing beats well structured raw data. And that's exactly - what Timber aims to provide. There are no agents, special APIs, or proprietary data - sets that you can't access. -2. **Improved log data quality.** Instead of relying on parsing alone, Timber ships libraries that - structure and augment your logs from _within_ your application. Improving your log data at the - source. -3. **Human readability.** Timber _augments_ your logs without sacrificing human readability. For - example: `log message @metadata {...}`. And when you view your logs in the - [Timber console](https://app.timber.io), you'll see the human friendly messages - with the ability to view the associated metadata. -4. **Long retention**. Logging is notoriously expensive with low retention. Timber - offers _6 months_ of retention by default with sane prices. -5. **Normalized schema.** Have multiple apps? All of Timber's libraries adhere to our - [JSON schema](https://github.com/timberio/log-event-json-schema). This means queries, alerts, - and graphs for your ruby app can also be applied to your elixir app (for example). --- </p></details> \ No newline at end of file