README.md in timber-1.1.14 vs README.md in timber-2.0.0

- old
+ new

@@ -1,386 +1,171 @@ -# 🌲 Timber - Master your Ruby apps with structured logging +# 🌲 Timber - Log Better. Solve Problems Faster. -<p align="center" style="background: #140f2a;"> -<a href="http://files.timber.io/images/readme-interface.gif"><img src="http://files.timber.io/images/readme-interface.gif" width="100%" /></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) -Still logging raw text? Timber is a complete *structured* logging solution that you can setup in -minutes. It solves logging so you don't have to! +* [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) -To learn more, checkout out [timber.io](https://timber.io). +## Overview -## Installation +Timber turns your raw text logs into rich JSON events that can be consumed by the +[Timber.io service](https://timber.io). It improves log data quality at the source, adding +critical event and context data to your logs so that you can filter out the noise and +solve problems faster. -1. *Add* the `timber` gem in `Gemfile`: +For example, Timber turns this: - ```ruby - # Gemfile +``` +Sent 200 in 45.ms +``` - gem 'timber' - ``` +Into this: -2. *Install* the `Timber::Logger` in `config/environments/production.rb`: +``` +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}}} +``` - ```ruby - # config/environments/production.rb +Allowing you to run queries like: - # config.log_formatter = ::Logger::Formatter.new # <--------------------------- REMOVE ME - # config.logger = ActiveSupport::TaggedLogging.new(logger) # <----------------- REMOVE ME +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_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:error` - Levels in your logs! - config.logger = ActiveSupport::TaggedLogging.new(Timber::Logger.new(STDOUT)) # <-- ADD ME - ``` ---- +## Installation -<details><summary><strong>Prefer to see an example pull request?</strong></summary><p> +1. In `Gemfile`, add the `timber` gem: -Checkout our the [Timber install example pull request](https://github.com/timberio/ruby-rails-example-app/pull/1/files) + ```ruby + gem 'timber', '~> 2.0' + ``` ---- +2. In your `shell`, run `bundle install` -</p></details> +3. In your `shell`, run `bundle exec timber install` -<details><summary><strong>Not using Rails?</strong></summary><p> -No problem! You can easily install Timber following these steps: - -1. *Insert* the Timber probes: - - This should be executed *immediately after* you have required your dependencies. - - ```ruby - Timber::Probes.insert! - ``` - -2. *Add* the Rack middlewares: - - This should be included where you build your `Rack` application. Usually `config.ru`: - - ```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 (choose one) - -<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></details> - -<details><summary><strong>Or, all other platforms (Network / HTTP)</strong></summary><p> - -1. *Specify* the Timber HTTP logger backend in `config/environments/production.rb`: - - Replace any existing `config.logger =` calls with: - - ```ruby - # config/environments/production.rb (or staging, etc) - - http_log_device = Timber::LogDevices::HTTP.new(ENV['TIMBER_LOGS_KEY']) - config.logger = ActiveSupport::TaggedLogging.new(Timber::Logger.new(http_log_device)) - ``` - -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>Or, 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> - - ## 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": {...}} +# => My log message @metadata {"level": "info", "context": {...}} ``` Timber will *never* deviate from the public `::Logger` interface in *any* way. --- </p></details> -<details><summary><strong>Tagging logs</strong></summary><p> +<details><summary><strong>Custom events</strong></summary><p> -Tags provide a quick way to identify logs. They work just like any tagging system. -In the context of logging, they prevent obstructing the log message to -accomplish the same thing, while also being a step down from creating a classified custom -event. If the event is meaningful in any way, we recommend creating a custom event. +Custom events allow you to extend beyond events already defined in +the [`Timber::Events`](lib/timber/events) namespace. ```ruby -logger.info(message: "My log message", tag: "tag") +Logger.warn "Payment rejected", payment_rejected: {customer_id: "abcd1234", amount: 100, reason: "Card expired"} -# My log message @metadata {"level": "info", "tags": ["tag"], "context": {...}} +# => Payment rejected @metadata {"level": "warn", "event": {"payment_rejected": {"customer_id": "abcd1234", "amount": 100, "reason": "Card expired"}}, "context": {...}} ``` -Multiple tags: +* 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/) -```ruby -logger.info(message: "My log message", tags: ["tag1", "tag2"]) - -# My log message @metadata {"level": "info", "tags": ["tag1", "tag2"], "context": {...}} -``` - -Using `ActiveSupport::TaggedLogging`? It works with that as well: - -```ruby -logger.tagged("tag") do - logger.info(message: "My log message", tags: ["important", "slow"]) -end - -# My log message @metadata {"level": "info", "tags": ["tag"], "context": {...}} -``` - -* In the Timber console use the query: `tags:tag`. - --- </p></details> -<details><summary><strong>Timing events</strong></summary><p> +<details><summary><strong>Custom contexts</strong></summary><p> -Timings provid a simple way to time code execution: +Context is additional data shared across log lines. Think of it like log join data. +Custom contexts allow you to extend beyond contexts already defined in +the [`Timber::Contexts`](lib/timber/contexts) namespace. ```ruby -start = Time.now -# ...my code to time... -time_ms = (Time.now - start) * 1000 -logger.info(message: "Task complete", tag: "my_task", time_ms: time_ms) +Timber::CurrentContext.with({build: {version: "1.0.0"}}) do + logger.info("My log message") +end -# My log message @metadata {"level": "info", tags: ["my_task"], "time_ms": 54.2132, "context": {...}} +# => My log message @metadata {"level": "info", "context": {"build": {"version": "1.0.0"}}} ``` -* In the Timber console use the query: `tags:my_task time_ms>500` -* The Timber console will also display this value inline with your logs. No need to include it - in the log message, but you certainly can if you'd prefer. +* 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>Custom events</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. -Custom events can be used to structure information about events that are central -to your line of business like receiving credit card payments, saving a draft of a post, -or changing a user's password. You have 2 options to do this: - -1. Log a structured Hash (simplest) - - ```ruby - Logger.warn message: "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": {...}} - ``` - - * The hash can *only* have 2 keys: `:message` and "event type" key; `:payment_rejected` in this example. - * Timber will keyspace your event data by the event type key passed. - -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": {...}} - ``` - -* In the Timber console use queries like: `payment_rejected.customer_id:xiaus1934` or `payment_rejected.amount>100` -* 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 -logger.info({key: "value"}) -# {"key": "value"} @metadata {"level": "info", "context": {...}} - -logger.info('{"key": "value"}') -# {"key": "value"} @metadata {"level": "info", "context": {...}} - -logger.info('key=value') -# key=value @metadata {"level": "info", "context": {...}} -``` - ---- - </p></details> -<details><summary><strong>Custom contexts</strong></summary><p> -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. - -1. Add a Hash (simplest) - - ```ruby - Timber::CurrentContext.with({build: {version: "1.0.0"}}) do - logger.info("My log message") - end - - # My log message @metadata {"level": "info", "context": {"build": {"version": "1.0.0"}}} - ``` - - This adds data to the context keyspaced by `build`. - -2. Add a Struct (recommended) - - Just like events, we recommend defining your custom contexts. It makes a stronger contract - with downstream consumers. - - ```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 - - # My log message @metadata {"level": "info", "context": {"build": {"version": "1.0.0"}}} - ``` - -</p></details> - - ## Jibber-Jabber -<details><summary><strong>What specifically does the Timber library do?</strong></summary><p> +<details><summary><strong>Which log events does Timber structure for me?</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. Allows you to easily add tags and timings to log. (see [Usage](#usage)) -4. Provides a framework for logging custom structured events. (see [Usage](#usage)) -5. Offers transport strategies to [send your logs](#send-your-logs) to the Timber service. +Out of the box you get everything in the [`Timber::Events`](lib/timber/events) namespace. ---- - -</p></details> - -<details><summary><strong>What are the benefits of using Timber?</strong></summary><p> - -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. - ---- - -</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: +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. +They'll continue to work as expected. Timber adheres to the default `Logger` interface. +Your previous logger calls will work as they always do. 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> + +--- + <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> +<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