timber 1.0.3

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.
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