timber 2.0.7 → 2.0.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b8e5de46982d4346797f2fcefba7f45d601bc6cd
4
- data.tar.gz: 2a46a4fbc36cb8b6de0901cb138aeb7249ddd931
3
+ metadata.gz: 39440c7ae6a4d416b54191200b5a406681750be5
4
+ data.tar.gz: 04de3467c2bc3bdc363d1816ea892a74b4b237cc
5
5
  SHA512:
6
- metadata.gz: 81c9834d338f1d54f15df5c36802e761c3725da1cc36de4c46992fc7cc536ff6d0630678666937d22e0181dffdc7c1790ff2c4a4046732d8cdf352ccee60cb36
7
- data.tar.gz: 8167bb8a446d336b4f798da86f10d66b1b67cba12f1d95f4c0a1be828d08526add3346916451f4a02226f469320c476a43ff1372a2f67384d68500e63a0553a9
6
+ metadata.gz: c2d65105138fa4e900262dc15274c4a4dbc79b1ecb89a91ffedefcdc64371933d12e5157cb16ffe8999e48375255d2186f7e0cd62ab30efafe13ee9074d2d2a8
7
+ data.tar.gz: 9e1562e6465a72a7e491841617d58ec5dd1dd10d6174a34a92fba8817fcd80e9d8433555c7e1ead74022fd0bb90b314dc3b3ea8cbb78b61c31c92dba8dcda6a7
data/README.md CHANGED
@@ -15,10 +15,10 @@
15
15
 
16
16
  Timber for Ruby is an optional upgrade you can install for Ruby apps on the
17
17
  [Timber.io logging platform](https://timber.io). Instead of completely replacing your log messages,
18
- Timber automatically augments your logs with JSON metadata. Essentially turning them into
18
+ Timber efficiently augments your logs with critical metadata. Turning them into
19
19
  [rich events with context](https://timber.io/docs/ruby/events-and-context). This preserves the
20
- readability of your logs while still dramatically improving the quality of your data.
21
- The end result: better logging and faster problem solving.
20
+ readability of your logs while still adding the critical context needed to properly analyze your
21
+ logs.
22
22
 
23
23
 
24
24
  ## How it works
@@ -61,21 +61,6 @@ For a complete overview, see the [Timber for Ruby docs](https://timber.io/docs/r
61
61
  3. In your `shell`, run `bundle exec timber install`
62
62
 
63
63
 
64
- ## Configuration
65
-
66
- All configuration options can be seen in the
67
- [`Timber::Config docs`](http://www.rubydoc.info/github/timberio/timber-ruby/Timber/Config).
68
- Here are a few popular options:
69
-
70
- 1. `config.timber.format =`
71
-
72
- * `:default` - This is the default. It's the original, default, unchanged log messages.
73
-
74
- * `:lograge` - Works exactly like [lograge](https://github.com/roidrage/lograge), except Timber's
75
- additional context and metadata is also appended. Lograge++.
76
-
77
-
78
-
79
64
  ## Usage
80
65
 
81
66
  <details><summary><strong>Basic logging</strong></summary><p>
@@ -88,8 +73,6 @@ logger.info("My log message")
88
73
  # => My log message @metadata {"level": "info", "context": {...}}
89
74
  ```
90
75
 
91
- Timber will *never* deviate from the public `::Logger` interface in *any* way.
92
-
93
76
  ---
94
77
 
95
78
  </p></details>
@@ -100,7 +83,7 @@ Custom events allow you to extend beyond events already defined in
100
83
  the [`Timber::Events`](lib/timber/events) namespace.
101
84
 
102
85
  ```ruby
103
- Logger.warn "Payment rejected", payment_rejected: {customer_id: "abcd1234", amount: 100, reason: "Card expired"}
86
+ logger.warn "Payment rejected", payment_rejected: {customer_id: "abcd1234", amount: 100, reason: "Card expired"}
104
87
 
105
88
  # => Payment rejected @metadata {"level": "warn", "event": {"payment_rejected": {"customer_id": "abcd1234", "amount": 100, "reason": "Card expired"}}, "context": {...}}
106
89
  ```
@@ -120,7 +103,7 @@ Custom contexts allow you to extend beyond contexts already defined in
120
103
  the [`Timber::Contexts`](lib/timber/contexts) namespace.
121
104
 
122
105
  ```ruby
123
- Timber::CurrentContext.with({build: {version: "1.0.0"}}) do
106
+ logger.with_context(build: {version: "1.0.0"}) do
124
107
  logger.info("My log message")
125
108
  end
126
109
 
@@ -0,0 +1,122 @@
1
+ require 'benchmark'
2
+ require 'benchmark-memory'
3
+ require 'bundler/setup'
4
+ require 'rails'
5
+ require 'action_controller'
6
+
7
+ io = StringIO.new # ensure we are logging to something
8
+ logger = Logger.new(io)
9
+ logger.level = Logger::DEBUG
10
+ Rails.logger = logger
11
+
12
+ #
13
+ # Setup
14
+ #
15
+
16
+ # Setup the rails app to test
17
+ class RailsApp < Rails::Application
18
+ if ::Rails.version =~ /^3\./
19
+ config.secret_token = '1e05af2b349457936a41427e63450937'
20
+ else
21
+ config.secret_key_base = '1e05af2b349457936a41427e63450937'
22
+ end
23
+
24
+ # This ensures our tests fail, otherwise exceptions get swallowed by ActionDispatch::DebugExceptions
25
+ config.action_dispatch.show_exceptions = false
26
+ config.active_support.deprecation = :stderr
27
+ config.eager_load = false
28
+ end
29
+
30
+ # Create a controller to test against
31
+ class HomeController < ActionController::Base
32
+ layout nil
33
+
34
+ def index
35
+ render json: {}
36
+ end
37
+
38
+ def method_for_action(action_name)
39
+ action_name
40
+ end
41
+ end
42
+
43
+ # Define the routes
44
+ ::RailsApp.routes.draw do
45
+ get '/' => 'home#index'
46
+ end
47
+
48
+ # Initialize the app
49
+ RailsApp.initialize!
50
+
51
+ # Helper function to issue requests.
52
+ def dispatch_rails_request(path, additional_env_options = {})
53
+ application = ::Rails.application
54
+ env = application.respond_to?(:env_config) ? application.env_config.clone : application.env_defaults.clone
55
+ env["rack.request.cookie_hash"] = {}.with_indifferent_access
56
+ env["REMOTE_ADDR"] = "123.456.789.10"
57
+ env["HTTP_X_REQUEST_ID"] = "unique-request-id-1234"
58
+ env["action_dispatch.request_id"] = env["HTTP_X_REQUEST_ID"]
59
+ env = env.merge(additional_env_options)
60
+ ::Rack::MockRequest.new(application).get(path, env)
61
+ end
62
+
63
+ # How many iterations to perform
64
+ iterations = 1_000
65
+
66
+ puts "############################################################"
67
+ puts ""
68
+ puts "Testing Without Timber (#{iterations} iterations)"
69
+ puts ""
70
+ puts "############################################################"
71
+ puts
72
+
73
+ # Use bmbm to mimimize initial GC differences.
74
+ puts "Timing via benchmark:"
75
+ puts ""
76
+
77
+ Benchmark.bmbm do |x|
78
+ x.report("Without Timber") { iterations.times { dispatch_rails_request("/") } }
79
+ end
80
+
81
+ puts "\n"
82
+ puts "Memory profiling via benchmark-memory:"
83
+ puts
84
+
85
+ Benchmark.memory do |x|
86
+ x.report("Without Timber") { iterations.times { dispatch_rails_request("/") } }
87
+ end
88
+
89
+ puts "\n\n\n\n"
90
+ puts "############################################################"
91
+ puts ""
92
+ puts "Testing With Timber (#{iterations} iterations)"
93
+ puts ""
94
+ puts "############################################################"
95
+ puts ""
96
+
97
+ # Integrate Timber (this is handled in our Railtie, but we cna't use that here since we already
98
+ # initialized the app.)
99
+ require 'timber'
100
+ io = StringIO.new # ensure we are logging to something
101
+ logger = Timber::Logger.new(io)
102
+ logger.level = Logger::DEBUG
103
+ Timber::Frameworks::Rails.set_logger(logger)
104
+ Timber::Frameworks::Rails.configure_middlewares(Rails.application.config.app_middleware)
105
+ Timber::Integrations.integrate!
106
+ Timber::Config.instance.append_metadata = false
107
+
108
+
109
+ puts "Timing via benchmark:"
110
+ puts ""
111
+
112
+ Benchmark.bmbm do |x|
113
+ x.report("With Timber") { iterations.times { dispatch_rails_request("/") } }
114
+ end
115
+
116
+ puts "\n"
117
+ puts "Memory profiling via benchmark-memory:"
118
+ puts ""
119
+
120
+ Benchmark.memory do |x|
121
+ x.report("With Timber") { iterations.times { dispatch_rails_request("/") } }
122
+ end
@@ -44,6 +44,13 @@ module Timber
44
44
  yield iteration
45
45
  end
46
46
 
47
+ case iteration
48
+ when 0
49
+ event!(:waiting_for_logs)
50
+ when 30
51
+ event!(:excessively_waiting_for_logs)
52
+ end
53
+
47
54
  sleep 0.5
48
55
 
49
56
  res = get!(HAS_LOGS_PATH)
@@ -29,10 +29,10 @@ module Timber
29
29
  puts Messages.application_details(app)
30
30
  puts ""
31
31
 
32
- case ask_yes_no("Are the above details correct?")
32
+ case ask_yes_no("Are the above details correct?", api)
33
33
  when :yes
34
34
  if app.heroku?
35
- update_environment_config("production", :stdout)
35
+ update_environment_config("production", :stdout, api)
36
36
 
37
37
  puts ""
38
38
  puts Messages.separator
@@ -40,7 +40,7 @@ module Timber
40
40
  puts Messages.heroku_install(app)
41
41
  puts ""
42
42
 
43
- ask_yes_no("Ready to proceed?")
43
+ ask_yes_no("Ready to proceed?", api)
44
44
  puts ""
45
45
 
46
46
  else
@@ -53,19 +53,19 @@ module Timber
53
53
  puts "2) Configuring in my app"
54
54
  puts ""
55
55
 
56
- case ask("Enter your choice: (1/2) ")
56
+ case ask("Enter your choice: (1/2) ", api)
57
57
  when "1"
58
- update_environment_config("production", :http, :api_key_code => "ENV['TIMBER_API_KEY']")
58
+ update_environment_config("production", :http, api, :api_key_code => "ENV['TIMBER_API_KEY']")
59
59
 
60
60
  puts ""
61
61
  puts Messages.http_environment_variables(app.api_key)
62
62
  puts ""
63
63
 
64
- ask_yes_no("Ready to proceed?")
64
+ ask_yes_no("Ready to proceed?", api)
65
65
  puts ""
66
66
 
67
67
  when "2"
68
- update_environment_config("production", :http, :api_key_code => "'#{app.api_key}'")
68
+ update_environment_config("production", :http, api, :api_key_code => "'#{app.api_key}'")
69
69
 
70
70
  end
71
71
 
@@ -107,7 +107,7 @@ module Timber
107
107
  end
108
108
 
109
109
  private
110
- def update_environment_config(name, log_device_type, options = {})
110
+ def update_environment_config(name, log_device_type, api, options = {})
111
111
  path = File.join("config", "environments", "#{name}.rb")
112
112
 
113
113
  puts ""
@@ -146,6 +146,7 @@ CODE
146
146
  if !current_contents.include?("Timber::Logger.new")
147
147
  new_contents = current_contents.sub(/\nend/, "\n\n#{logger_code}\nend")
148
148
  File.write(path, new_contents)
149
+ api.event!(:file_written, path: path)
149
150
  end
150
151
 
151
152
  puts colorize(Messages.task_complete(task_message), :green)
@@ -156,7 +157,9 @@ CODE
156
157
 
157
158
  http_device = LogDevices::HTTP.new(api_key)
158
159
  logger = Logger.new(http_device)
159
- logger.info("test")
160
+ logger.info("Welcome to Timber!")
161
+ logger.info("This is a test log to ensure the pipes are working")
162
+ logger.info("Be sure to commit and deploy your app to start seeing real logs")
160
163
 
161
164
  puts colorize(Messages.task_complete("Sending test logs"), :green)
162
165
  end
@@ -165,7 +168,9 @@ CODE
165
168
  puts ""
166
169
  puts Messages.separator
167
170
  puts ""
168
- rating = ask("How would rate this install experience? 1 (bad) - 5 (perfect)")
171
+
172
+ rating = ask("How would rate this install experience? 1 (bad) - 5 (perfect)", api)
173
+
169
174
  case rating
170
175
  when "4", "5"
171
176
  api.event!(:feedback, rating: rating.to_i)
@@ -177,7 +182,7 @@ CODE
177
182
  puts Messages.bad_experience_message
178
183
  puts ""
179
184
 
180
- comments = ask("Type your comments (enter sends)")
185
+ comments = ask("Type your comments (enter sends)", api)
181
186
 
182
187
  api.event!(:feedback, rating: rating.to_i, comments: comments)
183
188
 
@@ -1,20 +1,26 @@
1
1
  module Timber
2
2
  class CLI
3
3
  module IOHelper
4
- def ask(message)
4
+ def ask(message, api)
5
+ api.event!(:waiting_for_input, prompt: message)
6
+
5
7
  write message + " "
6
- gets
8
+ input = gets
9
+
10
+ api.event!(:received_input, prompt: message, value: input)
11
+
12
+ input
7
13
  end
8
14
 
9
- def ask_yes_no(message)
10
- case ask(message + " (y/n)")
15
+ def ask_yes_no(message, api)
16
+ case ask(message + " (y/n)", api)
11
17
  when "y", "Y"
12
18
  :yes
13
19
  when "n", "N"
14
20
  :no
15
21
  else
16
22
  puts "Woops! That's not a valid input. Please try again."
17
- ask_yes_no(message)
23
+ ask_yes_no(message, api)
18
24
  end
19
25
  end
20
26
 
@@ -45,8 +45,7 @@ message.rstrip
45
45
  message = <<-MESSAGE
46
46
  Last step! Commit and deploy:
47
47
 
48
- #{colorize("git add config/initializers/timber.rb", :blue)}
49
- #{colorize("git commit -am 'Install Timber'", :blue)}
48
+ #{colorize("git commit -m 'Install Timber' config/environments/production.rb", :blue)}
50
49
 
51
50
  #{colorize("push and deploy", :blue)} 🚀
52
51
  MESSAGE
@@ -3,8 +3,7 @@ module Timber
3
3
  # Custom contexts allow you to add application specific context not covered elsewhere.
4
4
  #
5
5
  # @example Adding a context
6
- # custom_context = Timber::Contexts::Custom.new(type: :keyspace, data: %{my: "data"})
7
- # Timber::CurrentContext.with(custom_context) do
6
+ # logger.with_context(build: {version: "1.0.0"}) do
8
7
  # # ... anything logged here will have the context ...
9
8
  # end
10
9
  class Custom < Context
@@ -10,7 +10,7 @@ module Timber
10
10
  # Example:
11
11
  #
12
12
  # organization_context = Timber::Contexts::Organization.new(id: "abc1234", name: "Timber Inc")
13
- # Timber::CurrentContext.with(organization_context) do
13
+ # logger.with_context(organization_context) do
14
14
  # # Logging will automatically include this context
15
15
  # logger.info("This is a log message")
16
16
  # end
@@ -13,7 +13,7 @@ module Timber
13
13
  @headers = Util::HTTPEvent.normalize_headers(attributes[:headers])
14
14
  @host = attributes[:host] || raise(ArgumentError.new(":host is required"))
15
15
  @method = Util::HTTPEvent.normalize_method(attributes[:method]) || raise(ArgumentError.new(":method is required"))
16
- @path = attributes[:path] || raise(ArgumentError.new(":path is required"))
16
+ @path = attributes[:path]
17
17
  @port = attributes[:port]
18
18
  @query_string = Util::HTTPEvent.normalize_query_string(attributes[:query_string])
19
19
  @request_id = attributes[:request_id]
@@ -13,7 +13,7 @@ module Timber
13
13
  @headers = Util::HTTPEvent.normalize_headers(attributes[:headers])
14
14
  @host = attributes[:host] || raise(ArgumentError.new(":host is required"))
15
15
  @method = Util::HTTPEvent.normalize_method(attributes[:method]) || raise(ArgumentError.new(":method is required"))
16
- @path = attributes[:path] || raise(ArgumentError.new(":path is required"))
16
+ @path = attributes[:path]
17
17
  @port = attributes[:port]
18
18
  @query_string = Util::HTTPEvent.normalize_query_string(attributes[:query_string])
19
19
  @scheme = attributes[:scheme] || raise(ArgumentError.new(":scheme is required"))
data/lib/timber/logger.rb CHANGED
@@ -212,6 +212,12 @@ module Timber
212
212
  super
213
213
  end
214
214
 
215
+ # Convenience method for adding context. Please see {{Timber::CurrentContext.with}} for
216
+ # a more detailed description and examples.
217
+ def with_context(context, &block)
218
+ Timber::CurrentContext.with(context, &block)
219
+ end
220
+
215
221
  # Backwards compatibility with older ActiveSupport::Logger versions
216
222
  Logger::Severity.constants.each do |severity|
217
223
  class_eval(<<-EOT, __FILE__, __LINE__ + 1)
@@ -34,7 +34,10 @@ module Timber
34
34
  if Config.instance.header_filters && Config.instance.header_filters.include?(k)
35
35
  h[k] = SANITIZED_VALUE
36
36
  else
37
- h[k] = v
37
+ # Force the header into a valid UTF-8 string, otherwise we will encounter
38
+ # encoding issues when we convert this data to json. Moreoever, if the
39
+ # data is already valid UTF-8 we don't pay a penalty.
40
+ h[k] = Timber::Util::String.normalize_to_utf8(v)
38
41
  end
39
42
  end
40
43
  end
@@ -0,0 +1,21 @@
1
+ module Timber
2
+ module Util
3
+ # @private
4
+ module String
5
+ UTF8 = "UTF-8".freeze
6
+
7
+ # @private
8
+ def self.normalize_to_utf8(string)
9
+ if string.encoding.to_s == UTF8
10
+ string
11
+ else
12
+ string.encode('UTF-8', {
13
+ :invalid => :replace,
14
+ :undef => :replace,
15
+ :replace => '?'
16
+ })
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
data/lib/timber/util.rb CHANGED
@@ -3,6 +3,7 @@ require "timber/util/hash"
3
3
  require "timber/util/http_event"
4
4
  require "timber/util/object"
5
5
  require "timber/util/request"
6
+ require "timber/util/string"
6
7
  require "timber/util/struct"
7
8
 
8
9
  module Timber
@@ -1,3 +1,3 @@
1
1
  module Timber
2
- VERSION = "2.0.7"
2
+ VERSION = "2.0.8"
3
3
  end
@@ -0,0 +1,27 @@
1
+ # encoding: UTF-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe Timber::Events::HTTPServerRequest, :rails_23 => true do
6
+ describe ".initialize" do
7
+ context "with a header filters" do
8
+ around(:each) do |example|
9
+ old_header_filters = Timber::Config.instance.header_filters
10
+ Timber::Config.instance.header_filters += ['api-key']
11
+ example.run
12
+ Timber::Config.instance.header_filters = old_header_filters
13
+ end
14
+
15
+ it "should sanitize headers when a config option is set" do
16
+ event = described_class.new(:headers => {'Api-Key' => 'abcde'}, :host => 'my.host.com', :method => 'GET', :path => '/path', :scheme => 'https')
17
+ expect(event.headers).to eq({'api-key' => '[sanitized]'})
18
+ end
19
+ end
20
+
21
+ it "should handle header encoding" do
22
+ referer = 'http://www.metrojobb.se/jobb/1013893-skadeadministratör'.force_encoding('ASCII-8BIT')
23
+ event = described_class.new(:headers => {'Referer' => referer}, :host => 'my.host.com', :method => 'GET', :path => '/path', :scheme => 'https')
24
+ expect(event.headers["referer"].encoding.to_s).to eq("UTF-8")
25
+ end
26
+ end
27
+ end
@@ -1,9 +1,9 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Timber::LogEntry, :rails_23 => true do
4
- describe "#to_msgpack" do
5
- let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
4
+ let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
6
5
 
6
+ describe "#to_msgpack" do
7
7
  it "should encode properly with an event and context" do
8
8
  event = Timber::Events::Custom.new(type: :event_type, message: "event_message", data: {a: 1})
9
9
  context = {custom: Timber::Contexts::Custom.new(type: :context_type, data: {b: 1})}
@@ -133,6 +133,27 @@ describe Timber::Logger, :rails_23 => true do
133
133
  end
134
134
  end
135
135
 
136
+ describe "#with_context" do
137
+ let(:io) { StringIO.new }
138
+ let(:logger) { Timber::Logger.new(io) }
139
+
140
+ it "should add context" do
141
+ expect(Timber::CurrentContext.hash).to eq({})
142
+
143
+ logger.with_context(build: {version: "1.0.0"}) do
144
+ expect(Timber::CurrentContext.hash).to eq({:custom=>{:build=>{:version=>"1.0.0"}}})
145
+
146
+ logger.with_context({testing: {key: "value"}}) do
147
+ expect(Timber::CurrentContext.hash).to eq({:custom=>{:build=>{:version=>"1.0.0"}, :testing=>{:key=>"value"}}})
148
+ end
149
+
150
+ expect(Timber::CurrentContext.hash).to eq({:custom=>{:build=>{:version=>"1.0.0"}}})
151
+ end
152
+
153
+ expect(Timber::CurrentContext.hash).to eq({})
154
+ end
155
+ end
156
+
136
157
  describe "#info" do
137
158
  let(:io) { StringIO.new }
138
159
  let(:logger) { Timber::Logger.new(io) }
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.0.7
4
+ version: 2.0.8
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-03-29 00:00:00.000000000 Z
11
+ date: 2017-04-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -137,6 +137,7 @@ files:
137
137
  - Gemfile
138
138
  - LICENSE.md
139
139
  - README.md
140
+ - benchmarks/rails.rb
140
141
  - bin/timber
141
142
  - gemfiles/rails-3.0.gemfile
142
143
  - gemfiles/rails-3.1.gemfile
@@ -208,6 +209,7 @@ files:
208
209
  - lib/timber/util/http_event.rb
209
210
  - lib/timber/util/object.rb
210
211
  - lib/timber/util/request.rb
212
+ - lib/timber/util/string.rb
211
213
  - lib/timber/util/struct.rb
212
214
  - lib/timber/version.rb
213
215
  - spec/README.md
@@ -229,6 +231,7 @@ files:
229
231
  - spec/timber/current_context_spec.rb
230
232
  - spec/timber/event_spec.rb
231
233
  - spec/timber/events/custom_spec.rb
234
+ - spec/timber/events/http_server_request_spec.rb
232
235
  - spec/timber/events_spec.rb
233
236
  - spec/timber/integrations/action_controller/log_subscriber_spec.rb
234
237
  - spec/timber/integrations/action_dispatch/debug_exceptions_spec.rb
@@ -283,6 +286,7 @@ test_files:
283
286
  - spec/timber/current_context_spec.rb
284
287
  - spec/timber/event_spec.rb
285
288
  - spec/timber/events/custom_spec.rb
289
+ - spec/timber/events/http_server_request_spec.rb
286
290
  - spec/timber/events_spec.rb
287
291
  - spec/timber/integrations/action_controller/log_subscriber_spec.rb
288
292
  - spec/timber/integrations/action_dispatch/debug_exceptions_spec.rb