README.md in timber-2.1.1 vs README.md in timber-2.1.2

- old
+ new

@@ -1,147 +1,44 @@ -# 🌲 Timber - Simple Ruby Structured Logging +# 🌲 Timber - Log Better. Solve Problems Faster. [![ISC License](https://img.shields.io/badge/license-ISC-ff69b4.svg)](LICENSE.md) +[![Hex.pm](https://img.shields.io/hexpm/v/timber.svg?maxAge=18000=plastic)](https://hex.pm/packages/timber) +[![Documentation](https://img.shields.io/badge/hexdocs-latest-blue.svg)](https://hexdocs.pm/timber/index.html) [![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) -* [Library docs](http://www.rubydoc.info/github/timberio/timber-ruby) -* [Support](mailto:support@timber.io) - - ## Overview -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. +[Timber](https://timber.io) is a logging platform with one major difference: Instead of parsing, +which relies on unreadable, unpredictable, hard to use text logs, Timber integrates directly with +your application, producing rich structured events containing metadata and context you couldn't +capture otherwise. It fundamentally changes the way you use your logs. -1. **Easy setup.** - `bundle exec timber install`, [get setup in seconds](#installation). +1. [**Easy setup** - `mix timber.install`](#installation) +2. [**Seamlessly integrates with popular libraries and frameworks**](#jibber-jabber) +3. [**Modern fast console, designed specifically for your application**](#the-timber-console) -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-integrations) - -4. **Pairs with a modern structured-logging console.** - Designed specifically for structured data, - hosted, instantly usable, tail users, trace requests. - [Checkout the docs](https://timber.io/docs/app/tutorials/). - - ## Installation -1. In `Gemfile`, add the `timber` gem: +1. In your `Gemfile`, add the `timber` gem: ```ruby - gem 'timber', '~> 2.0' + gem 'timber', '~> 2.1' ``` 2. In your `shell`, run `bundle install` 3. In your `shell`, run `bundle exec timber install` -## How it works - -Let's start with an example. Timber turns this production log line: - -``` -I, [2017-06-04T18:04:53.653812 #42348] INFO -- : [my.host.com] [df88dbaa-50fd-4178-85d7-d66279ea33b6] [192.32.23.12] [bfa8242cd9733bf0211e334be203f0d0] Sent 200 in 45.2ms -``` - -Into a structured [`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": {"http": {"method": "GET", "path": "/path", "remote_addr": "192.32.23.12", "request_id": "df88dbaa-50fd-4178-85d7-d66279ea33b6"}, "session": {"id": "bfa8242cd9733bf0211e334be203f0d0"}, "system": {"hostname": "my.host.com", "pid": "254354"}, "user": {"id": 1, "name": "Ben Johnson", "email": "bens@email.com"}}, "event": {"http_server_response": {"status": 200, "time_ms": 45.2}}} -``` - -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. And you have [complete control over which data is captured](#configuration). - -This is all accomplished by using the -[Timber::Logger](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Logger): - -```ruby -logger = Timber::Logger.new(STDOUT) -logger.info("Sent 200 in 45.2ms") -``` - -Here's a better look at the metadata: - -```js -{ - "dt": "2017-02-02T01:33:21.154345Z", - "level": "info", - "context": { - "http": { - "method": "GET", - "path": "/path", - "remote_addr": "192.32.23.12", - "request_id": "abcd1234" - }, - "session": { - "id": "bfa8242cd9733bf0211e334be203f0d0" - }, - "system": { - "hostname": "1.server.com", - "pid": "254354" - }, - "user": { // user identifiable logs :O - "id": 1, - "name": "Ben Johnson", - "email": "bens@email.com" - }, - }, - "event": { - "http_server_response": { - "status": 200, - "time_ms": 45.2 - } - } -} -``` - -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. - -So what can you do with this data? - -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` - -For a complete overview, see the [Timber for Ruby docs](https://timber.io/docs/ruby/overview/). - - -## Third-party integrations - -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 the `Timber::Logger` just like you would `::Logger`: ```ruby -logger = Timber::Logger.new(STDOUT) logger.info("My log message") # use warn, error, debug, etc. # => My log message @metadata {"level": "info", "context": {...}} ``` @@ -153,19 +50,18 @@ 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": {...}} ``` * Notice the `:payment_rejected` root key. Timber will classify this event as such. * In the [Timber console](https://app.timber.io) use the query: `type:payment_rejected` or `payment_rejected.amount:>100`. -* See more details on our [custom events docs page](https://timber.io/docs/ruby/custom-events/) +* See more details on our [custom events docs page](https://timber.io/docs/ruby/usage/custom-events/) --- </p></details> @@ -175,21 +71,20 @@ 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"}}} ``` * 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/) +* See more details on our [custom contexts docs page](https://timber.io/docs/ruby/usage/custom-contexts/) --- </p></details> @@ -202,11 +97,10 @@ 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}}} @@ -219,11 +113,10 @@ ``` Lastly, metrics aren't limited to timings. You can 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}}} ``` @@ -329,11 +222,10 @@ 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: @@ -405,62 +297,70 @@ </p></details> ## Jibber-Jabber -<details><summary><strong>Which events and contexts does Timber capture for me?</strong></summary><p> +<details><summary><strong>Which log events does Timber structure for me?</strong></summary><p> -Out of the box you get everything in the -[`Timber::Events`](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Events) namespace. +Out of the box you get everything in the [`Timber.Events`](lib/timber/events) namespace. -We also add context to every log, everything in the -[`Timber::Contexts`](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Contexts) +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. It's -how Timber is able to accomplished tailing users (`context.user.id:1`). +was written. It is included in every log line. Think of it like join data for your logs. -Lastly, you can checkout how we capture these events in -[`Timber::Integrations`](lib/timber/integrations). - --- </p></details> -<details><summary><strong>Won't this increase the size of my log data?</strong></summary><p> +<details><summary><strong>What about my current log statements?</strong></summary><p> -Yes, but it's no different than adding any other useful data to your logs, such as -[tags](http://api.rubyonrails.org/classes/ActiveSupport/TaggedLogging.html). A few -of things to note: +They'll continue to work as expected. Timber adheres strictly to the default `Logger` interface +and will never deviate in *any* way. -1. Timber generally _reduces_ the amount of logs your app generates, trading quality for quantity. - It does so by providing options to consolidate request / response logs, template logs, and - even silence logs that are not of value to you. (see [configuration](#configuration) for examples). -2. Timber lets you pick exactly which events and contexts you want. - (see [configuration](#configuration) for examples) -3. Your logging provider should be compressing your data and charging you accordingly. Log data - is notoriously repetitive, and the context Timber generates is repetitive. - Because of compression we've seen somes apps only incur a ~15% increase in data size. +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). -Finally, log what is useful to you. Quality over quantity certainly applies to logging. +</p></details> ---- +<details><summary><strong>When to use metadata or events?</strong></summary><p> -</p></details> +At it's basic level, both metadata and events serve the same purpose: they add structured +data to your logs. And anyone that's implemented structured logging know's this can quickly get +out of hand. This is why we created events. Here's how we recommend using them: -<details><summary><strong>What about my current log statements?</strong></summary><p> +1. Use `events` when the log cleanly maps to an event that you'd like to alert on, graph, or use + in a meaningful way. Typically something that is core to your business or application. +2. Use metadata for debugging purposes; when you simply want additional insight without + polluting the message. -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. +### Example 1: Logging that a payment was rejected -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). +This is clearly an event that is meaningful to your business. You'll probably want to alert and +graph this data. So let's log it as an official event: +```ruby +logger.info("Payment rejected", payment_rejected: {customer_id: "xiaus1934", amount: 1900, currency: "USD"}) +``` + +### Example 2: Logging that an email was changed + +This is definitely log worthy, but not something that is core to your business or application. +Instead of an event, use metadata: + +```ruby +logger.info("Email successfully changed", old_email: old_email, new_email: new_email) +``` + --- </p></details> ---- +## The Timber Console + +[![Timber Console](http://files.timber.io/images/readme-interface7.gif)](https://app.timber.io) + +## Your Moment of Zen + <p align="center" style="background: #221f40;"> -<a href="http://github.com/timberio/timber-elixir"><img src="http://files.timber.io/images/ruby-library-readme-log-truth.png" height="947" /></a> -</p> \ No newline at end of file +<a href="http://github.com/timberio/timber-ruby"><img src="http://files.timber.io/images/readme-log-truth.png" height="947" /></a> +</p>