timber 2.1.1 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +56 -156
- data/lib/timber/config.rb +7 -8
- data/lib/timber/config/integrations/rack.rb +4 -4
- data/lib/timber/contexts/runtime.rb +3 -2
- data/lib/timber/contexts/system.rb +2 -2
- data/lib/timber/current_context.rb +5 -0
- data/lib/timber/events.rb +3 -5
- data/lib/timber/events/controller_call.rb +7 -5
- data/lib/timber/events/{exception.rb → error.rb} +13 -12
- data/lib/timber/events/{http_server_request.rb → http_request.rb} +5 -5
- data/lib/timber/events/{http_server_response.rb → http_response.rb} +3 -3
- data/lib/timber/events/sql_query.rb +3 -0
- data/lib/timber/integrations/action_dispatch.rb +2 -2
- data/lib/timber/integrations/rack.rb +3 -3
- data/lib/timber/integrations/rack/{exception_event.rb → error_event.rb} +6 -6
- data/lib/timber/integrations/rack/http_events.rb +10 -10
- data/lib/timber/log_devices.rb +1 -0
- data/lib/timber/log_devices/http.rb +5 -1
- data/lib/timber/log_devices/multi.rb +34 -0
- data/lib/timber/log_entry.rb +3 -1
- data/lib/timber/logger.rb +37 -11
- data/lib/timber/util/http_event.rb +4 -5
- data/lib/timber/version.rb +1 -1
- data/spec/timber/contexts/system_spec.rb +2 -2
- data/spec/timber/events/{exception_spec.rb → error_spec.rb} +4 -4
- data/spec/timber/events/{http_server_request_spec.rb → http_request_spec.rb} +1 -1
- data/spec/timber/integrations/action_dispatch/debug_exceptions_spec.rb +1 -1
- data/spec/timber/logger_spec.rb +9 -0
- metadata +11 -12
- data/lib/timber/events/http_client_request.rb +0 -65
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fbf04b7803b278fe7386975994f5c16d019b125b
|
4
|
+
data.tar.gz: 27b112405610ebb0b2c948ba4e1ae0f698fd5ac0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a474845f4dc5c7c328d860458ce93d5a2769cd850ebb7117cf442662565ab654e1c57f3a209037d43f80a8f5b0b27e54cebfb581f6505aad8f0939c8e14285a1
|
7
|
+
data.tar.gz: 0f60bc9a972900139d780a22c9c10c0e35afaa46689c5cc8a4bacd22ee1db454fbb34f3ef003d5a92a070255c7f309d0c2fcd3e38e4ffce6c48bfbdff9de2f96
|
data/README.md
CHANGED
@@ -1,41 +1,28 @@
|
|
1
|
-
# 🌲 Timber -
|
1
|
+
# 🌲 Timber - Log Better. Solve Problems Faster.
|
2
2
|
|
3
3
|
[](LICENSE.md)
|
4
|
+
[](https://hex.pm/packages/timber)
|
5
|
+
[](https://hexdocs.pm/timber/index.html)
|
4
6
|
[](https://travis-ci.org/timberio/timber-ruby)
|
5
|
-
[](https://codeclimate.com/github/timberio/timber-ruby)
|
6
|
-
[](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
|
17
|
-
|
18
|
-
|
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
|
-
|
26
|
-
|
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.
|
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
|
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.
|
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>
|
314
|
+
<details><summary><strong>What about my current log statements?</strong></summary><p>
|
429
315
|
|
430
|
-
|
431
|
-
|
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
|
-
|
435
|
-
|
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
|
-
|
322
|
+
</p></details>
|
444
323
|
|
445
|
-
|
324
|
+
<details><summary><strong>When to use metadata or events?</strong></summary><p>
|
446
325
|
|
447
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
456
|
-
|
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
|
+
[](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-
|
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>
|
data/lib/timber/config.rb
CHANGED
@@ -36,7 +36,7 @@ module Timber
|
|
36
36
|
|
37
37
|
# @private
|
38
38
|
def initialize
|
39
|
-
@http_body_limit =
|
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 `
|
134
|
-
# If you want to capture more data, you can raise this to a maximum of `
|
135
|
-
# or lower this to be more efficient with data. `
|
136
|
-
# idea of the body content.
|
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 =
|
139
|
+
# config.timber.http_body_limit = 2048
|
141
140
|
# @example Everything else
|
142
|
-
# Timber::Config.instance.http_body_limit =
|
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::
|
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.
|
24
|
-
def
|
25
|
-
Timber::Integrations::Rack::
|
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
|
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, :
|
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
|
|
data/lib/timber/events.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
require "timber/events/controller_call"
|
2
2
|
require "timber/events/custom"
|
3
|
-
require "timber/events/
|
4
|
-
require "timber/events/
|
5
|
-
require "timber/events/
|
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 ||=
|
48
|
-
nil
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
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
|
11
|
-
|
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
|
-
|
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?
|
19
|
-
|
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:
|
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
|
-
{:
|
34
|
+
{:error => to_hash}
|
34
35
|
end
|
35
36
|
|
36
37
|
def message
|
37
|
-
"#{name} (#{
|
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
|
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]
|
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]
|
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
|
-
{:
|
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
|
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
|
-
{:
|
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/
|
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::
|
13
|
+
Rack::ErrorEvent.enabled?
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.integrate!
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require "timber/integrations/rack/
|
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
|
-
|
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,
|
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/
|
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
|
18
|
-
# {Timber::Events::
|
19
|
-
class
|
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::
|
34
|
+
Events::Error.new(
|
35
35
|
name: exception.class.name,
|
36
|
-
|
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/
|
7
|
-
require "timber/events/
|
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::
|
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::
|
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::
|
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::
|
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::
|
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::
|
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::
|
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::
|
167
|
+
Events::HTTPResponse.new(
|
168
168
|
body: event_body,
|
169
169
|
headers: headers,
|
170
170
|
request_id: request.request_id,
|
data/lib/timber/log_devices.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/timber/log_entry.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/timber/logger.rb
CHANGED
@@ -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
|
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
|
189
|
-
#
|
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
|
-
#
|
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
|
-
#
|
195
|
-
|
196
|
-
|
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
|
200
|
-
|
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
|
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::
|
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
|
-
|
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
|
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
|
65
|
+
query_string && query_string.byteslice(0, MAX_QUERY_STRING_BYTES)
|
67
66
|
end
|
68
67
|
end
|
69
68
|
end
|
data/lib/timber/version.rb
CHANGED
@@ -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(
|
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::
|
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",
|
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",
|
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",
|
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
|
@@ -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\":{\"
|
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
|
data/spec/timber/logger_spec.rb
CHANGED
@@ -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.
|
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-
|
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/
|
186
|
-
- lib/timber/events/
|
187
|
-
- lib/timber/events/
|
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/
|
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/
|
265
|
-
- spec/timber/events/
|
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/
|
333
|
-
- spec/timber/events/
|
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
|