timber 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +2 -0
  4. data/.yardopts +6 -0
  5. data/Appraisals +41 -0
  6. data/Gemfile +30 -0
  7. data/LICENSE.md +15 -0
  8. data/README.md +194 -0
  9. data/circle.yml +33 -0
  10. data/lib/timber/config.rb +18 -0
  11. data/lib/timber/context.rb +17 -0
  12. data/lib/timber/contexts/custom.rb +27 -0
  13. data/lib/timber/contexts/http.rb +28 -0
  14. data/lib/timber/contexts/organization.rb +35 -0
  15. data/lib/timber/contexts/user.rb +36 -0
  16. data/lib/timber/contexts.rb +10 -0
  17. data/lib/timber/current_context.rb +43 -0
  18. data/lib/timber/event.rb +17 -0
  19. data/lib/timber/events/controller_call.rb +40 -0
  20. data/lib/timber/events/custom.rb +42 -0
  21. data/lib/timber/events/exception.rb +35 -0
  22. data/lib/timber/events/http_request.rb +50 -0
  23. data/lib/timber/events/http_response.rb +36 -0
  24. data/lib/timber/events/sql_query.rb +26 -0
  25. data/lib/timber/events/template_render.rb +26 -0
  26. data/lib/timber/events.rb +37 -0
  27. data/lib/timber/frameworks/rails.rb +13 -0
  28. data/lib/timber/frameworks.rb +19 -0
  29. data/lib/timber/log_devices/http.rb +87 -0
  30. data/lib/timber/log_devices.rb +8 -0
  31. data/lib/timber/log_entry.rb +59 -0
  32. data/lib/timber/logger.rb +142 -0
  33. data/lib/timber/probe.rb +23 -0
  34. data/lib/timber/probes/action_controller_log_subscriber/log_subscriber.rb +64 -0
  35. data/lib/timber/probes/action_controller_log_subscriber.rb +20 -0
  36. data/lib/timber/probes/action_dispatch_debug_exceptions.rb +80 -0
  37. data/lib/timber/probes/action_view_log_subscriber/log_subscriber.rb +62 -0
  38. data/lib/timber/probes/action_view_log_subscriber.rb +20 -0
  39. data/lib/timber/probes/active_record_log_subscriber/log_subscriber.rb +29 -0
  40. data/lib/timber/probes/active_record_log_subscriber.rb +20 -0
  41. data/lib/timber/probes/rack_http_context.rb +51 -0
  42. data/lib/timber/probes/rails_rack_logger.rb +76 -0
  43. data/lib/timber/probes.rb +21 -0
  44. data/lib/timber/util/active_support_log_subscriber.rb +33 -0
  45. data/lib/timber/util/hash.rb +14 -0
  46. data/lib/timber/util.rb +8 -0
  47. data/lib/timber/version.rb +3 -0
  48. data/lib/timber.rb +22 -0
  49. data/spec/spec_helper.rb +30 -0
  50. data/spec/support/action_controller.rb +4 -0
  51. data/spec/support/action_view.rb +4 -0
  52. data/spec/support/active_record.rb +28 -0
  53. data/spec/support/coveralls.rb +2 -0
  54. data/spec/support/rails/templates/_partial.html +1 -0
  55. data/spec/support/rails/templates/template.html +1 -0
  56. data/spec/support/rails.rb +37 -0
  57. data/spec/support/simplecov.rb +9 -0
  58. data/spec/support/socket_hostname.rb +12 -0
  59. data/spec/support/timber.rb +4 -0
  60. data/spec/support/timecop.rb +3 -0
  61. data/spec/support/webmock.rb +2 -0
  62. data/spec/timber/events_spec.rb +55 -0
  63. data/spec/timber/log_devices/http_spec.rb +62 -0
  64. data/spec/timber/logger_spec.rb +89 -0
  65. data/spec/timber/probes/action_controller_log_subscriber_spec.rb +70 -0
  66. data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +51 -0
  67. data/spec/timber/probes/action_view_log_subscriber_spec.rb +61 -0
  68. data/spec/timber/probes/active_record_log_subscriber_spec.rb +52 -0
  69. data/spec/timber/probes/rack_http_context_spec.rb +54 -0
  70. data/spec/timber/probes/rails_rack_logger_spec.rb +46 -0
  71. data/timber.gemspec +22 -0
  72. 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
@@ -0,0 +1,15 @@
1
+ .DS_Store
2
+ .rvmrc
3
+ .ruby-version
4
+ coverage
5
+ Gemfile.lock
6
+ *.swp
7
+ *.gem
8
+
9
+ /.bundle
10
+ /.yardoc
11
+ /doc
12
+ /gemfiles
13
+ /log
14
+ /tmp
15
+ /pkg
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --order rand
data/.yardopts ADDED
@@ -0,0 +1,6 @@
1
+ --protected
2
+ --no-private
3
+ --embed-mixin ClassMethods
4
+ -
5
+ LICENSE.md
6
+ README.md
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,10 @@
1
+ require "timber/contexts/custom"
2
+ require "timber/contexts/http"
3
+ require "timber/contexts/organization"
4
+ require "timber/contexts/user"
5
+
6
+ module Timber
7
+ # @private
8
+ module Contexts
9
+ end
10
+ 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
@@ -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