timber 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.yardopts +6 -0
- data/Appraisals +41 -0
- data/Gemfile +30 -0
- data/LICENSE.md +15 -0
- data/README.md +194 -0
- data/circle.yml +33 -0
- data/lib/timber/config.rb +18 -0
- data/lib/timber/context.rb +17 -0
- data/lib/timber/contexts/custom.rb +27 -0
- data/lib/timber/contexts/http.rb +28 -0
- data/lib/timber/contexts/organization.rb +35 -0
- data/lib/timber/contexts/user.rb +36 -0
- data/lib/timber/contexts.rb +10 -0
- data/lib/timber/current_context.rb +43 -0
- data/lib/timber/event.rb +17 -0
- data/lib/timber/events/controller_call.rb +40 -0
- data/lib/timber/events/custom.rb +42 -0
- data/lib/timber/events/exception.rb +35 -0
- data/lib/timber/events/http_request.rb +50 -0
- data/lib/timber/events/http_response.rb +36 -0
- data/lib/timber/events/sql_query.rb +26 -0
- data/lib/timber/events/template_render.rb +26 -0
- data/lib/timber/events.rb +37 -0
- data/lib/timber/frameworks/rails.rb +13 -0
- data/lib/timber/frameworks.rb +19 -0
- data/lib/timber/log_devices/http.rb +87 -0
- data/lib/timber/log_devices.rb +8 -0
- data/lib/timber/log_entry.rb +59 -0
- data/lib/timber/logger.rb +142 -0
- data/lib/timber/probe.rb +23 -0
- data/lib/timber/probes/action_controller_log_subscriber/log_subscriber.rb +64 -0
- data/lib/timber/probes/action_controller_log_subscriber.rb +20 -0
- data/lib/timber/probes/action_dispatch_debug_exceptions.rb +80 -0
- data/lib/timber/probes/action_view_log_subscriber/log_subscriber.rb +62 -0
- data/lib/timber/probes/action_view_log_subscriber.rb +20 -0
- data/lib/timber/probes/active_record_log_subscriber/log_subscriber.rb +29 -0
- data/lib/timber/probes/active_record_log_subscriber.rb +20 -0
- data/lib/timber/probes/rack_http_context.rb +51 -0
- data/lib/timber/probes/rails_rack_logger.rb +76 -0
- data/lib/timber/probes.rb +21 -0
- data/lib/timber/util/active_support_log_subscriber.rb +33 -0
- data/lib/timber/util/hash.rb +14 -0
- data/lib/timber/util.rb +8 -0
- data/lib/timber/version.rb +3 -0
- data/lib/timber.rb +22 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/action_controller.rb +4 -0
- data/spec/support/action_view.rb +4 -0
- data/spec/support/active_record.rb +28 -0
- data/spec/support/coveralls.rb +2 -0
- data/spec/support/rails/templates/_partial.html +1 -0
- data/spec/support/rails/templates/template.html +1 -0
- data/spec/support/rails.rb +37 -0
- data/spec/support/simplecov.rb +9 -0
- data/spec/support/socket_hostname.rb +12 -0
- data/spec/support/timber.rb +4 -0
- data/spec/support/timecop.rb +3 -0
- data/spec/support/webmock.rb +2 -0
- data/spec/timber/events_spec.rb +55 -0
- data/spec/timber/log_devices/http_spec.rb +62 -0
- data/spec/timber/logger_spec.rb +89 -0
- data/spec/timber/probes/action_controller_log_subscriber_spec.rb +70 -0
- data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +51 -0
- data/spec/timber/probes/action_view_log_subscriber_spec.rb +61 -0
- data/spec/timber/probes/active_record_log_subscriber_spec.rb +52 -0
- data/spec/timber/probes/rack_http_context_spec.rb +54 -0
- data/spec/timber/probes/rails_rack_logger_spec.rb +46 -0
- data/timber.gemspec +22 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8580577f8c85abeb5eacec510136a2bf05d72406
|
4
|
+
data.tar.gz: 7a08aa6f00eda500d016c413ccf2b95d759b9266
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b906867c8198c1d94052856b6e7b402af88bd252b770ed9a8a33aae7284a3f562569f4fde7202c759eb786e28f8b85b0b5a2b1ab8c2b5d63bde56f1ee686d404
|
7
|
+
data.tar.gz: d4afc694df19c1c90a4711b7d1762bb0f203cf4521aa44a1f63d5e7f66c8cef37258f7af2c8e6fb73b166305aa43a1b4bfce3a2ea0c625e1951e9bc0726657dc
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.yardopts
ADDED
data/Appraisals
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
appraise "rails-2.3.X" do
|
2
|
+
gem "rails", "~> 2.3.18"
|
3
|
+
end
|
4
|
+
|
5
|
+
appraise "rails-3.0.X" do
|
6
|
+
gem "rails", "~> 3.0.20"
|
7
|
+
end
|
8
|
+
|
9
|
+
appraise "rails-3.1.X" do
|
10
|
+
gem "rails", "~> 3.1.12"
|
11
|
+
end
|
12
|
+
|
13
|
+
appraise "rails-3.2.X" do
|
14
|
+
gem "rails", "~> 3.2.22"
|
15
|
+
end
|
16
|
+
|
17
|
+
appraise "rails-4.0.X" do
|
18
|
+
gem "minitest"
|
19
|
+
gem "rails", "~> 4.0.13"
|
20
|
+
end
|
21
|
+
|
22
|
+
appraise "rails-4.1.X" do
|
23
|
+
gem "minitest"
|
24
|
+
gem "rails", "~> 4.1.15"
|
25
|
+
end
|
26
|
+
|
27
|
+
appraise "rails-4.2.X" do
|
28
|
+
gem "minitest"
|
29
|
+
gem "rails", "~> 4.2.7"
|
30
|
+
end
|
31
|
+
|
32
|
+
appraise "rails-5.0.X" do
|
33
|
+
gem "minitest"
|
34
|
+
gem "rails", "~> 5.0.0"
|
35
|
+
end
|
36
|
+
|
37
|
+
appraise "rails-edge" do
|
38
|
+
gem 'rack', github: 'rack/rack', branch: 'master'
|
39
|
+
gem 'arel', github: 'rails/arel', branch: 'master'
|
40
|
+
gem 'rails', github: 'rails/rails', branch: 'master'
|
41
|
+
end
|
data/Gemfile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
gemspec
|
3
|
+
|
4
|
+
group :test do
|
5
|
+
gem 'appraisal'
|
6
|
+
gem 'coveralls', require: false
|
7
|
+
gem 'json', '~> 1'
|
8
|
+
gem 'pry'
|
9
|
+
gem 'rails_stdout_logging'
|
10
|
+
gem 'rake'
|
11
|
+
gem 'rspec', '~> 3.4'
|
12
|
+
gem 'rspec-its'
|
13
|
+
gem 'simplecov', require: false
|
14
|
+
gem 'sqlite3'
|
15
|
+
gem 'terminal-table'
|
16
|
+
gem 'timecop'
|
17
|
+
|
18
|
+
ruby_version = Gem::Version.new("#{RUBY_VERSION}")
|
19
|
+
if ruby_version < Gem::Version.new("2.0.0")
|
20
|
+
gem 'public_suffix', '~> 1.4.6'
|
21
|
+
gem 'term-ansicolor', '~> 1.3.2'
|
22
|
+
gem 'webmock', '~> 2.2.0'
|
23
|
+
else
|
24
|
+
gem 'webmock'
|
25
|
+
end
|
26
|
+
|
27
|
+
# for coveralls
|
28
|
+
gem 'rest-client', '~> 1.8' # >= 2.0 requires ruby 2+, we have tests for 1.9
|
29
|
+
gem 'tins', '~> 1.6.0' # > 1.6 requires ruby 2+, we have tests for 1.9
|
30
|
+
end
|
data/LICENSE.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# License
|
2
|
+
|
3
|
+
Copyright (c) 2016, Timber Technologies, Inc.
|
4
|
+
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose
|
6
|
+
with or without fee is hereby granted, provided that the above copyright notice
|
7
|
+
and this permission notice appear in all copies.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
11
|
+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
13
|
+
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
14
|
+
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
15
|
+
THIS SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
# Timber
|
2
|
+
|
3
|
+
<p align="center" style="background: #140f2a;">
|
4
|
+
<a href="http://github.com/timberio/timber-ruby"><img src="http://files.timber.io/images/ruby-library-readme-header.gif" height="469" /></a>
|
5
|
+
</p>
|
6
|
+
|
7
|
+
[![CircleCI](https://circleci.com/gh/timberio/timber-ruby.svg?style=shield&circle-token=:circle-token)](https://circleci.com/gh/timberio/timber-ruby/tree/master)
|
8
|
+
[![Coverage Status](https://coveralls.io/repos/github/timberio/timber-ruby/badge.svg?branch=master)](https://coveralls.io/github/timberio/timber-ruby?branch=master)
|
9
|
+
[![Code Climate](https://codeclimate.com/github/timberio/timber-ruby/badges/gpa.svg)](https://codeclimate.com/github/timberio/timber-ruby)
|
10
|
+
[![View docs](https://img.shields.io/badge/docs-viewdocs-blue.svg?style=flat-square "Viewdocs")](http://www.rubydoc.info/github/timberio/timber-ruby)
|
11
|
+
|
12
|
+
|
13
|
+
1. [What is timber?](#what-is-timber)
|
14
|
+
1. [How does it work?](#what-is-timber)
|
15
|
+
2. [Logging Custom Events](#logging-custom-events)
|
16
|
+
3. [The Timber Console / Pricing](#the-timber-console-pricing)
|
17
|
+
2. [Install](#install)
|
18
|
+
|
19
|
+
|
20
|
+
## What is Timber?
|
21
|
+
|
22
|
+
Timber automatically structures your logs with events and context in a non-proprietary JSON format.
|
23
|
+
It’s simple, quick, managed, and has absolutely no risk of code debt or lock-in.
|
24
|
+
It’s just good ol’ logging.
|
25
|
+
|
26
|
+
Timber’s philosophy is that application insight should be open and owned by you.
|
27
|
+
And there is no better vehicle than logging:
|
28
|
+
|
29
|
+
1. It’s a shared practice that has been around since the dawn of computers.
|
30
|
+
2. It’s baked into every language, library, and framework. Even your own apps.
|
31
|
+
3. The data is entirely owned by you.
|
32
|
+
|
33
|
+
The problem is that logs are messy, noisy, and hard to use. Timber solves this by being
|
34
|
+
application aware, properly structuring your logs, and optionally providing a [fast, modern,
|
35
|
+
and beautiful console](https://timber.io) -- allowing you to realize the power of
|
36
|
+
your logs.
|
37
|
+
|
38
|
+
|
39
|
+
## How does it work?
|
40
|
+
|
41
|
+
Glad you asked! :) Timber automatically structures your logs by taking advantage of public APIs.
|
42
|
+
|
43
|
+
For example, by subscribing to `ActiveSupport::Notifications`, Timber can automatically turn this:
|
44
|
+
|
45
|
+
```
|
46
|
+
Completed 200 OK in 117ms (Views: 85.2ms | ActiveRecord: 25.3ms)
|
47
|
+
```
|
48
|
+
|
49
|
+
Into this:
|
50
|
+
|
51
|
+
```json
|
52
|
+
{
|
53
|
+
"dt": "2016-12-01T02:23:12.236543Z",
|
54
|
+
"level": "info",
|
55
|
+
"message": "Completed 200 OK in 117ms (Views: 85.2ms | ActiveRecord: 25.3ms)",
|
56
|
+
"context": {
|
57
|
+
"http": {
|
58
|
+
"method": "GET",
|
59
|
+
"path": "/checkout",
|
60
|
+
"remote_addr": "123.456.789.10",
|
61
|
+
"request_id": "abcd1234"
|
62
|
+
},
|
63
|
+
"user": {
|
64
|
+
"id": 2,
|
65
|
+
"name": "Ben Johnson",
|
66
|
+
"email": "ben@johnson.com"
|
67
|
+
}
|
68
|
+
},
|
69
|
+
"event": {
|
70
|
+
"http_response": {
|
71
|
+
"status": 200,
|
72
|
+
"time_ms": 117
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
```
|
77
|
+
|
78
|
+
It does the same for `http requests`, `sql queries`, `exceptions`, `template renderings`,
|
79
|
+
and any other event your framework logs. (for a full list see `Timber::Events`)
|
80
|
+
|
81
|
+
|
82
|
+
## Logging Custom Events
|
83
|
+
|
84
|
+
> Another service? More code debt? :*(
|
85
|
+
|
86
|
+
Nope! Logging custom events is Just Logging™. Check it out:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
# Simple string (original Logger interface remains untouched)
|
90
|
+
Logger.warn "Payment rejected for customer abcd1234, reason: Card expired"
|
91
|
+
|
92
|
+
# Structured hash
|
93
|
+
Logger.warn message: "Payment rejected", type: :payment_rejected,
|
94
|
+
data: %{customer_id: "abcd1234", amount: 100, reason: "Card expired"}
|
95
|
+
|
96
|
+
# Using a Struct
|
97
|
+
PaymentRejectedEvent = Struct.new(:customer_id, :amount, :reason) do
|
98
|
+
def message; "Payment rejected for #{customer_id}"; end
|
99
|
+
def type; :payment_rejected; end
|
100
|
+
end
|
101
|
+
Logger.warn PaymentRejectedEvent.new("abcd1234", 100, "Card expired")
|
102
|
+
```
|
103
|
+
|
104
|
+
(for more examples, see the `Timber::Logger` docs)
|
105
|
+
|
106
|
+
No mention of Timber anywhere! In fact, this approach pushes things the opposite way. What if,
|
107
|
+
as a result of structured logging, you could start decoupling other services from your application?
|
108
|
+
|
109
|
+
Before:
|
110
|
+
|
111
|
+
```
|
112
|
+
|---[HTTP]---> sentry / bugsnag / etc
|
113
|
+
My Application |---[HTTP]---> librato / graphite / etc
|
114
|
+
|---[HTTP]---> new relic / etc
|
115
|
+
|--[STDOUT]--> logs
|
116
|
+
|---> Logging service
|
117
|
+
|---> S3
|
118
|
+
|---> RedShift
|
119
|
+
```
|
120
|
+
|
121
|
+
|
122
|
+
After:
|
123
|
+
|
124
|
+
```
|
125
|
+
|-- sentry / bugsnag / etc
|
126
|
+
|-- librato / graphite / etc
|
127
|
+
My Application |--[STDOUT]--> logs ---> Timber ---> |-- new relic / etc
|
128
|
+
^ |-- S3
|
129
|
+
| |-- RedShift
|
130
|
+
| ^
|
131
|
+
fast, efficient, durable, |
|
132
|
+
replayable, auditable, change any of these without
|
133
|
+
just logging touching your code
|
134
|
+
*and* backfill them!
|
135
|
+
```
|
136
|
+
|
137
|
+
[Mind-blown!](http://i.giphy.com/EldfH1VJdbrwY.gif)
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
## The Timber Console / Pricing
|
142
|
+
|
143
|
+
> This is all gravy, but wouldn't the extra data get expensive?
|
144
|
+
|
145
|
+
If you opt use the [Timber Console](https://timber.io), we only charge for
|
146
|
+
the size of the `message`, `dt`, and `event.custom` attributes. Everything else is
|
147
|
+
stored at no cost to you. [Say wha?!](http://i.giphy.com/l0HlL2vlfpWI0meJi.gif). This ensures
|
148
|
+
pricing remains predictable. We charge per GB sent to us and retained. No user limits,
|
149
|
+
no weird feature matrixes, just data. Finally, the data is yours, in a simple
|
150
|
+
non-proprietary JSON format that you can export to S3, Redshift, or any of our other integrations.
|
151
|
+
|
152
|
+
For more details checkout out [timber.io](https://timber.io).
|
153
|
+
|
154
|
+
## Install
|
155
|
+
|
156
|
+
### 1. Install the gem:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
# Gemfile
|
160
|
+
gem 'timber'
|
161
|
+
```
|
162
|
+
|
163
|
+
### 2. Install the logger:
|
164
|
+
|
165
|
+
#### Heroku:
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
# config/environments/production.rb (or staging, etc)
|
169
|
+
config.logger = Timber::Logger.new(STDOUT)
|
170
|
+
```
|
171
|
+
|
172
|
+
The command to add your log drain will be displayed in the [Timber app](https://app.timber.io)
|
173
|
+
after you add your application.
|
174
|
+
|
175
|
+
#### Non-Heroku:
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
# config/environments/production.rb (or staging, etc)
|
179
|
+
log_device = Timber::LogDevices::HTTP.new(ENV['TIMBER_KEY']) # key can be obtained by signing up at https://timber.io
|
180
|
+
config.logger = Timber::Logger.new(log_device)
|
181
|
+
```
|
182
|
+
|
183
|
+
Your Timber application key will be displayed in the [Timber app](https://app.timber.io)
|
184
|
+
after you add your application.
|
185
|
+
|
186
|
+
|
187
|
+
*Other transport methods coming soon!*
|
188
|
+
|
189
|
+
---
|
190
|
+
|
191
|
+
That's it! Log to your heart's content.
|
192
|
+
|
193
|
+
For documentation on logging structured events, and other features,
|
194
|
+
checkout [the docs](http://thedocs.com/). For more information on Timber visit [timber.io](https://timber.io).
|
data/circle.yml
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
machine:
|
2
|
+
environment:
|
3
|
+
COVERALLS_REPO_TOKEN: fzLA6t2EFT4KBemv3du0AdHGcqTzyRzlr
|
4
|
+
|
5
|
+
dependencies:
|
6
|
+
override:
|
7
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-1.9.3-p448/bin:$PATH rvm-exec 1.9.3 bundle install
|
8
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-1.9.3-p448/bin:$PATH rvm-exec 1.9.3 appraisal generate
|
9
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-1.9.3-p448/bin:$PATH rvm-exec 1.9.3 appraisal rails-2.3.X bundle install
|
10
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-1.9.3-p448/bin:$PATH rvm-exec 1.9.3 appraisal rails-3.0.X bundle install
|
11
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-1.9.3-p448/bin:$PATH rvm-exec 1.9.3 appraisal rails-3.1.X bundle install
|
12
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-1.9.3-p448/bin:$PATH rvm-exec 1.9.3 appraisal rails-3.2.X bundle install
|
13
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-2.2.6/bin:$PATH rvm-exec 2.2.6 bundle install
|
14
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-2.2.6/bin:$PATH rvm-exec 2.2.6 appraisal install
|
15
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-2.3.3/bin:$PATH rvm-exec 2.3.3 bundle install
|
16
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-2.3.3/bin:$PATH rvm-exec 2.3.3 appraisal install
|
17
|
+
|
18
|
+
test:
|
19
|
+
override:
|
20
|
+
- RAILS_23=true PATH=/home/ubuntu/.rvm/gems/ruby-1.9.3-p448/bin:$PATH rvm-exec 1.9.3 appraisal rails-2.3.X rspec --tag rails_23
|
21
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-1.9.3-p448/bin:$PATH rvm-exec 1.9.3 appraisal rails-3.0.X rspec
|
22
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-1.9.3-p448/bin:$PATH rvm-exec 1.9.3 appraisal rails-3.1.X rspec
|
23
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-1.9.3-p448/bin:$PATH rvm-exec 1.9.3 appraisal rails-3.2.X rspec
|
24
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-2.2.6/bin:$PATH rvm-exec 2.2.6 appraisal rails-4.0.X rspec
|
25
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-2.2.6/bin:$PATH rvm-exec 2.2.6 appraisal rails-4.1.X rspec
|
26
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-2.2.6/bin:$PATH rvm-exec 2.2.6 appraisal rails-4.2.X rspec
|
27
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-2.2.6/bin:$PATH rvm-exec 2.2.6 appraisal rails-5.0.X rspec
|
28
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-2.2.6/bin:$PATH rvm-exec 2.2.6 appraisal rails-edge rspec
|
29
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-2.3.3/bin:$PATH rvm-exec 2.3.3 appraisal rails-4.0.X rspec
|
30
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-2.3.3/bin:$PATH rvm-exec 2.3.3 appraisal rails-4.1.X rspec
|
31
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-2.3.3/bin:$PATH rvm-exec 2.3.3 appraisal rails-4.2.X rspec
|
32
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-2.3.3/bin:$PATH rvm-exec 2.3.3 appraisal rails-5.0.X rspec
|
33
|
+
- PATH=/home/ubuntu/.rvm/gems/ruby-2.3.3/bin:$PATH rvm-exec 2.3.3 appraisal rails-edge rspec
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "singleton"
|
2
|
+
|
3
|
+
module Timber
|
4
|
+
# Interface for configuring Timber.
|
5
|
+
#
|
6
|
+
# @note If using rails this will be installed in the `config` object via `config.timber`.
|
7
|
+
class Config
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
attr_writer :logger
|
11
|
+
|
12
|
+
# Set a logger to view internal Timber library log message.
|
13
|
+
# Useful for debugging. Defaults to `::Logger.new(nil)`.
|
14
|
+
def logger
|
15
|
+
@logger ||= Logger.new(nil)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Timber
|
2
|
+
# Base class for all `Timber::Contexts::*` classes.
|
3
|
+
# @private
|
4
|
+
class Context
|
5
|
+
def keyspace
|
6
|
+
raise NoImplementedError.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def as_json(options = {})
|
10
|
+
raise NotImplementedError.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_json(options = {})
|
14
|
+
Util::Hash.compact(as_json).to_json(options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Timber
|
2
|
+
module Contexts
|
3
|
+
# Custom contexts allow you to add application specific context not covered elsewhere.
|
4
|
+
#
|
5
|
+
# @example Adding a context
|
6
|
+
# custom_context = Timber::Contexts::Custom.new(type: :keyspace, data: %{my: "data"})
|
7
|
+
# Timber::CurrentContext.with(custom_context) do
|
8
|
+
# # ... anything logged here will have the context ...
|
9
|
+
# end
|
10
|
+
class Custom < Context
|
11
|
+
attr_reader :type, :data
|
12
|
+
|
13
|
+
def initialize(attributes)
|
14
|
+
@type = attributes[:type] || raise(ArgumentError.new(":type is required"))
|
15
|
+
@data = attributes[:data] || raise(ArgumentError.new(":data is required"))
|
16
|
+
end
|
17
|
+
|
18
|
+
def keyspace
|
19
|
+
:custom
|
20
|
+
end
|
21
|
+
|
22
|
+
def as_json(_options = {})
|
23
|
+
{type => data}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Timber
|
2
|
+
module Contexts
|
3
|
+
# The HTTP content tracks the current HTTP request being processed. This serves
|
4
|
+
# as join data across your logs, allowing you to query all logs for any attribute
|
5
|
+
# presented here. For example, viewing all logs for a given request_id.
|
6
|
+
#
|
7
|
+
# @note This context should be installed automatically through probes,
|
8
|
+
# such as the {Probes::RackHTTPContext} probe.
|
9
|
+
class HTTP < Context
|
10
|
+
attr_reader :method, :path, :remote_addr, :request_id
|
11
|
+
|
12
|
+
def initialize(attributes)
|
13
|
+
@method = attributes[:method] || raise(ArgumentError.new(":method is required"))
|
14
|
+
@path = attributes[:path] || raise(ArgumentError.new(":path is required"))
|
15
|
+
@remote_addr = attributes[:remote_addr]
|
16
|
+
@request_id = attributes[:request_id]
|
17
|
+
end
|
18
|
+
|
19
|
+
def keyspace
|
20
|
+
:http
|
21
|
+
end
|
22
|
+
|
23
|
+
def as_json(_options = {})
|
24
|
+
{:method => method, :path => path, :remote_addr => remote_addr, :request_id => request_id}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Timber
|
2
|
+
module Contexts
|
3
|
+
# The organization context tracks the organization of the currently
|
4
|
+
# authenticated user.
|
5
|
+
#
|
6
|
+
# You will want to add this context at the time you determine
|
7
|
+
# the organization a user belongs to, typically in the authentication
|
8
|
+
# flow.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# organization_context = Timber::Contexts::Organization.new(id: "abc1234", name: "Timber Inc")
|
13
|
+
# Timber::CurrentContext.with(organization_context) do
|
14
|
+
# # Logging will automatically include this context
|
15
|
+
# logger.info("This is a log message")
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
class Organization < Context
|
19
|
+
attr_reader :id, :name
|
20
|
+
|
21
|
+
def initialize(attributes)
|
22
|
+
@id = attributes[:id]
|
23
|
+
@name = attributes[:name]
|
24
|
+
end
|
25
|
+
|
26
|
+
def keyspace
|
27
|
+
:organization
|
28
|
+
end
|
29
|
+
|
30
|
+
def as_json(_options = {})
|
31
|
+
{id: id, name: name}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Timber
|
2
|
+
module Contexts
|
3
|
+
# The user context tracks the currently authenticated user.
|
4
|
+
#
|
5
|
+
# You will want to add this context at the time log the user in, typically
|
6
|
+
# during the authentication flow.
|
7
|
+
#
|
8
|
+
# Note: Timber will attempt to automatically add this if you add a #current_user
|
9
|
+
# method to your controllers. Most authentication solutions do this for you automatically.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# user_context = Timber::Contexts::User.new(id: "abc1234", name: "Ben Johnson")
|
14
|
+
# Timber::CurrentContext.with(user_context) do
|
15
|
+
# # Logging will automatically include this context
|
16
|
+
# logger.info("This is a log message")
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
class User < Context
|
20
|
+
attr_reader :id, :name
|
21
|
+
|
22
|
+
def initialize(attributes)
|
23
|
+
@id = attributes[:id]
|
24
|
+
@name = attributes[:name]
|
25
|
+
end
|
26
|
+
|
27
|
+
def keyspace
|
28
|
+
:user
|
29
|
+
end
|
30
|
+
|
31
|
+
def as_json(_options = {})
|
32
|
+
{id: id, name: name}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "singleton"
|
2
|
+
|
3
|
+
module Timber
|
4
|
+
# Holds the current context in a thread safe memory storage. This context is
|
5
|
+
# appended to every log line. Think of context as join data between your log lines,
|
6
|
+
# allowing you to relate them and filter them appropriately.
|
7
|
+
class CurrentContext
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
THREAD_NAMESPACE = :_timber_current_context.freeze
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# Convenience method for {#with}.
|
14
|
+
#
|
15
|
+
# @example Adding a context
|
16
|
+
# custom_context = Timber::Contexts::Custom.new(type: :keyspace, data: %{my: "data"})
|
17
|
+
# Timber::CurrentContext.with(custom_context) do
|
18
|
+
# # ... anything logged here will have the context ...
|
19
|
+
# end
|
20
|
+
def with(*args, &block)
|
21
|
+
instance.with(*args, &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Adds a context to the current stack.
|
26
|
+
def with(data)
|
27
|
+
key = data.keyspace
|
28
|
+
hash[key] = data
|
29
|
+
yield
|
30
|
+
ensure
|
31
|
+
hash.delete(key)
|
32
|
+
end
|
33
|
+
|
34
|
+
def snapshot
|
35
|
+
hash.clone
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def hash
|
40
|
+
Thread.current[THREAD_NAMESPACE] ||= {}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/timber/event.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Timber
|
2
|
+
# Base class for `Timber::Events::*`
|
3
|
+
# @private
|
4
|
+
class Event
|
5
|
+
def message
|
6
|
+
raise NotImplementedError.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def as_json(options = {})
|
10
|
+
raise NotImplementedError.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_json(options = {})
|
14
|
+
Util::Hash.compact(as_json).to_json(options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Timber
|
2
|
+
module Events
|
3
|
+
# The controller call event tracks controller invocations. For example, this line in Rails:
|
4
|
+
#
|
5
|
+
# Processing by PagesController#home as HTML
|
6
|
+
#
|
7
|
+
# @note This event should be installed automatically through probes,
|
8
|
+
# such as the {Probes::ActionControllerLogSubscriber} probe.
|
9
|
+
class ControllerCall < Timber::Event
|
10
|
+
attr_reader :controller, :action, :params, :format
|
11
|
+
|
12
|
+
def initialize(attributes)
|
13
|
+
@controller = attributes[:controller] || raise(ArgumentError.new(":controller is required"))
|
14
|
+
@action = attributes[:action] || raise(ArgumentError.new(":action is required"))
|
15
|
+
@params = attributes[:params]
|
16
|
+
@format = attributes[:format]
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hash
|
20
|
+
{controller: controller, action: action}
|
21
|
+
end
|
22
|
+
alias to_h to_hash
|
23
|
+
|
24
|
+
def as_json(_options = {})
|
25
|
+
{:controller_call => to_hash}
|
26
|
+
end
|
27
|
+
|
28
|
+
def message
|
29
|
+
message = "Processing by #{controller}##{action}"
|
30
|
+
if !message.nil?
|
31
|
+
message << " as #{format}"
|
32
|
+
end
|
33
|
+
if !params.nil? && params.length > 0
|
34
|
+
message << "\n Parameters: #{params.inspect}"
|
35
|
+
end
|
36
|
+
message
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Timber
|
2
|
+
module Events
|
3
|
+
# Allows for custom events that aren't covered elsewhere.
|
4
|
+
#
|
5
|
+
# Custom events can be used to encode information about events that are central
|
6
|
+
# to your line of business like receiving credit card payments, saving a draft of a post,
|
7
|
+
# or changing a user's password.
|
8
|
+
#
|
9
|
+
# For examples of logging custom events see {Logger}.
|
10
|
+
class Custom < Timber::Event
|
11
|
+
attr_reader :type, :message, :data
|
12
|
+
|
13
|
+
# Instantiates a new custom event that can be logged. See {Logger} for examples
|
14
|
+
# on logging custom events.
|
15
|
+
#
|
16
|
+
# @param [Hash] attributes the options to create a custom event with.
|
17
|
+
# @option attributes [Symbol] :type *required* The custom event type. This should be in
|
18
|
+
# snake case. Example: `:my_custom_event`.
|
19
|
+
# @option attributes [String] :message *required* The message to be logged.
|
20
|
+
# @option attributes [Hash] :data A hash of JSON encodable data to be stored with the
|
21
|
+
# log line.
|
22
|
+
def initialize(attributes)
|
23
|
+
@type = attributes[:type] || raise(ArgumentError.new(":type is required"))
|
24
|
+
@message = attributes[:message] || raise(ArgumentError.new(":message is required"))
|
25
|
+
@data = attributes[:data]
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_hash
|
29
|
+
{type => data}
|
30
|
+
end
|
31
|
+
alias to_h to_hash
|
32
|
+
|
33
|
+
def as_json(_options = {})
|
34
|
+
{:custom => to_hash}
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_json(options = {})
|
38
|
+
as_json().to_json(options)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|