timber 1.0.13 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +278 -121
  3. data/lib/timber.rb +1 -0
  4. data/lib/timber/context.rb +1 -1
  5. data/lib/timber/contexts.rb +29 -2
  6. data/lib/timber/contexts/runtime.rb +24 -0
  7. data/lib/timber/contexts/system.rb +19 -0
  8. data/lib/timber/current_context.rb +14 -5
  9. data/lib/timber/event.rb +1 -1
  10. data/lib/timber/events.rb +11 -6
  11. data/lib/timber/events/controller_call.rb +1 -1
  12. data/lib/timber/events/custom.rb +1 -1
  13. data/lib/timber/events/exception.rb +1 -1
  14. data/lib/timber/events/{http_request.rb → http_server_request.rb} +1 -1
  15. data/lib/timber/events/{http_response.rb → http_server_response.rb} +2 -1
  16. data/lib/timber/events/sql_query.rb +2 -1
  17. data/lib/timber/events/template_render.rb +2 -1
  18. data/lib/timber/frameworks/rails.rb +12 -1
  19. data/lib/timber/log_devices/http.rb +29 -24
  20. data/lib/timber/log_entry.rb +23 -9
  21. data/lib/timber/logger.rb +20 -6
  22. data/lib/timber/probes.rb +1 -3
  23. data/lib/timber/probes/active_support_tagged_logging.rb +0 -43
  24. data/lib/timber/probes/rails_rack_logger.rb +1 -1
  25. data/lib/timber/rack_middlewares.rb +12 -0
  26. data/lib/timber/rack_middlewares/http_context.rb +30 -0
  27. data/lib/timber/util.rb +1 -0
  28. data/lib/timber/util/struct.rb +16 -0
  29. data/lib/timber/version.rb +1 -1
  30. data/spec/README.md +23 -0
  31. data/spec/support/timber.rb +1 -1
  32. data/spec/timber/contexts_spec.rb +49 -0
  33. data/spec/timber/events_spec.rb +1 -1
  34. data/spec/timber/log_devices/http_spec.rb +7 -7
  35. data/spec/timber/log_entry_spec.rb +15 -0
  36. data/spec/timber/logger_spec.rb +14 -10
  37. data/spec/timber/probes/action_controller_log_subscriber_spec.rb +6 -7
  38. data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +1 -1
  39. data/spec/timber/probes/action_view_log_subscriber_spec.rb +2 -2
  40. data/spec/timber/probes/rails_rack_logger_spec.rb +3 -3
  41. data/spec/timber/rack_middlewares/http_context_spec.rb +47 -0
  42. data/timber.gemspec +1 -0
  43. metadata +31 -8
  44. data/lib/timber/contexts/tags.rb +0 -22
  45. data/lib/timber/probes/rack_http_context.rb +0 -51
  46. data/spec/timber/probes/rack_http_context_spec.rb +0 -50
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 271e6ee909502a8d6581ab9b574fe0b11379dd5c
4
- data.tar.gz: 7325365af0abd4c6c83949604c026c64ff0044de
3
+ metadata.gz: 1a526175aa41bf2a0f6124b8081fa68dff14d79f
4
+ data.tar.gz: 85e34c100200bc0c61c576126cf1980d0ac576ca
5
5
  SHA512:
6
- metadata.gz: 671677150f46a95f0dd091ef12dbebc660abeb810ac8b64a27669677f44052a838305461a0724e5ca4f09410c4f0555c080cacd8c73bf45406da0f7eff2b2a4e
7
- data.tar.gz: d74ed9f56a5422f55dd403e98939dfd08b4a589c804ad11ea878e42cd813b0b7e40a0673f0a9607adf68a2fb017d8bf5a4f37383c65e88586e4453dbc385d6b4
6
+ metadata.gz: bbf7e0964fb175ced6f826ff852079373c138f97ee72f1cc8ae367c43c9d11bdd26d318ec365a801fd3657d5cd7d832cae8f6c836e09f93f16ab3f27665f5ee2
7
+ data.tar.gz: f001a84ee2a8d2df95dd339ca467d884b15e75a26b57fc3bce66ccd3b4234254025b2cf4fddb12c447768b483b869a3cc5d7fa99f2d823cf5d183d2ff54858de
data/README.md CHANGED
@@ -1,200 +1,357 @@
1
- # Timber
1
+ # 🌲 Timber - Master your Ruby apps with structured logging
2
2
 
3
3
  <p align="center" style="background: #140f2a;">
4
4
  <a href="http://github.com/timberio/timber-ruby"><img src="http://files.timber.io/images/ruby-library-readme-header.gif" height="469" /></a>
5
5
  </p>
6
6
 
7
+ [![ISC License](https://img.shields.io/badge/license-ISC-ff69b4.svg)](LICENSE.md)
7
8
  [![CircleCI](https://circleci.com/gh/timberio/timber-ruby.svg?style=shield&circle-token=:circle-token)](https://circleci.com/gh/timberio/timber-ruby/tree/master)
8
9
  [![Coverage Status](https://coveralls.io/repos/github/timberio/timber-ruby/badge.svg?branch=master)](https://coveralls.io/github/timberio/timber-ruby?branch=master)
9
10
  [![Code Climate](https://codeclimate.com/github/timberio/timber-ruby/badges/gpa.svg)](https://codeclimate.com/github/timberio/timber-ruby)
10
11
  [![View docs](https://img.shields.io/badge/docs-viewdocs-blue.svg?style=flat-square "Viewdocs")](http://www.rubydoc.info/github/timberio/timber-ruby)
11
12
 
12
13
 
13
- **Timber is in beta testing. If interested, please email beta@timber.io**
14
+ ---
15
+
16
+ 👉 **Timber is in beta testing, if interested in joining, please email us at
17
+ [beta@timber.io](mailto:beta@timber.io)**
18
+
19
+ ---
14
20
 
21
+ Still logging raw text? Timber is a complete *structured* logging solution that you can setup in
22
+ minutes. It goes beyond traditional log management by focusing on data quality and modern
23
+ developer standards.
24
+
25
+ High quality logs, [a modern UX-first interface](https://timber.io), simple setup,
26
+ zero-maintenance, 6-month retention, and sane prices are just a few of the benefits Timber
27
+ offers.
28
+
29
+ To learn more, checkout out [timber.io](https://timber.io) or the
30
+ ["why we built Timber"](http://moss-ibex2.cloudvent.net/blog/why-were-building-timber/)
31
+ blog post.
32
+
33
+
34
+ ## Overview
35
+
36
+ <details><summary><strong>What are the benefits of using Timber?</strong></summary><p>
37
+
38
+ 1. **Data quality.** The usefulness of your logs starts here. This is why we ship libraries that
39
+ structure logs from *within* your application; a fundamental difference from parsing. Not only
40
+ is it much more stable, but we can include data you couldn't obtain otherwise.
41
+ 2. **Human readability.** Structuring your logs doesn't mean they have to be unreadable. Timber
42
+ *augments* your logs with structured data. Meaning we do not alter the original log message,
43
+ we simply attach metadata to it. And our console is specifically designed to give you access
44
+ to this data, without compromising readability. 😮
45
+ 3. **Reliable downstream consumption.** All log events adhere to a
46
+ [normalized, shared, schema](https://github.com/timberio/log-event-json-schema) that follows
47
+ [semantic versioning](http://semver.org/) and goes through a [standard release process](https://github.com/timberio/log-event-json-schema/releases).
48
+ This means you can *rely* on the structure of your logs and interact consistently with them
49
+ across apps of any language: queries, graphs, alerts, and other downstream consumers.
50
+ 4. **Zero risk of code debt or lock-in.** Logging is a standard that has been around since the dawn
51
+ of computers. It's built into every language, framework, and library. Timber adheres strictly
52
+ to the default `Logger` interface. There are no special APIs, and no need to pepper your app
53
+ with Timber specific code. It's just better logging. If you choose to stop using Timber, you
54
+ can do so without consequence.
55
+ 5. **Long term retention.** Timber is designed on modern big-data principles. As a result, we can
56
+ offer 6+ months of retention at prices cheaper than alternatives offering <1 month.
57
+ This allows you to unlock your logs for purposes beyond debugging.
58
+ ---
15
59
 
16
- 1. [What is timber?](#what-is-timber)
17
- 2. [How it works](#how-it-works)
18
- 3. [Why timber?](#why-timber)
19
- 4. [Logging Custom Events](#logging-custom-events)
20
- 5. [The Timber Console / Pricing](#the-timber-console--pricing)
21
- 6. [Install](#install)
60
+ </p></details>
22
61
 
62
+ <details><summary><strong>What specifically does the Timber library do?</strong></summary><p>
23
63
 
24
- ## What is Timber?
64
+ 1. Captures and structures your framework and 3rd party logs. (see next question)
65
+ 2. Adds useful context to every log line. (see next question)
66
+ 3. Provides a [framework for logging custom structured events](#what-about-custom-events).
67
+ 4. Offers transport strategies to [send your logs](#send-your-logs) to the Timber service.
25
68
 
26
- Using your logs shouldn't be a time consuming, frustrating process! If you were like us, it goes
27
- something like this:
69
+ ---
28
70
 
29
- > I need to centralize my logs, where should I send them? Which provider should I use?
30
- > Wow, this is expensive. Why is searching so difficult and time consuming?
31
- > Would structuring my logs help? Which format should I use? Will they still be human readable?
32
- > What if the structure changes? What about logs from 3rd party libraries? Ahhh!!!
71
+ </p></details>
33
72
 
34
- Timber solves this by providing a complete, managed, end-to-end logging solution that marries
35
- a beautiful, *fast*, console with libraries that automatically structure and enrich your logs.
73
+ <details><summary><strong>What events does Timber capture & structure for me?</strong></summary><p>
36
74
 
75
+ Out of the box you get everything in the [`Timber::Events`](lib/timber/events) namespace:
76
+
77
+ 1. [Controller Call Event](lib/timber/events/controller_call.rb)
78
+ 2. [Exception Event](lib/timber/events/exception.rb)
79
+ 3. [HTTP Client Request Event (net/http outgoing)](lib/timber/events/http_client_request.rb)
80
+ 4. [HTTP Client Response Event (resposne from net/http outgoing)](lib/timber/events/http_client_response.rb)
81
+ 5. [HTTP Server Request Event (incoming client request)](lib/timber/events/http_server_request.rb)
82
+ 6. [HTTP Server Response Event (response to incoming client request)](lib/timber/events/http_server_response.rb)
83
+ 7. [SQL Query Event](lib/timber/events/sql_query.rb)
84
+ 8. [Template Render Event](lib/timber/events/template_render.rb)
85
+ 9. ...more coming soon, [file an issue](https://github.com/timberio/timber-ruby/issues) to request.
86
+
87
+ We also add context to every log, everything in the [`Timber::Contexts`](lib/timber/contexts)
88
+ namespace. Context is structured data representing the current environment when the log line was
89
+ written. It is included in every log line. Think of it like join data for your logs:
90
+
91
+ 1. [HTTP Context](lib/timber/contexts/http.rb)
92
+ 2. [Organization Context](lib/timber/contexts/organization.rb)
93
+ 3. [Process Context](lib/timber/contexts/process.rb)
94
+ 4. [Server Context](lib/timber/contexts/server.rb)
95
+ 5. [Runtime Context](lib/timber/contexts/runtime.rb)
96
+ 5. [User Context](lib/timber/contexts/user.rb)
97
+ 6. ...more coming soon, [file an issue](https://github.com/timberio/timber-ruby/issues) to request.
98
+
99
+ ---
37
100
 
38
- ## How it works
101
+ </p></details>
39
102
 
40
- The Timber ruby library takes care of all of the log structuring madness. For example,
41
- it turns this Rails log line:
103
+ <details><summary><strong>What about my current log statements?</strong></summary><p>
42
104
 
105
+ They'll continue to work as expected. Timber adheres strictly to the default `::Logger` interface
106
+ and will never deviate in *any* way.
107
+
108
+ In fact, traditional log statements for non-meaningful events, debug statements, etc, are
109
+ encouraged. In cases where the data is meaningful, consider [logging a custom event](#usage).
110
+
111
+ </p></details>
112
+
113
+ ## Usage
114
+
115
+ <details><summary><strong>Basic logging</strong></summary><p>
116
+
117
+ Use `Logger` as normal:
118
+
119
+ ```ruby
120
+ logger.info("My log message")
121
+
122
+ # My log message @metadata {"level": "info", "context": {...}}
43
123
  ```
44
- Completed 200 OK in 117ms (Views: 85.2ms | ActiveRecord: 25.3ms)
124
+
125
+ Timber will never deviate from the public `::Logger` interface in *any* way.
126
+
127
+ ---
128
+
129
+ </p></details>
130
+
131
+ <details><summary><strong>Tagging logs</strong></summary><p>
132
+
133
+ Need a quick and easy way to identify a log? Use tags!:
134
+
135
+ ```ruby
136
+ logger.info(message: "My log message", tag: "tag")
137
+
138
+ # My log message @metadata {"level": "info", "tags": ["tag"], "context": {...}}
139
+ ```
140
+
141
+ Multiple tags:
142
+
143
+ ```ruby
144
+ logger.info(message: "My log message", tags: ["tag1", "tag2"])
145
+
146
+ # My log message @metadata {"level": "info", "tags": ["tag1", "tag2"], "context": {...}}
45
147
  ```
46
148
 
47
- Into this:
48
-
49
- ```javascript
50
- {
51
- "dt": "2016-12-01T02:23:12.236543Z",
52
- "level": "info",
53
- "message": "Completed 200 OK in 117ms (Views: 85.2ms | ActiveRecord: 25.3ms)",
54
- "context": {
55
- "http": {
56
- "method": "GET",
57
- "path": "/checkout",
58
- "remote_addr": "123.456.789.10",
59
- "request_id": "abcd1234"
60
- },
61
- "user": { // <---- http://i.giphy.com/EldfH1VJdbrwY.gif
62
- "id": 2,
63
- "name": "Ben Johnson",
64
- "email": "ben@johnson.com"
65
- }
66
- },
67
- "event": {
68
- "http_response": {
69
- "status": 200,
70
- "time_ms": 117
71
- }
72
- }
73
- }
149
+ Using `ActiveSupport::TaggedLogging`? It works with that as well:
150
+
151
+ ```ruby
152
+ logger.tagged("tag") do
153
+ logger.info(message: "My log message", tags: ["important", "slow"])
154
+ end
155
+
156
+ # My log message @metadata {"level": "info", "tags": ["tag"], "context": {...}}
74
157
  ```
75
158
 
76
- Notice we preserve the original log message. When viewing this event in the
77
- [Timber Console](https://timber.io), you'll see the simple, human readable line with the
78
- ability to view, and use, the attached structured data! Also, notice how rich the event is.
79
- Beecause we're inside your application, we can capture data beyond what's in the log line.
159
+ </p></details>
160
+
161
+ <details><summary><strong>Custom events</strong></summary><p>
162
+
163
+ 1. Log a structured Hash (simplest)
80
164
 
81
- (for a full list see [`Timber::Events`](lib/timber/events))
165
+ ```ruby
166
+ Logger.warn message: "Payment rejected", payment_rejected: {customer_id: "abcd1234", amount: 100, reason: "Card expired"}
82
167
 
168
+ # Payment rejected @metadata {"level": "warn", "event": {"payment_rejected": {"customer_id": "abcd1234", "amount": 100, "reason": "Card expired"}}, "context": {...}}
169
+ ```
83
170
 
84
- ## Why Timber?
171
+ * The hash can *only* have a `:message` and "event type" key, where `:payment_rejected` is the event type in the above example.
85
172
 
86
- Glad you asked! :)
173
+ 2. Log a Struct (recommended)
87
174
 
88
- 1. Human readable logs *and* rich structured data. You don't have to choose.
89
- 2. Data beyond what's in the log line itself making your logs exceptionally useful.
90
- 3. Normalized log data. Timber enforces a strict schema, meaning your log data, across all apps
91
- and languages will be normalized.
92
- 4. Absolutely no lock-in or risk of code debt. No fancy API, no proprietary data format locked
93
- away in our servers, Timber is just good ol' loggin'.
94
- 5. Fully managed, all the way from the log messages to the console you use. No fragile parsing
95
- rules or complicated interfaces.
175
+ Defining structs for your important events just feels oh so good :) It creates a strong contract
176
+ with down stream consumers and gives you compile time guarantees.
96
177
 
178
+ ```ruby
179
+ PaymentRejectedEvent = Struct.new(:customer_id, :amount, :reason) do
180
+ def message; "Payment rejected for #{customer_id}"; end
181
+ def type; :payment_rejected; end
182
+ end
183
+ Logger.warn PaymentRejectedEvent.new("abcd1234", 100, "Card expired")
97
184
 
98
- ## Logging Custom Events
185
+ # Payment rejected @metadata {"level": "warn", "event": {"payment_rejected": {"customer_id": "abcd1234", "amount": 100, "reason": "Card expired"}}, "context": {...}}
186
+ ```
99
187
 
100
- > Another service? More lock-in? :*(
188
+ * `:type` is how Timber classifies the event, it creates a namespace for the data you send.
189
+ * For more advanced examples see [`Timber::Logger`](lib/timber.logger.rb).
190
+ * Also, notice there is no mention of Timber in the above code. Just plain old logging.
101
191
 
102
- Nope! Logging custom events is Just Logging™. Check it out:
192
+ #### What about regular Hashes, JSON, or logfmt?
193
+
194
+ Go for it! Timber will parse the data server side, but we *highly* recommend the above examples.
195
+ Providing a `:type` allows timber to classify the event, create a namespace for the data you
196
+ send, and make it easier to search, graph, alert, etc.
103
197
 
104
198
  ```ruby
105
- # Simple string (original Logger interface remains untouched)
106
- Logger.warn "Payment rejected for customer abcd1234, reason: Card expired"
199
+ logger.info({key: "value"})
200
+ # {"key": "value"} @metadata {"level": "info", "context": {...}}
107
201
 
108
- # Structured hash
109
- Logger.warn message: "Payment rejected", type: :payment_rejected,
110
- data: {customer_id: "abcd1234", amount: 100, reason: "Card expired"}
202
+ logger.info('{"key": "value"}')
203
+ # {"key": "value"} @metadata {"level": "info", "context": {...}}
111
204
 
112
- # Using a Struct
113
- PaymentRejectedEvent = Struct.new(:customer_id, :amount, :reason) do
114
- def message; "Payment rejected for #{customer_id}"; end
115
- def type; :payment_rejected; end
116
- end
117
- Logger.warn PaymentRejectedEvent.new("abcd1234", 100, "Card expired")
205
+ logger.info('key=value')
206
+ # key=value @metadata {"level": "info", "context": {...}}
118
207
  ```
119
208
 
120
- (for more examples, see [the `Timber::Logger` docs](lib/timber/logger.rb))
209
+ ---
210
+
211
+ </p></details>
212
+
213
+ <details><summary><strong>Custom contexts</strong></summary><p>
121
214
 
122
- No mention of Timber anywhere!
215
+ Context is structured data representing the current environment when the log line was written.
216
+ It is included in every log line. Think of it like join data for your logs. For example, the
217
+ `http.request_id` field is included in the context, allowing you to find all log lines related
218
+ to that request ID, if desired. This is in contrast to *only* showing log lines that contain this
219
+ value.
123
220
 
221
+ 1. Add a Hash (simplest)
124
222
 
125
- ## The Timber Console / Pricing
223
+ ```ruby
224
+ Timber::CurrentContext.with({build: {version: "1.0.0"}}) do
225
+ logger.info("My log message")
226
+ end
126
227
 
127
- > What good is structured log data if you can't search and visualize it?
228
+ # My log message @metadata {"level": "info", "context": {"build": {"version": "1.0.0"}}}
229
+ ```
128
230
 
129
- The [Timber Console](https://timber.io) is *fast*, modern, and beautiful console designed
130
- specifically for this library.
231
+ This adds data to the context keyspaced by `build`.
131
232
 
132
- A few example queries:
233
+ 2. Add a Struct (recommended)
133
234
 
134
- 1. `context.user.email:ben@johnson.com` - Tail a specific user!
135
- 2. `context.http.request_id:1234` - View *all* logs for a given HTTP request!
136
- 3. `event.http_reponse.time_ms>3000` - Easily find outliers and have the proper context to resolve them!
137
- 4. `level:warn` - Log levels in your logs. Imagine that!
235
+ Just like events, we recommend defining your custom contexts. It makes a stronger contract
236
+ with downstream consumers.
138
237
 
139
- > This is all gravy, but wouldn't the extra data get expensive?
238
+ ```ruby
239
+ BuildContext = Struct.new(:version) do
240
+ def type; :build; end
241
+ end
242
+ build_context = BuildContext.new("1.0.0")
243
+ Timber::CurrentContext.with(build_context) do
244
+ logger.info("My log message")
245
+ end
140
246
 
141
- If you opt to use the [Timber Console](https://timber.io), we only charge for
142
- the size of the `message`, `dt`, and `event.custom` attributes. Everything else is
143
- stored at no cost to you. [Say wha?!](http://i.giphy.com/l0HlL2vlfpWI0meJi.gif). This ensures
144
- pricing remains predictable. We charge per GB sent to us and retained. No user limits,
145
- no weird feature matrixes, just data. Finally, the data is yours, in a simple
146
- non-proprietary JSON format that you can export to S3, Redshift, or any of our other integrations.
247
+ # My log message @metadata {"level": "info", "context": {"build": {"version": "1.0.0"}}}
248
+ ```
147
249
 
148
- For more details checkout out [timber.io](https://timber.io).
250
+ </p></details>
149
251
 
150
- ## Install
151
252
 
152
- **Timber is in beta testing. If interested, please email beta@timber.io**
153
253
 
154
- ### 1. Install the gem:
254
+ ## Installation
155
255
 
156
256
  ```ruby
157
257
  # Gemfile
158
258
  gem 'timber'
159
259
  ```
160
260
 
161
- ### 2. Install the logger:
162
261
 
163
- #### Heroku:
262
+ ## Setup
263
+
264
+ <details><summary><strong>Rails >= 3.0</strong></summary><p>
265
+
266
+ *Replace* any existing `config.logger=` calls in `config/environments/production.rb` with:
164
267
 
165
268
  ```ruby
166
- # config/environments/production.rb (or staging, etc)
167
- config.logger = Timber::Logger.new(STDOUT)
269
+ # config/environments/production.rb
270
+
271
+ config.logger = ActiveSupport::TaggedLogging.new(Timber::Logger.new(STDOUT))
168
272
  ```
169
273
 
170
- The command to add your log drain will be displayed in the [Timber app](https://app.timber.io)
171
- after you add your application.
274
+ * Prefer examples? Checkout our [Ruby / Rails example app](https://github.com/timberio/ruby-rails-example-app),
275
+ 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).
172
276
 
173
- #### Non-Heroku:
277
+ ---
174
278
 
175
- ```ruby
176
- # config/environments/production.rb (or staging, etc)
177
- log_device = Timber::LogDevices::HTTP.new(ENV['TIMBER_KEY']) # key can be obtained by signing up at https://timber.io
178
- config.logger = Timber::Logger.new(log_device)
179
- ```
279
+ </p></details>
180
280
 
181
- Your Timber application key will be displayed in the [Timber app](https://app.timber.io)
182
- after you add your application.
281
+ <details><summary><strong>Other</strong></summary><p>
183
282
 
283
+ 1. *Insert* the Timber probes:
184
284
 
185
- *Other transport methods coming soon!*
285
+ This should be executed *immediately after* you have required your dependencies.
186
286
 
287
+ ```ruby
288
+ Timber::Probes.insert!
289
+ ```
187
290
 
188
- #### Rails TaggedLogging?
291
+ 2. *Add* the Rack middlewares:
189
292
 
190
- No probs! Use it as normal, Timber will even pull out the tags and include them in the `context`.
293
+ This should be included where you build your `Rack` application. Usually `config.ru`:
191
294
 
192
- ```ruby
193
- config.logger = ActiveSupport::TaggedLogging.new(Timber::Logger.new(STDOUT))
194
- ```
295
+ ```ruby
296
+ # Most likely config.ru
297
+
298
+ Timber::RackMiddlewares.middlewares.each do |m|
299
+ use m
300
+ end
301
+ ```
302
+
303
+ 2. *Instantiate* the Timber logger:
304
+
305
+ This should be *globally* available to your application:
306
+
307
+ ```ruby
308
+ logger = Timber::Logger.new(STDOUT)
309
+ ```
310
+
311
+ </p></details>
312
+
313
+
314
+ ## Send your logs
315
+
316
+ <details><summary><strong>Heroku (log drains)</strong></summary><p>
317
+
318
+ The recommended strategy for Heroku is to setup a
319
+ [log drain](https://devcenter.heroku.com/articles/log-drains). To get your Timber log drain URL:
320
+
321
+ 👉 **[Add your app to Timber](https://app.timber.io)**
195
322
 
196
323
  ---
197
324
 
198
- <p align="center" style="background: #140f2a;">
325
+ </p></details>
326
+
327
+ <details><summary><strong>All other platforms (Network / HTTP)</strong></summary><p>
328
+
329
+ 1. *Specify* the Timber Network logger backend in `config/environments/production.rb`:
330
+
331
+ Replace any existing `config.logger =` calls with:
332
+
333
+ ```ruby
334
+ # config/environments/production.rb (or staging, etc)
335
+
336
+ network_log_device = Timber::LogDevices::Network.new(ENV['TIMBER_LOGS_KEY'])
337
+ config.logger = Timber::Logger.new(network_log_device) # <-- Use network_log_device instead of STDOUT
338
+ ```
339
+
340
+ 2. Obtain your Timber API :key: by **[adding your app in Timber](https://app.timber.io)**.
341
+
342
+ 3. Assign your API key to the `TIMBER_LOGS_KEY` environment variable.
343
+
344
+ </p></details>
345
+
346
+ <details><summary><strong>Advanced setup (syslog, file tailing agent, etc)</strong></summary><p>
347
+
348
+ Checkout our [docs](https://timber.io/docs) for a comprehensive list of install instructions.
349
+
350
+ </p></details>
351
+
352
+
353
+ ---
354
+
355
+ <p align="center" style="background: #221f40;">
199
356
  <a href="http://github.com/timberio/timber-ruby"><img src="http://files.timber.io/images/ruby-library-readme-log-truth.png" height="947" /></a>
200
- </p>
357
+ </p>