timber 2.1.1 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +56 -156
  3. data/lib/timber/config.rb +7 -8
  4. data/lib/timber/config/integrations/rack.rb +4 -4
  5. data/lib/timber/contexts/runtime.rb +3 -2
  6. data/lib/timber/contexts/system.rb +2 -2
  7. data/lib/timber/current_context.rb +5 -0
  8. data/lib/timber/events.rb +3 -5
  9. data/lib/timber/events/controller_call.rb +7 -5
  10. data/lib/timber/events/{exception.rb → error.rb} +13 -12
  11. data/lib/timber/events/{http_server_request.rb → http_request.rb} +5 -5
  12. data/lib/timber/events/{http_server_response.rb → http_response.rb} +3 -3
  13. data/lib/timber/events/sql_query.rb +3 -0
  14. data/lib/timber/integrations/action_dispatch.rb +2 -2
  15. data/lib/timber/integrations/rack.rb +3 -3
  16. data/lib/timber/integrations/rack/{exception_event.rb → error_event.rb} +6 -6
  17. data/lib/timber/integrations/rack/http_events.rb +10 -10
  18. data/lib/timber/log_devices.rb +1 -0
  19. data/lib/timber/log_devices/http.rb +5 -1
  20. data/lib/timber/log_devices/multi.rb +34 -0
  21. data/lib/timber/log_entry.rb +3 -1
  22. data/lib/timber/logger.rb +37 -11
  23. data/lib/timber/util/http_event.rb +4 -5
  24. data/lib/timber/version.rb +1 -1
  25. data/spec/timber/contexts/system_spec.rb +2 -2
  26. data/spec/timber/events/{exception_spec.rb → error_spec.rb} +4 -4
  27. data/spec/timber/events/{http_server_request_spec.rb → http_request_spec.rb} +1 -1
  28. data/spec/timber/integrations/action_dispatch/debug_exceptions_spec.rb +1 -1
  29. data/spec/timber/logger_spec.rb +9 -0
  30. metadata +11 -12
  31. data/lib/timber/events/http_client_request.rb +0 -65
  32. data/lib/timber/events/http_client_response.rb +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a6efcc9b1ff96b7bfea713d3e87e36b7bfc061fd
4
- data.tar.gz: 6ef8b32449171a85062421fc42da7c4417b7fdf8
3
+ metadata.gz: fbf04b7803b278fe7386975994f5c16d019b125b
4
+ data.tar.gz: 27b112405610ebb0b2c948ba4e1ae0f698fd5ac0
5
5
  SHA512:
6
- metadata.gz: 79c590f521790d5f24349bdfb2cd9058cd78b18955bfa61164338d75b70cb7e58a24594a31f93780af6a1f27bbafd124c5a3249d3dac9d331debfb3181792e63
7
- data.tar.gz: 6e284e8801b5e9643fe36e65200f1425c2da8ab078a6bb60f29339323da86ef371f7ca8a32634f8f7d22932672422e35bc4883e59f814ae06d131503a08ec076
6
+ metadata.gz: a474845f4dc5c7c328d860458ce93d5a2769cd850ebb7117cf442662565ab654e1c57f3a209037d43f80a8f5b0b27e54cebfb581f6505aad8f0939c8e14285a1
7
+ data.tar.gz: 0f60bc9a972900139d780a22c9c10c0e35afaa46689c5cc8a4bacd22ee1db454fbb34f3ef003d5a92a070255c7f309d0c2fcd3e38e4ffce6c48bfbdff9de2f96
data/README.md CHANGED
@@ -1,41 +1,28 @@
1
- # 🌲 Timber - Simple Ruby Structured Logging
1
+ # 🌲 Timber - Log Better. Solve Problems Faster.
2
2
 
3
3
  [![ISC License](https://img.shields.io/badge/license-ISC-ff69b4.svg)](LICENSE.md)
4
+ [![Hex.pm](https://img.shields.io/hexpm/v/timber.svg?maxAge=18000=plastic)](https://hex.pm/packages/timber)
5
+ [![Documentation](https://img.shields.io/badge/hexdocs-latest-blue.svg)](https://hexdocs.pm/timber/index.html)
4
6
  [![Build Status](https://travis-ci.org/timberio/timber-ruby.svg?branch=master)](https://travis-ci.org/timberio/timber-ruby)
5
- [![Code Climate](https://codeclimate.com/github/timberio/timber-ruby/badges/gpa.svg)](https://codeclimate.com/github/timberio/timber-ruby)
6
- [![View docs](https://img.shields.io/badge/docs-viewdocs-blue.svg?style=flat-square "Viewdocs")](http://www.rubydoc.info/github/timberio/timber-ruby)
7
-
8
- * [Timber website](https://timber.io)
9
- * [Timber docs](https://timber.io/docs)
10
- * [Library docs](http://www.rubydoc.info/github/timberio/timber-ruby)
11
- * [Support](mailto:support@timber.io)
12
-
13
7
 
14
8
  ## Overview
15
9
 
16
- Timber solves ruby structured logging so you don't have to. Go from raw text logs to rich
17
- structured events in seconds. Spend more time focusing on your app and less time
18
- focusing on logging.
19
-
20
- 1. **Easy setup.** - `bundle exec timber install`, [get setup in seconds](#installation).
21
-
22
- 2. **Automatically structures yours logs.** - Third-party and in-app logs are all structured
23
- in a consistent format. See [how it works](#how-it-works) below.
10
+ [Timber](https://timber.io) is a logging platform with one major difference: Instead of parsing,
11
+ which relies on unreadable, unpredictable, hard to use text logs, Timber integrates directly with
12
+ your application, producing rich structured events containing metadata and context you couldn't
13
+ capture otherwise. It fundamentally changes the way you use your logs.
24
14
 
25
- 3. **Seamlessly integrates with popular libraries and frameworks.** - Rails, Rack, Devise,
26
- Omniauth, etc. [Automatically captures user context, HTTP context, and event data.](#third-party-integrations)
27
-
28
- 4. **Pairs with a modern structured-logging console.** - Designed specifically for structured data,
29
- hosted, instantly usable, tail users, trace requests.
30
- [Checkout the docs](https://timber.io/docs/app/tutorials/).
15
+ 1. [**Easy setup** - `mix timber.install`](#installation)
16
+ 2. [**Seamlessly integrates with popular libraries and frameworks**](#jibber-jabber)
17
+ 3. [**Modern fast console, designed specifically for your application**](#the-timber-console)
31
18
 
32
19
 
33
20
  ## Installation
34
21
 
35
- 1. In `Gemfile`, add the `timber` gem:
22
+ 1. In your `Gemfile`, add the `timber` gem:
36
23
 
37
24
  ```ruby
38
- gem 'timber', '~> 2.0'
25
+ gem 'timber', '~> 2.1'
39
26
  ```
40
27
 
41
28
  2. In your `shell`, run `bundle install`
@@ -43,95 +30,6 @@ focusing on logging.
43
30
  3. In your `shell`, run `bundle exec timber install`
44
31
 
45
32
 
46
- ## How it works
47
-
48
- Let's start with an example. Timber turns this production log line:
49
-
50
- ```
51
- 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
52
- ```
53
-
54
- Into a structured [`http_server_response` event](https://timber.io/docs/ruby/events-and-context/http-server-response-event/).
55
-
56
- ```
57
- 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}}}
58
- ```
59
-
60
- Notice that instead of completely replacing your log messages,
61
- Timber _augments_ your logs with structured metadata. Turning them into
62
- [rich events with context](https://timber.io/docs/ruby/events-and-context) without sacrificing
63
- readability. And you have [complete control over which data is captured](#configuration).
64
-
65
- This is all accomplished by using the
66
- [Timber::Logger](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Logger):
67
-
68
- ```ruby
69
- logger = Timber::Logger.new(STDOUT)
70
- logger.info("Sent 200 in 45.2ms")
71
- ```
72
-
73
- Here's a better look at the metadata:
74
-
75
- ```js
76
- {
77
- "dt": "2017-02-02T01:33:21.154345Z",
78
- "level": "info",
79
- "context": {
80
- "http": {
81
- "method": "GET",
82
- "path": "/path",
83
- "remote_addr": "192.32.23.12",
84
- "request_id": "abcd1234"
85
- },
86
- "session": {
87
- "id": "bfa8242cd9733bf0211e334be203f0d0"
88
- },
89
- "system": {
90
- "hostname": "1.server.com",
91
- "pid": "254354"
92
- },
93
- "user": { // user identifiable logs :O
94
- "id": 1,
95
- "name": "Ben Johnson",
96
- "email": "bens@email.com"
97
- },
98
- },
99
- "event": {
100
- "http_server_response": {
101
- "status": 200,
102
- "time_ms": 45.2
103
- }
104
- }
105
- }
106
- ```
107
-
108
- This structure isn't arbitrary either, it follows the
109
- [simple log event JSON schema](https://github.com/timberio/log-event-json-schema), which
110
- formalizes the data structure, creates a contract with downstream consumers, and
111
- improves stability.
112
-
113
- So what can you do with this data?
114
-
115
- 1. [**Tail a user** - `user.id:1`](https://timber.io/docs/app/tutorials/tail-a-user/)
116
- 2. [**Trace a request** - `http.request_id:abcd1234`](https://timber.io/docs/app/tutorials/view-in-request-context/)
117
- 3. **Narrow by host** - `system.hostname:1.server.com`
118
- 4. **View slow responses** - `http_server_response.time_ms:>=1000`
119
- 5. **Filter by log level** - `level:error`
120
- 6. **Quickly find exceptions** - `is:exception`
121
-
122
- For a complete overview, see the [Timber for Ruby docs](https://timber.io/docs/ruby/overview/).
123
-
124
-
125
- ## Third-party integrations
126
-
127
- 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/)).
128
- 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/).
129
- 3. **Devise, Omniauth, Clearance**: captures [user context](https://timber.io/docs/ruby/events-and-context/user-context/)
130
- 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).
131
-
132
- ...and more. Timber will continue to evolve and support more libraries.
133
-
134
-
135
33
  ## Usage
136
34
 
137
35
  <details><summary><strong>Basic logging</strong></summary><p>
@@ -139,7 +37,6 @@ For a complete overview, see the [Timber for Ruby docs](https://timber.io/docs/r
139
37
  Use the `Timber::Logger` just like you would `::Logger`:
140
38
 
141
39
  ```ruby
142
- logger = Timber::Logger.new(STDOUT)
143
40
  logger.info("My log message") # use warn, error, debug, etc.
144
41
 
145
42
  # => My log message @metadata {"level": "info", "context": {...}}
@@ -155,7 +52,6 @@ Custom events allow you to extend beyond events already defined in
155
52
  the [`Timber::Events`](lib/timber/events) namespace.
156
53
 
157
54
  ```ruby
158
- logger = Timber::Logger.new(STDOUT)
159
55
  logger.warn "Payment rejected", payment_rejected: {customer_id: "abcd1234", amount: 100, reason: "Card expired"}
160
56
 
161
57
  # => Payment rejected @metadata {"level": "warn", "event": {"payment_rejected": {"customer_id": "abcd1234", "amount": 100, "reason": "Card expired"}}, "context": {...}}
@@ -163,7 +59,7 @@ logger.warn "Payment rejected", payment_rejected: {customer_id: "abcd1234", amou
163
59
 
164
60
  * Notice the `:payment_rejected` root key. Timber will classify this event as such.
165
61
  * In the [Timber console](https://app.timber.io) use the query: `type:payment_rejected` or `payment_rejected.amount:>100`.
166
- * See more details on our [custom events docs page](https://timber.io/docs/ruby/custom-events/)
62
+ * See more details on our [custom events docs page](https://timber.io/docs/ruby/usage/custom-events/)
167
63
 
168
64
  ---
169
65
 
@@ -177,7 +73,6 @@ Custom contexts allow you to extend beyond contexts already defined in
177
73
  the [`Timber::Contexts`](lib/timber/contexts) namespace.
178
74
 
179
75
  ```ruby
180
- logger = Timber::Logger.new(STDOUT)
181
76
  logger.with_context(build: {version: "1.0.0"}) do
182
77
  logger.info("My log message")
183
78
  end
@@ -187,7 +82,7 @@ end
187
82
 
188
83
  * Notice the `:build` root key. Timber will classify this context as such.
189
84
  * In the [Timber console](https://app.timber.io) use queries like: `build.version:1.0.0`
190
- * See more details on our [custom contexts docs page](https://timber.io/docs/ruby/custom-contexts/)
85
+ * See more details on our [custom contexts docs page](https://timber.io/docs/ruby/usage/custom-contexts/)
191
86
 
192
87
  ---
193
88
 
@@ -204,7 +99,6 @@ Here's a timing example. Notice how Timber automatically calculates the time and
204
99
  to the message.
205
100
 
206
101
  ```ruby
207
- logger = Timber::Logger.new(STDOUT)
208
102
  timer = Timber::Timer.start
209
103
  # ... code to time ...
210
104
  logger.info("Processed background job", background_job: {time_ms: timer})
@@ -221,7 +115,6 @@ logger.info("Processed background job", background_job: {time_ms: 45.6})
221
115
  Lastly, metrics aren't limited to timings. You can capture any metric you want:
222
116
 
223
117
  ```ruby
224
- logger = Timber::Logger.new(STDOUT)
225
118
  logger.info("Credit card charged", credit_card_charge: {amount: 123.23})
226
119
 
227
120
  # => Credit card charged @metadata {"level": "info", "event": {"credit_card_charge": {"amount": 123.23}}}
@@ -331,7 +224,6 @@ Simply set the formatter like you would with any other logger:
331
224
 
332
225
  ```ruby
333
226
  # This is set in your various environment files
334
- logger = Timber::Logger.new(STDOUT)
335
227
  logger.formatter = Timber::Logger::JSONFormatter.new
336
228
  ```
337
229
 
@@ -407,60 +299,68 @@ All variables are optional, but at least one must be present.
407
299
 
408
300
  ## Jibber-Jabber
409
301
 
410
- <details><summary><strong>Which events and contexts does Timber capture for me?</strong></summary><p>
302
+ <details><summary><strong>Which log events does Timber structure for me?</strong></summary><p>
411
303
 
412
- Out of the box you get everything in the
413
- [`Timber::Events`](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Events) namespace.
304
+ Out of the box you get everything in the [`Timber.Events`](lib/timber/events) namespace.
414
305
 
415
- We also add context to every log, everything in the
416
- [`Timber::Contexts`](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Contexts)
306
+ We also add context to every log, everything in the [`Timber.Contexts`](lib/timber/contexts)
417
307
  namespace. Context is structured data representing the current environment when the log line
418
- was written. It is included in every log line. Think of it like join data for your logs. It's
419
- how Timber is able to accomplished tailing users (`context.user.id:1`).
420
-
421
- Lastly, you can checkout how we capture these events in
422
- [`Timber::Integrations`](lib/timber/integrations).
308
+ was written. It is included in every log line. Think of it like join data for your logs.
423
309
 
424
310
  ---
425
311
 
426
312
  </p></details>
427
313
 
428
- <details><summary><strong>Won't this increase the size of my log data?</strong></summary><p>
314
+ <details><summary><strong>What about my current log statements?</strong></summary><p>
429
315
 
430
- Yes, but it's no different than adding any other useful data to your logs, such as
431
- [tags](http://api.rubyonrails.org/classes/ActiveSupport/TaggedLogging.html). A few
432
- of things to note:
316
+ They'll continue to work as expected. Timber adheres strictly to the default `Logger` interface
317
+ and will never deviate in *any* way.
433
318
 
434
- 1. Timber generally _reduces_ the amount of logs your app generates, trading quality for quantity.
435
- It does so by providing options to consolidate request / response logs, template logs, and
436
- even silence logs that are not of value to you. (see [configuration](#configuration) for examples).
437
- 2. Timber lets you pick exactly which events and contexts you want.
438
- (see [configuration](#configuration) for examples)
439
- 3. Your logging provider should be compressing your data and charging you accordingly. Log data
440
- is notoriously repetitive, and the context Timber generates is repetitive.
441
- Because of compression we've seen somes apps only incur a ~15% increase in data size.
319
+ In fact, traditional log statements for non-meaningful events, debug statements, etc, are
320
+ encouraged. In cases where the data is meaningful, consider [logging a custom event](#usage).
442
321
 
443
- Finally, log what is useful to you. Quality over quantity certainly applies to logging.
322
+ </p></details>
444
323
 
445
- ---
324
+ <details><summary><strong>When to use metadata or events?</strong></summary><p>
446
325
 
447
- </p></details>
326
+ At it's basic level, both metadata and events serve the same purpose: they add structured
327
+ data to your logs. And anyone that's implemented structured logging know's this can quickly get
328
+ out of hand. This is why we created events. Here's how we recommend using them:
448
329
 
449
- <details><summary><strong>What about my current log statements?</strong></summary><p>
330
+ 1. Use `events` when the log cleanly maps to an event that you'd like to alert on, graph, or use
331
+ in a meaningful way. Typically something that is core to your business or application.
332
+ 2. Use metadata for debugging purposes; when you simply want additional insight without
333
+ polluting the message.
450
334
 
451
- They'll continue to work as expected. Timber adheres to the default `::Logger` interface.
452
- Your previous logger calls will work as they always do. Just swap in `Timber::Logger` and
453
- you're good to go.
335
+ ### Example 1: Logging that a payment was rejected
454
336
 
455
- In fact, traditional log statements for non-meaningful events, debug statements, etc, are
456
- encouraged. In cases where the data is meaningful, consider [logging a custom event](#usage).
337
+ This is clearly an event that is meaningful to your business. You'll probably want to alert and
338
+ graph this data. So let's log it as an official event:
339
+
340
+ ```ruby
341
+ logger.info("Payment rejected", payment_rejected: {customer_id: "xiaus1934", amount: 1900, currency: "USD"})
342
+ ```
343
+
344
+ ### Example 2: Logging that an email was changed
345
+
346
+ This is definitely log worthy, but not something that is core to your business or application.
347
+ Instead of an event, use metadata:
348
+
349
+ ```ruby
350
+ logger.info("Email successfully changed", old_email: old_email, new_email: new_email)
351
+ ```
457
352
 
458
353
  ---
459
354
 
460
355
  </p></details>
461
356
 
462
- ---
357
+
358
+ ## The Timber Console
359
+
360
+ [![Timber Console](http://files.timber.io/images/readme-interface7.gif)](https://app.timber.io)
361
+
362
+ ## Your Moment of Zen
463
363
 
464
364
  <p align="center" style="background: #221f40;">
465
- <a href="http://github.com/timberio/timber-elixir"><img src="http://files.timber.io/images/ruby-library-readme-log-truth.png" height="947" /></a>
466
- </p>
365
+ <a href="http://github.com/timberio/timber-ruby"><img src="http://files.timber.io/images/readme-log-truth.png" height="947" /></a>
366
+ </p>
@@ -36,7 +36,7 @@ module Timber
36
36
 
37
37
  # @private
38
38
  def initialize
39
- @http_body_limit = 2000
39
+ @http_body_limit = 2048
40
40
  end
41
41
 
42
42
  # Convenience method for logging debug statements to the debug logger
@@ -130,16 +130,15 @@ module Timber
130
130
  @http_header_filters ||= []
131
131
  end
132
132
 
133
- # Truncates captured HTTP bodies to this specified limit. The default is `2000`.
134
- # If you want to capture more data, you can raise this to a maximum of `5000`,
135
- # or lower this to be more efficient with data. `2000` characters should give you a good
136
- # idea of the body content. If you need to raise to `5000` you're only constraint is
137
- # network throughput.
133
+ # Truncates captured HTTP bodies to this specified limit. The default is `2048`.
134
+ # If you want to capture more data, you can raise this to a maximum of `8192`,
135
+ # or lower this to be more efficient with data. `2048` characters should give you a good
136
+ # idea of the body content.
138
137
  #
139
138
  # @example Rails
140
- # config.timber.http_body_limit = 500
139
+ # config.timber.http_body_limit = 2048
141
140
  # @example Everything else
142
- # Timber::Config.instance.http_body_limit = 500
141
+ # Timber::Config.instance.http_body_limit = 2048
143
142
  def http_body_limit=(value)
144
143
  @http_body_limit = value
145
144
  end
@@ -14,15 +14,15 @@ module Timber
14
14
  module Rack
15
15
  extend self
16
16
 
17
- # Convenience method for accessing the {Timber::Integrations::Rack::ExceptionEvent}
17
+ # Convenience method for accessing the {Timber::Integrations::Rack::ErrorEvent}
18
18
  # middleware class specific configuration. See {Timber::Integrations::Rack::ExceptionEvent}
19
19
  # for a list of methods available.
20
20
  #
21
21
  # @example
22
22
  # config = Timber::Config.instance
23
- # config.integrations.rack.exception_event.enabled = false
24
- def exception_event
25
- Timber::Integrations::Rack::ExceptionEvent
23
+ # config.integrations.rack.error_event.enabled = false
24
+ def error_event
25
+ Timber::Integrations::Rack::ErrorEvent
26
26
  end
27
27
 
28
28
  # Convenience method for accessing the {Timber::Integrations::Rack::HTTPContext}
@@ -9,7 +9,7 @@ module Timber
9
9
  class Runtime < Context
10
10
  @keyspace = :runtime
11
11
 
12
- attr_reader :application, :class_name, :file, :function, :line, :module_name
12
+ attr_reader :application, :class_name, :file, :function, :line, :module_name, :vm_pid
13
13
 
14
14
  def initialize(attributes)
15
15
  @application = attributes[:application]
@@ -18,12 +18,13 @@ module Timber
18
18
  @function = attributes[:function]
19
19
  @line = attributes[:line]
20
20
  @module_name = attributes[:module_name]
21
+ @vm_pid = attributes[:vm_pid]
21
22
  end
22
23
 
23
24
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
24
25
  def as_json(_options = {})
25
26
  {application: application, class_name: class_name, file: file, function: function,
26
- line: line, module_name: module_name}
27
+ line: line, module_name: module_name, vm_pid: vm_pid}
27
28
  end
28
29
  end
29
30
  end
@@ -15,12 +15,12 @@ module Timber
15
15
  def initialize(attributes)
16
16
  @hostname = attributes[:hostname]
17
17
  @pid = attributes[:pid]
18
- @pid = @pid.to_s
18
+ @pid = Timber::Util::Object.try(@pid, :to_i)
19
19
  end
20
20
 
21
21
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
22
22
  def as_json(_options = {})
23
- {hostname: hostname, pid: Timber::Util::Object.try(pid, :to_s)}
23
+ {hostname: hostname, pid: Timber::Util::Object.try(pid, :to_i)}
24
24
  end
25
25
  end
26
26
  end
@@ -158,6 +158,11 @@ module Timber
158
158
  system_context = Contexts::System.new(hostname: hostname, pid: pid)
159
159
  add_to!(new_hash, system_context)
160
160
 
161
+ # Runtime context
162
+ thread_object_id = Thread.current.object_id
163
+ runtime_context = Contexts::System.new(vm_pid: thread_object_id)
164
+ add_to!(new_hash, runtime_context)
165
+
161
166
  new_hash
162
167
  end
163
168
 
@@ -1,10 +1,8 @@
1
1
  require "timber/events/controller_call"
2
2
  require "timber/events/custom"
3
- require "timber/events/exception"
4
- require "timber/events/http_client_request"
5
- require "timber/events/http_client_response"
6
- require "timber/events/http_server_request"
7
- require "timber/events/http_server_response"
3
+ require "timber/events/error"
4
+ require "timber/events/http_request"
5
+ require "timber/events/http_response"
8
6
  require "timber/events/sql_query"
9
7
  require "timber/events/template_render"
10
8
 
@@ -10,6 +10,7 @@ module Timber
10
10
  # @note This event should be installed automatically through integrations,
11
11
  # such as the {Integrations::ActionController::LogSubscriber} integration.
12
12
  class ControllerCall < Timber::Event
13
+ PARAMS_JSON_MAX_BYTES = 8192.freeze
13
14
  PASSWORD_NAME = 'password'.freeze
14
15
 
15
16
  attr_reader :controller, :action, :params, :format
@@ -44,11 +45,12 @@ module Timber
44
45
 
45
46
  private
46
47
  def params_json
47
- @params_json ||= if params.nil? || params == {}
48
- nil
49
- else
50
- params.to_json
51
- end
48
+ @params_json ||=
49
+ if params.nil? || params == {}
50
+ nil
51
+ else
52
+ params.to_json.byteslice(0, PARAMS_JSON_MAX_BYTES)
53
+ end
52
54
  end
53
55
 
54
56
  def sanitize_params(params)
@@ -3,38 +3,39 @@ require "timber/util"
3
3
 
4
4
  module Timber
5
5
  module Events
6
- # The exception event is used to track exceptions.
6
+ # The error event is used to track errors and exceptions.
7
7
  #
8
8
  # @note This event should be installed automatically through integrations,
9
9
  # such as the {Integrations::ActionDispatch::DebugExceptions} integration.
10
- class Exception < Timber::Event
11
- attr_reader :name, :exception_message, :backtrace
10
+ class Error < Timber::Event
11
+ MAX_MESSAGE_BYTES = 8192.freeze
12
+
13
+ attr_reader :name, :error_message, :backtrace
12
14
 
13
15
  def initialize(attributes)
14
16
  @name = attributes[:name] || raise(ArgumentError.new(":name is required"))
15
- @exception_message = attributes[:exception_message] || raise(ArgumentError.new(":exception_message is required"))
17
+
18
+ @error_message = attributes[:error_message] || raise(ArgumentError.new(":error_message is required"))
19
+ @error_message = @error_message.byteslice(0, MAX_MESSAGE_BYTES)
16
20
 
17
21
  backtrace = attributes[:backtrace]
18
- if backtrace.nil? || backtrace == []
19
- raise(ArgumentError.new(":backtrace is required"))
22
+ if !backtrace.nil? && backtrace != []
23
+ @backtrace = backtrace[0..9].collect { |line| parse_backtrace_line(line) }
20
24
  end
21
-
22
- # 10 items max
23
- @backtrace = backtrace[0..9].collect { |line| parse_backtrace_line(line) }
24
25
  end
25
26
 
26
27
  def to_hash
27
- {name: name, message: exception_message, backtrace: backtrace}
28
+ {name: name, message: error_message, backtrace: backtrace}
28
29
  end
29
30
  alias to_h to_hash
30
31
 
31
32
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
32
33
  def as_json(_options = {})
33
- {:exception => to_hash}
34
+ {:error => to_hash}
34
35
  end
35
36
 
36
37
  def message
37
- "#{name} (#{exception_message})"
38
+ "#{name} (#{error_message})"
38
39
  end
39
40
 
40
41
  private
@@ -8,19 +8,19 @@ module Timber
8
8
  #
9
9
  # @note This event should be installed automatically through integrations,
10
10
  # such as the {Integrations::ActionController::LogSubscriber} integration.
11
- class HTTPServerRequest < Timber::Event
11
+ class HTTPRequest < Timber::Event
12
12
  attr_reader :body, :headers, :host, :method, :path, :port, :query_string, :request_id,
13
- :scheme
13
+ :scheme, :service_name
14
14
 
15
15
  def initialize(attributes)
16
16
  @body = attributes[:body] && Util::HTTPEvent.normalize_body(attributes[:body])
17
17
  @headers = Util::HTTPEvent.normalize_headers(attributes[:headers])
18
- @host = attributes[:host] || raise(ArgumentError.new(":host is required"))
18
+ @host = attributes[:host]
19
19
  @method = Util::HTTPEvent.normalize_method(attributes[:method]) || raise(ArgumentError.new(":method is required"))
20
20
  @path = attributes[:path]
21
21
  @port = attributes[:port]
22
22
  @query_string = Util::HTTPEvent.normalize_query_string(attributes[:query_string])
23
- @scheme = attributes[:scheme] || raise(ArgumentError.new(":scheme is required"))
23
+ @scheme = attributes[:scheme]
24
24
  @request_id = attributes[:request_id]
25
25
  end
26
26
 
@@ -32,7 +32,7 @@ module Timber
32
32
 
33
33
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
34
34
  def as_json(_options = {})
35
- {:http_server_request => to_hash}
35
+ {:http_request => to_hash}
36
36
  end
37
37
 
38
38
  def message
@@ -8,8 +8,8 @@ module Timber
8
8
  #
9
9
  # @note This event should be installed automatically through integrations,
10
10
  # such as the {Integrations::ActionController::LogSubscriber} integration.
11
- class HTTPServerResponse < Timber::Event
12
- attr_reader :body, :headers, :http_context, :request_id, :status, :time_ms
11
+ class HTTPResponse < Timber::Event
12
+ attr_reader :body, :headers, :http_context, :request_id, :service_name, :status, :time_ms
13
13
 
14
14
  def initialize(attributes)
15
15
  @body = attributes[:body] && Util::HTTPEvent.normalize_body(attributes[:body])
@@ -28,7 +28,7 @@ module Timber
28
28
 
29
29
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
30
30
  def as_json(_options = {})
31
- {:http_server_response => to_hash}
31
+ {:http_response => to_hash}
32
32
  end
33
33
 
34
34
  # Returns the human readable log message for this event.
@@ -7,10 +7,13 @@ module Timber
7
7
  # @note This event should be installed automatically through integrations,
8
8
  # such as the {Integrations::ActiveRecord::LogSubscriber} integration.
9
9
  class SQLQuery < Timber::Event
10
+ MAX_QUERY_BYTES = 1024.freeze
11
+
10
12
  attr_reader :sql, :time_ms, :message
11
13
 
12
14
  def initialize(attributes)
13
15
  @sql = attributes[:sql] || raise(ArgumentError.new(":sql is required"))
16
+ @sql = @sql.byteslice(0, MAX_QUERY_BYTES)
14
17
  @time_ms = attributes[:time_ms] || raise(ArgumentError.new(":time_ms is required"))
15
18
  @time_ms = @time_ms.round(6)
16
19
  @message = attributes[:message] || raise(ArgumentError.new(":message is required"))
@@ -1,5 +1,5 @@
1
1
  require "timber/integration"
2
- require "timber/integrations/rack/exception_event"
2
+ require "timber/integrations/rack/error_event"
3
3
  require "timber/integrations/action_dispatch/debug_exceptions"
4
4
 
5
5
  module Timber
@@ -10,7 +10,7 @@ module Timber
10
10
  # works as expected.
11
11
  module ActionDispatch
12
12
  def self.enabled?
13
- Rack::ExceptionEvent.enabled?
13
+ Rack::ErrorEvent.enabled?
14
14
  end
15
15
 
16
16
  def self.integrate!
@@ -1,4 +1,4 @@
1
- require "timber/integrations/rack/exception_event"
1
+ require "timber/integrations/rack/error_event"
2
2
  require "timber/integrations/rack/http_context"
3
3
  require "timber/integrations/rack/http_events"
4
4
  require "timber/integrations/rack/session_context"
@@ -9,7 +9,7 @@ module Timber
9
9
  module Rack
10
10
  # Enable / disable all Rack middlewares with a single setting.
11
11
  def self.enabled=(value)
12
- ExceptionEvent.enabled = value
12
+ ErrorEvent.enabled = value
13
13
  HTTPContext.enabled = value
14
14
  HTTPEvents.enabled = value
15
15
  SessionContext.enabled = value
@@ -20,7 +20,7 @@ module Timber
20
20
  # context are added first so that context is included in subsequent log lines.
21
21
  def self.middlewares
22
22
  @middlewares ||= [HTTPContext, SessionContext, UserContext,
23
- HTTPEvents, ExceptionEvent].select(&:enabled?)
23
+ HTTPEvents, ErrorEvent].select(&:enabled?)
24
24
  end
25
25
  end
26
26
  end
@@ -7,16 +7,16 @@ rescue Exception
7
7
  end
8
8
 
9
9
  require "timber/config"
10
- require "timber/events/exception"
10
+ require "timber/events/error"
11
11
  require "timber/integrations/rack/middleware"
12
12
  require "timber/util"
13
13
 
14
14
  module Timber
15
15
  module Integrations
16
16
  module Rack
17
- # A Rack middleware that is reponsible for capturing exceptions events
18
- # {Timber::Events::Exception}.
19
- class ExceptionEvent < Middleware
17
+ # A Rack middleware that is reponsible for capturing exception and error events
18
+ # {Timber::Events::Error}.
19
+ class ErrorEvent < Middleware
20
20
  # We determine this when the app loads to avoid the overhead on a per request basis.
21
21
  EXCEPTION_WRAPPER_TAKES_CLEANER = if Gem.loaded_specs["rails"]
22
22
  Gem.loaded_specs["rails"].version >= Gem::Version.new('5.0.0')
@@ -31,9 +31,9 @@ module Timber
31
31
  Config.instance.logger.fatal do
32
32
  backtrace = extract_backtrace(env, exception)
33
33
 
34
- Events::Exception.new(
34
+ Events::Error.new(
35
35
  name: exception.class.name,
36
- exception_message: exception.message,
36
+ error_message: exception.message,
37
37
  backtrace: backtrace
38
38
  )
39
39
  end
@@ -3,15 +3,15 @@ require "set"
3
3
  require "timber/config"
4
4
  require "timber/contexts/http"
5
5
  require "timber/current_context"
6
- require "timber/events/http_server_request"
7
- require "timber/events/http_server_response"
6
+ require "timber/events/http_request"
7
+ require "timber/events/http_response"
8
8
  require "timber/integrations/rack/middleware"
9
9
 
10
10
  module Timber
11
11
  module Integrations
12
12
  module Rack
13
13
  # A Rack middleware that is reponsible for capturing and logging HTTP server requests and
14
- # response events. The {Events::HTTPServerRequest} and {Events::HTTPServerResponse} events
14
+ # response events. The {Events::HTTPRequest} and {Events::HTTPResponse} events
15
15
  # respectively.
16
16
  class HTTPEvents < Middleware
17
17
  class << self
@@ -26,7 +26,7 @@ module Timber
26
26
  # helpful and redundant to the body captured here.
27
27
  #
28
28
  # If you opt to capture bodies, you can also truncate the size to reduce the data
29
- # captured. See {Events::HTTPServerRequest}.
29
+ # captured. See {Events::HTTPRequest}.
30
30
  #
31
31
  # @example
32
32
  # Timber::Integrations::Rack::HTTPEvents.capture_request_body = true
@@ -39,7 +39,7 @@ module Timber
39
39
  @capture_request_body == true
40
40
  end
41
41
 
42
- # Just like {#capture_request_body=} but for the {Events::HTTPServerResponse} event.
42
+ # Just like {#capture_request_body=} but for the {Events::HTTPResponse} event.
43
43
  # Please see {#capture_request_body=} for more details. The documentation there also
44
44
  # applies here.
45
45
  def capture_response_body=(value)
@@ -67,9 +67,9 @@ module Timber
67
67
  #
68
68
  # Get "/" sent 200 OK in 79ms
69
69
  #
70
- # The single event is still a {Timber::Events::HTTPServerResponse} event. Because
70
+ # The single event is still a {Timber::Events::HTTPResponse} event. Because
71
71
  # we capture HTTP context, you still get the HTTP details, but you will not get
72
- # all of the request details that the {Timber::Events::HTTPServerRequest} event would
72
+ # all of the request details that the {Timber::Events::HTTPRequest} event would
73
73
  # provide.
74
74
  #
75
75
  # @example
@@ -128,7 +128,7 @@ module Timber
128
128
  http_context = CurrentContext.fetch(http_context_key)
129
129
  time_ms = (Time.now - start) * 1000.0
130
130
 
131
- Events::HTTPServerResponse.new(
131
+ Events::HTTPResponse.new(
132
132
  headers: headers,
133
133
  http_context: http_context,
134
134
  request_id: request.request_id,
@@ -145,7 +145,7 @@ module Timber
145
145
  Config.instance.logger.info do
146
146
  event_body = capture_request_body? ? request.body_content : nil
147
147
 
148
- Events::HTTPServerRequest.new(
148
+ Events::HTTPRequest.new(
149
149
  body: event_body,
150
150
  headers: request.headers,
151
151
  host: request.host,
@@ -164,7 +164,7 @@ module Timber
164
164
  time_ms = (Time.now - start) * 1000.0
165
165
  event_body = capture_response_body? ? body : nil
166
166
 
167
- Events::HTTPServerResponse.new(
167
+ Events::HTTPResponse.new(
168
168
  body: event_body,
169
169
  headers: headers,
170
170
  request_id: request.request_id,
@@ -1,4 +1,5 @@
1
1
  require "timber/log_devices/http"
2
+ require "timber/log_devices/multi"
2
3
 
3
4
  module Timber
4
5
  # Namespace for all log devices.
@@ -214,7 +214,11 @@ module Timber
214
214
  def build_http
215
215
  http = Net::HTTP.new(@timber_url.host, @timber_url.port)
216
216
  http.set_debug_output(Config.instance.debug_logger) if Config.instance.debug_logger
217
- http.use_ssl = true if @timber_url.scheme == 'https'
217
+ if @timber_url.scheme == 'https'
218
+ http.use_ssl = true
219
+ # Verification on Windows fails despite having a valid certificate.
220
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
221
+ end
218
222
  http.read_timeout = 30
219
223
  http.ssl_timeout = 10
220
224
  http.open_timeout = 10
@@ -0,0 +1,34 @@
1
+ module Timber
2
+ module LogDevices
3
+ # @private
4
+ #
5
+ # A log device that writes to multiple IO devices.
6
+ #
7
+ # Note, you should not have to instantiate this class directly. Simply pass multiple
8
+ # arguments to the `Timber::Logger#new` method.
9
+ #
10
+ # See the {Timber::Logger#new} for examples.
11
+ class Multi
12
+ def initialize(targets)
13
+ @targets = targets
14
+ end
15
+
16
+ def write(*args)
17
+ @targets.each { |t| t.write(*args) }
18
+ @targets.first
19
+ end
20
+
21
+ def sync=(value)
22
+ @targets.each do |t|
23
+ if t.respond_to?(:sync=)
24
+ t.sync = value
25
+ end
26
+ end
27
+ end
28
+
29
+ def close
30
+ @targets.each(&:close)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -8,7 +8,8 @@ module Timber
8
8
  # `Logger` and the log device that you set it up with.
9
9
  class LogEntry #:nodoc:
10
10
  DT_PRECISION = 6.freeze
11
- SCHEMA = "https://raw.githubusercontent.com/timberio/log-event-json-schema/v2.0.4/schema.json".freeze
11
+ MESSAGE_MAX_BYTES = 8192.freeze
12
+ SCHEMA = "https://raw.githubusercontent.com/timberio/log-event-json-schema/v3.0.7/schema.json".freeze
12
13
 
13
14
  attr_reader :context_snapshot, :event, :level, :message, :progname, :tags, :time, :time_ms
14
15
 
@@ -31,6 +32,7 @@ module Timber
31
32
  # This follows the default behavior set by ::Logger
32
33
  # See: https://github.com/ruby/ruby/blob/trunk/lib/logger.rb#L615
33
34
  @message = message.is_a?(String) ? message : message.inspect
35
+ @message = @message.byteslice(0, MESSAGE_MAX_BYTES)
34
36
  @tags = options[:tags]
35
37
  @time_ms = options[:time_ms]
36
38
  @context_snapshot = context_snapshot
@@ -4,7 +4,7 @@ require "msgpack"
4
4
  require "timber/config"
5
5
  require "timber/current_context"
6
6
  require "timber/event"
7
- require "timber/log_devices/http"
7
+ require "timber/log_devices"
8
8
  require "timber/log_entry"
9
9
 
10
10
  module Timber
@@ -185,24 +185,50 @@ module Timber
185
185
  include ::ActiveSupport::LoggerThreadSafeLevel if defined?(::ActiveSupport::LoggerThreadSafeLevel)
186
186
  include ::LoggerSilence if defined?(::LoggerSilence)
187
187
 
188
- # Creates a new Timber::Logger instances. Accepts the same arguments as `::Logger.new`.
189
- # The only difference is that it default the formatter to {AugmentedFormatter}. Using
190
- # a different formatter is easy. For example, if you prefer your logs in JSON.
188
+ # Creates a new Timber::Logger instance where the passed argument is an IO device. That is,
189
+ # anything that responds to `#write` and `#close`.
191
190
  #
192
- # @example Changing your formatter
191
+ # Note, this method does *not* accept the same arguments as the standard Ruby `::Logger`.
192
+ # The Ruby `::Logger` accepts additional options controlling file rotation if the first argument
193
+ # is a file *name*. This is a design flaw that Timber does not assume. Logging to a file, or
194
+ # multiple IO devices is demonstrated in the examples below.
195
+ #
196
+ # @example Logging to STDOUT
193
197
  # logger = Timber::Logger.new(STDOUT)
194
- # logger.formatter = Timber::Logger::JSONFormatter.new
195
- def initialize(*args)
196
- super(*args)
198
+ #
199
+ # @example Logging to the Timber HTTP device
200
+ # http_device = Timber::LogDevices::HTTP.new("my-timber-api-key")
201
+ # logger = Timber::Logger.new(http_device)
202
+ #
203
+ # @example Logging to a file (with rotation)
204
+ # file_device = Logger::LogDevice.new("path/to/file.log")
205
+ # logger = Timber::Logger.new(file_device)
206
+ #
207
+ # @example Logging to a file and the Timber HTTP device (multiple log devices)
208
+ # http_device = Timber::LogDevices::HTTP.new("my-timber-api-key")
209
+ # file_device = Logger::LogDevice.new("path/to/file.log")
210
+ # logger = Timber::Logger.new(http_device, file_device)
211
+ def initialize(*io_devices)
212
+ io_device = \
213
+ if io_devices.size == 0
214
+ raise ArgumentError.new("At least one IO device must be provided when instantiating " +
215
+ "a Timber::Logger. Ex: Timber::Logger.new(STDOUT).")
216
+ elsif io_devices.size > 1
217
+ LogDevices::Multi.new(io_devices)
218
+ else
219
+ io_devices.first
220
+ end
221
+
222
+ super(io_device)
197
223
 
198
224
  # Ensure we sync STDOUT to avoid buffering
199
- if args.size == 1 and args.first.respond_to?(:"sync=")
200
- args.first.sync = true
225
+ if io_device.respond_to?(:"sync=")
226
+ io_device.sync = true
201
227
  end
202
228
 
203
229
  # Set the default formatter. The formatter cannot be set during
204
230
  # initialization, and can be changed with #formatter=.
205
- if args.size == 1 and args.first.is_a?(LogDevices::HTTP)
231
+ if io_device.is_a?(LogDevices::HTTP)
206
232
  self.formatter = PassThroughFormatter.new
207
233
  elsif Config.instance.development? || Config.instance.test?
208
234
  self.formatter = MessageOnlyFormatter.new
@@ -1,10 +1,9 @@
1
1
  module Timber
2
2
  module Util
3
- # Utility module for dealing with HTTP events {Events::HTTPServerRequest},
4
- # {Events::HTTPServerResponse}, {Events::HTTPClientRequest}, {Events::HTTPClientResponse}.
3
+ # Utility module for dealing with HTTP events {Events::HTTPRequest} and {Events::HTTPResponse}.
5
4
  module HTTPEvent
6
5
  HEADERS_TO_SANITIZE = ['authorization', 'x-amz-security-token'].freeze
7
- QUERY_STRING_LIMIT = 5_000.freeze
6
+ MAX_QUERY_STRING_BYTES = 2048.freeze
8
7
  STRING_CLASS_NAME = 'String'.freeze
9
8
 
10
9
  extend self
@@ -25,7 +24,7 @@ module Timber
25
24
 
26
25
  limit = Config.instance.http_body_limit
27
26
  if limit
28
- body[0..(limit - 1)]
27
+ body.byteslice(0, limit)
29
28
  else
30
29
  body
31
30
  end
@@ -63,7 +62,7 @@ module Timber
63
62
  query_string = query_string.to_s
64
63
  end
65
64
 
66
- query_string && query_string[0..(QUERY_STRING_LIMIT - 1)]
65
+ query_string && query_string.byteslice(0, MAX_QUERY_STRING_BYTES)
67
66
  end
68
67
  end
69
68
  end
@@ -1,3 +1,3 @@
1
1
  module Timber
2
- VERSION = "2.1.1"
2
+ VERSION = "2.1.2"
3
3
  end
@@ -3,9 +3,9 @@ require "spec_helper"
3
3
  describe Timber::Contexts::System, :rails_23 => true do
4
4
  describe ".as_json" do
5
5
  it "should coerce pid into an string" do
6
- custom_context = described_class.new(:pid => 1)
6
+ custom_context = described_class.new(:pid => "1")
7
7
  json = custom_context.as_json()
8
- expect(json[:pid]).to eq("1")
8
+ expect(json[:pid]).to eq(1)
9
9
  end
10
10
  end
11
11
  end
@@ -1,6 +1,6 @@
1
1
  require "spec_helper"
2
2
 
3
- describe Timber::Events::Exception, :rails_23 => true do
3
+ describe Timber::Events::Error, :rails_23 => true do
4
4
  describe ".initialize" do
5
5
  it "should clean the backtrace" do
6
6
  backtrace = [
@@ -8,7 +8,7 @@ describe Timber::Events::Exception, :rails_23 => true do
8
8
  "path/to/file2.rb:86:in `function2'"
9
9
  ]
10
10
 
11
- exception_event = described_class.new(name: "RuntimeError", exception_message: "Boom", backtrace: backtrace)
11
+ exception_event = described_class.new(name: "RuntimeError", error_message: "Boom", backtrace: backtrace)
12
12
  expect(exception_event.backtrace).to eq([{:file=>"/path/to/file1.rb", :line=>26, :function=>"function1"}, {:file=>"path/to/file2.rb", :line=>86, :function=>"function2"}])
13
13
  end
14
14
 
@@ -18,7 +18,7 @@ describe Timber::Events::Exception, :rails_23 => true do
18
18
  "path/to/file2.rb:86" # function names are optional
19
19
  ]
20
20
 
21
- exception_event = described_class.new(name: "RuntimeError", exception_message: "Boom", backtrace: backtrace)
21
+ exception_event = described_class.new(name: "RuntimeError", error_message: "Boom", backtrace: backtrace)
22
22
  expect(exception_event.backtrace).to eq([{:file=>"/path/to/file1.rb", :line=>26, :function=>"function1"}, {:file=>"path/to/file2.rb", :line=>86}])
23
23
  end
24
24
 
@@ -27,7 +27,7 @@ describe Timber::Events::Exception, :rails_23 => true do
27
27
  "malformed"
28
28
  ]
29
29
 
30
- exception_event = described_class.new(name: "RuntimeError", exception_message: "Boom", backtrace: backtrace)
30
+ exception_event = described_class.new(name: "RuntimeError", error_message: "Boom", backtrace: backtrace)
31
31
  expect(exception_event.backtrace).to eq([{:file=>"malformed"}])
32
32
  end
33
33
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "spec_helper"
4
4
 
5
- describe Timber::Events::HTTPServerRequest, :rails_23 => true do
5
+ describe Timber::Events::HTTPRequest, :rails_23 => true do
6
6
  describe ".initialize" do
7
7
  context "with a header filters" do
8
8
  around(:each) do |example|
@@ -41,7 +41,7 @@ if defined?(::ActionDispatch)
41
41
  lines = clean_lines(io.string.split("\n"))
42
42
  expect(lines.length).to eq(3)
43
43
  expect(lines[2]).to start_with('RuntimeError (boom) @metadata {"level":"fatal",')
44
- expect(lines[2]).to include("\"event\":{\"exception\":{\"name\":\"RuntimeError\",\"message\":\"boom\",\"backtrace\":[")
44
+ expect(lines[2]).to include("\"event\":{\"error\":{\"name\":\"RuntimeError\",\"message\":\"boom\",\"backtrace\":[")
45
45
  end
46
46
 
47
47
  # Remove blank lines since Rails does this to space out requests in the logs
@@ -20,6 +20,15 @@ describe Timber::Logger, :rails_23 => true do
20
20
  expect(logger.formatter).to be_kind_of(Timber::Logger::MessageOnlyFormatter)
21
21
  end
22
22
  end
23
+
24
+ it "should use the Multi log device" do
25
+ io1 = StringIO.new
26
+ io2 = StringIO.new
27
+ logger = Timber::Logger.new(STDOUT, io1, io2)
28
+ logger.info("hello world")
29
+ expect(io1.string).to start_with("hello world @metadata {")
30
+ expect(io2.string).to start_with("hello world @metadata {")
31
+ end
23
32
  end
24
33
 
25
34
  describe "#add" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timber
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Timber Technologies, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-28 00:00:00.000000000 Z
11
+ date: 2017-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -182,11 +182,9 @@ files:
182
182
  - lib/timber/events.rb
183
183
  - lib/timber/events/controller_call.rb
184
184
  - lib/timber/events/custom.rb
185
- - lib/timber/events/exception.rb
186
- - lib/timber/events/http_client_request.rb
187
- - lib/timber/events/http_client_response.rb
188
- - lib/timber/events/http_server_request.rb
189
- - lib/timber/events/http_server_response.rb
185
+ - lib/timber/events/error.rb
186
+ - lib/timber/events/http_request.rb
187
+ - lib/timber/events/http_response.rb
190
188
  - lib/timber/events/sql_query.rb
191
189
  - lib/timber/events/template_render.rb
192
190
  - lib/timber/frameworks.rb
@@ -205,7 +203,7 @@ files:
205
203
  - lib/timber/integrations/active_record/log_subscriber.rb
206
204
  - lib/timber/integrations/active_record/log_subscriber/timber_log_subscriber.rb
207
205
  - lib/timber/integrations/rack.rb
208
- - lib/timber/integrations/rack/exception_event.rb
206
+ - lib/timber/integrations/rack/error_event.rb
209
207
  - lib/timber/integrations/rack/http_context.rb
210
208
  - lib/timber/integrations/rack/http_events.rb
211
209
  - lib/timber/integrations/rack/middleware.rb
@@ -218,6 +216,7 @@ files:
218
216
  - lib/timber/log_devices/http.rb
219
217
  - lib/timber/log_devices/http/dropping_sized_queue.rb
220
218
  - lib/timber/log_devices/http/flushable_sized_queue.rb
219
+ - lib/timber/log_devices/multi.rb
221
220
  - lib/timber/log_entry.rb
222
221
  - lib/timber/logger.rb
223
222
  - lib/timber/overrides.rb
@@ -261,8 +260,8 @@ files:
261
260
  - spec/timber/event_spec.rb
262
261
  - spec/timber/events/controller_call_spec.rb
263
262
  - spec/timber/events/custom_spec.rb
264
- - spec/timber/events/exception_spec.rb
265
- - spec/timber/events/http_server_request_spec.rb
263
+ - spec/timber/events/error_spec.rb
264
+ - spec/timber/events/http_request_spec.rb
266
265
  - spec/timber/events_spec.rb
267
266
  - spec/timber/integrations/action_controller/log_subscriber_spec.rb
268
267
  - spec/timber/integrations/action_dispatch/debug_exceptions_spec.rb
@@ -329,8 +328,8 @@ test_files:
329
328
  - spec/timber/event_spec.rb
330
329
  - spec/timber/events/controller_call_spec.rb
331
330
  - spec/timber/events/custom_spec.rb
332
- - spec/timber/events/exception_spec.rb
333
- - spec/timber/events/http_server_request_spec.rb
331
+ - spec/timber/events/error_spec.rb
332
+ - spec/timber/events/http_request_spec.rb
334
333
  - spec/timber/events_spec.rb
335
334
  - spec/timber/integrations/action_controller/log_subscriber_spec.rb
336
335
  - spec/timber/integrations/action_dispatch/debug_exceptions_spec.rb
@@ -1,65 +0,0 @@
1
- require "timber/event"
2
- require "timber/util"
3
-
4
- module Timber
5
- module Events
6
- # The HTTP client request event tracks *outgoing* HTTP requests giving you structured insight
7
- # into communication with external services.
8
- #
9
- # @note This event should be installed automatically through integrations,
10
- # such as the {Integrations::NetHTTP} integration.
11
- class HTTPClientRequest < Timber::Event
12
- attr_reader :body, :headers, :host, :method, :path, :port, :query_string, :request_id,
13
- :scheme, :service_name
14
-
15
- def initialize(attributes)
16
- @headers = Util::HTTPEvent.normalize_headers(attributes[:headers])
17
- @host = attributes[:host] || raise(ArgumentError.new(":host is required"))
18
- @method = Util::HTTPEvent.normalize_method(attributes[:method]) || raise(ArgumentError.new(":method is required"))
19
- @path = attributes[:path]
20
- @port = attributes[:port]
21
- @query_string = Util::HTTPEvent.normalize_query_string(attributes[:query_string])
22
- @request_id = attributes[:request_id]
23
- @scheme = attributes[:scheme] || raise(ArgumentError.new(":scheme is required"))
24
- @service_name = attributes[:service_name]
25
-
26
- @body = Util::HTTPEvent.normalize_body(@headers["content-type"], attributes[:body])
27
- end
28
-
29
- def to_hash
30
- {headers: headers, host: host, method: method, parsed_body_json: parsed_body_json,
31
- path: path, port: port, query_string: query_string, request_id: request_id,
32
- scheme: scheme, service_name: service_name}
33
- end
34
- alias to_h to_hash
35
-
36
- # Builds a hash representation containing simple objects, suitable for serialization (JSON).
37
- def as_json(_options = {})
38
- {:http_client_request => to_hash}
39
- end
40
-
41
- def message
42
- message = 'Outgoing HTTP request to '
43
-
44
- if service_name
45
- mesage << " #{service_name} [#{method}] #{full_path}"
46
- else
47
- message << " [#{method}] #{full_url}"
48
- end
49
- end
50
-
51
- def status_description
52
- Rack::Utils::HTTP_STATUS_CODES[status]
53
- end
54
-
55
- private
56
- def full_path
57
- Util::HTTPEvent.full_path(path, query_string)
58
- end
59
-
60
- def full_url
61
- "#{scheme}#{host}#{full_path}"
62
- end
63
- end
64
- end
65
- end
@@ -1,51 +0,0 @@
1
- require "timber/event"
2
- require "timber/util"
3
-
4
- module Timber
5
- module Events
6
- # The HTTP client response event tracks responses for *outgoing* HTTP *requests*.
7
- # This gives you structured insight into communication with external services.
8
- #
9
- # @note This event should be installed automatically through integrations,
10
- # such as the {Integrations::NetHTTP} integration.
11
- class HTTPClientResponse < Timber::Event
12
- attr_reader :body, :headers, :request_id, :service_name, :status, :time_ms
13
-
14
- def initialize(attributes)
15
- @headers = Util::HTTPEvent.normalize_headers(attributes[:headers])
16
- @request_id = attributes[:request_id]
17
- @service_name = attributes[:service_name]
18
- @status = attributes[:status] || raise(ArgumentError.new(":status is required"))
19
- @time_ms = attributes[:time_ms] || raise(ArgumentError.new(":time_ms is required"))
20
- @time_ms = @time_ms.round(6)
21
-
22
- @body = Util::HTTPEvent.normalize_body(@headers["content-type"], attributes[:body])
23
- end
24
-
25
- def to_hash
26
- {body: body, headers: headers, request_id: request_id, service_name: service_name,
27
- status: status, time_ms: time_ms}
28
- end
29
- alias to_h to_hash
30
-
31
- # Builds a hash representation containing simple objects, suitable for serialization (JSON).
32
- def as_json(_options = {})
33
- {:http_client_response => to_hash}
34
- end
35
-
36
- def message
37
- message = "Outgoing HTTP response"
38
-
39
- if service_name
40
- message << " from #{service_name}"
41
- end
42
-
43
- message << " #{status_description} in #{time_ms}ms"
44
- end
45
-
46
- def status_description
47
- Rack::Utils::HTTP_STATUS_CODES[status]
48
- end
49
- end
50
- end
51
- end