timber 2.0.20 → 2.0.21

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: 946dc64576210d225bbf4952fa78363705b3b87f
4
- data.tar.gz: e972b58acf8efc60ebcc79746939d7bbceeadd06
3
+ metadata.gz: f65e44d5582af9d177dabd02f2229adda5ea094b
4
+ data.tar.gz: a113bb27776a6403b3b237b49d33c7fbac962758
5
5
  SHA512:
6
- metadata.gz: f44fdf7da119df5a80cd1cb589f1c55d8fee39bfccd6cacf35eaf9b5944afdc227cb6d715473c651d3830035915bc62df11cae4c7c58a779331870fa23659d26
7
- data.tar.gz: 7cad85ce7ea3c9df75fd2d3fc7e18df07d42d20f294bba6af8803dce710f7b9a7e0a8990ef243d3feff942807625ffb8b40bc2ecde89c98e2604a7e129c94533
6
+ metadata.gz: 10b371f99e2c4f4203526917a770140095581e17a12b72fba44dfc8db8b2151a198b5e771527fb715f6c9c4fe9e0d1c56772ff6663fb31c07f76bd95210339d4
7
+ data.tar.gz: e0b9fd2b91d7de66b4bee5305337afeb5017ec42fff2c76e8374c957af5d88445701aa029fca846f2f7e7778e4ffa7231989ea6c05d3814f307e244e5b2607d0
data/.travis.yml CHANGED
@@ -22,7 +22,7 @@ env:
22
22
  before_script:
23
23
  - echo $BUNDLE_GEMFILE
24
24
  - bundle install
25
- script: bundle exec rspec
25
+ script: bundle exec rspec --format documentation
26
26
  matrix:
27
27
  fast_finish: true
28
28
  exclude:
data/lib/timber/config.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require "logger"
1
2
  require "singleton"
2
3
 
3
4
  module Timber
@@ -13,6 +14,13 @@ module Timber
13
14
  class Config
14
15
  class NoLoggerError < StandardError; end
15
16
 
17
+ class SimpleFormatter < ::Logger::Formatter
18
+ # This method is invoked when a log event occurs
19
+ def call(severity, timestamp, progname, msg)
20
+ "[Timber] #{String === msg ? msg : msg.inspect}\n"
21
+ end
22
+ end
23
+
16
24
  PRODUCTION_NAME = "production".freeze
17
25
  STAGING_NAME = "staging".freeze
18
26
 
@@ -54,6 +62,7 @@ module Timber
54
62
  file.binmode
55
63
  file.sync = config.autoflush_log
56
64
  file_logger = ::Logger.new(file)
65
+ file_logger.formatter = SimpleFormatter.new
57
66
  self.debug_logger = file_logger
58
67
  end
59
68
 
@@ -65,6 +74,7 @@ module Timber
65
74
  # Timber::Config.instance.debug_to_file("log/timber.log")
66
75
  def debug_to_stdout
67
76
  stdout_logger = ::Logger.new(STDOUT)
77
+ stdout_logger.formatter = SimpleFormatter.new
68
78
  self.debug_logger = stdout_logger
69
79
  end
70
80
 
@@ -133,7 +143,11 @@ module Timber
133
143
  # @example Everything else
134
144
  # Timber::Config.instance.logger = Timber::Logger.new(STDOUT)
135
145
  def logger
136
- @logger || Logger.new(STDOUT)
146
+ if @logger.is_a?(Proc)
147
+ @logger.call()
148
+ else
149
+ @logger ||= Logger.new(STDOUT)
150
+ end
137
151
  end
138
152
 
139
153
  private
@@ -3,12 +3,12 @@ require "logger"
3
3
  # Attempt to require Rails. We can not list it as a gem
4
4
  # dependency because we want to support multiple frameworks.
5
5
  begin
6
- require("rails")
6
+ require "rails"
7
7
  rescue LoadError
8
8
  end
9
9
 
10
10
  if defined?(::Rails) && defined?(::Rails::Railtie)
11
- require 'timber/frameworks/rails'
11
+ require "timber/frameworks/rails"
12
12
  end
13
13
 
14
14
  module Timber
@@ -48,14 +48,17 @@ module Timber
48
48
  class Railtie < ::Rails::Railtie
49
49
  config.timber = Config.instance
50
50
 
51
- # Initialize Timber immediately after the logger in case anything uses the logger
52
- # during the initialization process.
53
- initializer(:timber, after: :initialize_logger) do
54
- logger = Rails.ensure_timber_logger(::Rails.logger)
55
- Rails.set_logger(logger)
51
+ config.before_initialize do
52
+ Timber::Config.instance.logger = Proc.new { ::Rails.logger }
53
+ end
56
54
 
55
+ # Must be loaded after initializers so that we respect any Timber configuration
56
+ # set
57
+ initializer(:timber, after: :load_config_initializers) do
57
58
  Integrations.integrate!
58
59
 
60
+ # Install the Rack middlewares so that we capture structured data instead of
61
+ # raw text logs.
59
62
  timber_operations = Integrations::Rack.middlewares.collect do |middleware_class|
60
63
  [:use, [middleware_class], nil]
61
64
  end
@@ -63,43 +66,6 @@ module Timber
63
66
  config.app_middleware.timber_operations = timber_operations
64
67
  end
65
68
  end
66
-
67
- # This builds a new Timber::Logger from an existing logger. This allows us to transparentl
68
- # switch users onto the Timber::Logger since we support a more useful logging API.
69
- def self.ensure_timber_logger(existing_logger)
70
- if existing_logger.is_a?(Logger)
71
- return existing_logger
72
- end
73
-
74
- log_device = existing_logger.instance_variable_get(:@logdev).try(:dev)
75
- logger = Logger.new(log_device)
76
- logger.level = existing_logger.try(:level) || Logger::DEBUG
77
- if defined?(::ActiveSupport::TaggedLogging)
78
- logger = ::ActiveSupport::TaggedLogging.new(logger)
79
- end
80
- logger
81
- end
82
-
83
- # Sets the Rails logger. Rails
84
- def self.set_logger(logger)
85
- if defined?(::ActiveSupport::TaggedLogging) && !logger.is_a?(::ActiveSupport::TaggedLogging)
86
- logger = ::ActiveSupport::TaggedLogging.new(logger)
87
- end
88
-
89
- Config.instance.logger = logger
90
-
91
- # Set the various Rails framework loggers. We *have* to do this because Rails
92
- # internally sets these with an ActiveSupport.onload(:active_record) { } callback.
93
- # We don't have an opportunity to intercept this since the :initialize_logger
94
- # initializer loads these modules. Moreover, earlier version of rails don't do this
95
- # at all, hence the defined? checks. Yay for being implicit.
96
- ::ActionCable::Server::Base.logger = logger if defined?(::ActionCable::Server::Base) && ::ActionCable::Server::Base.respond_to?(:logger=)
97
- ::ActionController::Base.logger = logger if defined?(::ActionController::Base) && ::ActionController::Base.respond_to?(:logger=)
98
- ::ActionMailer::Base.logger = logger if defined?(::ActionMailer::Base) && ::ActionMailer::Base.respond_to?(:logger=)
99
- ::ActionView::Base.logger = logger if defined?(::ActionView::Base) && ::ActionView::Base.respond_to?(:logger=)
100
- ::ActiveRecord::Base.logger = logger if defined?(::ActiveRecord::Base) && ::ActiveRecord::Base.respond_to?(:logger=)
101
- ::Rails.logger = logger
102
- end
103
69
  end
104
70
  end
105
71
  end
@@ -1,6 +1,6 @@
1
- # We require all of ActiveRecord because #logger usses ActiveRecord::Base.
2
- # We can't require active_record/base directly because ActiveRecord does not require
3
- # files properly.
1
+ # We require all of ActiveRecord because the #logger method in ::ActiveRecord::LogSubscriber
2
+ # uses ActiveRecord::Base. We can't require active_record/base directly because ActiveRecord
3
+ # does not require files properly and we receive unintialized constant errors.
4
4
  require "active_record"
5
5
  require "active_record/log_subscriber"
6
6
 
@@ -6,7 +6,18 @@ module Timber
6
6
  class RequirementNotMetError < StandardError; end
7
7
 
8
8
  class << self
9
+ attr_writer :enabled
10
+
11
+ def enabled?
12
+ @enabled != false
13
+ end
14
+
9
15
  def integrate!(*args)
16
+ if !enabled?
17
+ Config.instance.debug_logger.debug("#{name} integration disabled, skipping") if Config.instance.debug_logger
18
+ return false
19
+ end
20
+
10
21
  new(*args).integrate!
11
22
  Config.instance.debug_logger.debug("Integrated #{name}") if Config.instance.debug_logger
12
23
  true
@@ -5,7 +5,8 @@ module Timber
5
5
  module LogDevices
6
6
  # A highly efficient log device that buffers and delivers log messages over HTTPS to
7
7
  # the Timber API. It uses batches, keep-alive connections, and msgpack to deliver logs with
8
- # high-throughput and little overhead.
8
+ # high-throughput and little overhead. All log preparation and delivery is done asynchronously
9
+ # in a thread as not to block application execution.
9
10
  #
10
11
  # See {#initialize} for options and more details.
11
12
  class HTTP
@@ -118,6 +119,7 @@ module Timber
118
119
  @requests_per_conn = options[:requests_per_conn] || 2_500
119
120
  @msg_queue = LogMsgQueue.new(@batch_size)
120
121
  @request_queue = options[:request_queue] || SizedQueue.new(3)
122
+ @successive_error_count = 0
121
123
  @requests_in_flight = 0
122
124
  end
123
125
 
@@ -133,7 +135,7 @@ module Timber
133
135
  ensure_flush_threads_are_started
134
136
 
135
137
  if @msg_queue.full?
136
- debug_logger.debug("Flushing timber buffer via write") if debug_logger
138
+ debug_logger.debug("Flushing HTTP buffer via write") if debug_logger
137
139
  flush
138
140
  end
139
141
  true
@@ -154,9 +156,26 @@ module Timber
154
156
 
155
157
  # Closes the log device, cleans up, and attempts one last delivery.
156
158
  def close
159
+ # Kill the flush thread immediately since we are about to flush again.
157
160
  @flush_thread.kill if @flush_thread
158
- @outlet_thread.kill if @outlet_thread
161
+
162
+ # Flush all remaining messages
159
163
  flush
164
+
165
+ # Kill the request_outlet thread gracefully. We do not want to kill it while a
166
+ # request is inflight. Ideally we'd let it finish before we die.
167
+ if @request_outlet_thread
168
+ 4.times do
169
+ if @requests_in_flight == 0 && @request_queue.size == 0
170
+ @request_outlet_thread.kill
171
+ break
172
+ else
173
+ debug_logger.error("Busy delivering the final log messages, " +
174
+ "connection will close when complete.")
175
+ sleep 1
176
+ end
177
+ end
178
+ end
160
179
  end
161
180
 
162
181
  private
@@ -170,8 +189,8 @@ module Timber
170
189
  # threads are started after process forking.
171
190
  def ensure_flush_threads_are_started
172
191
  if @flush_continuously
173
- if @outlet_thread.nil? || !@outlet_thread.alive?
174
- @outlet_thread = Thread.new { outlet }
192
+ if @request_outlet_thread.nil? || !@request_outlet_thread.alive?
193
+ @request_outlet_thread = Thread.new { request_outlet }
175
194
  end
176
195
 
177
196
  if @flush_thread.nil? || !@flush_thread.alive?
@@ -183,15 +202,17 @@ module Timber
183
202
  def intervaled_flush
184
203
  # Wait specified time period before starting
185
204
  sleep @flush_interval
205
+
186
206
  loop do
187
207
  begin
188
208
  if intervaled_flush_ready?
189
- debug_logger.debug("Flushing timber buffer via the interval") if debug_logger
209
+ debug_logger.debug("Flushing HTTP buffer via the interval") if debug_logger
190
210
  flush
191
211
  end
192
- sleep(0.1)
212
+
213
+ sleep(0.5)
193
214
  rescue Exception => e
194
- logger.error("Timber intervaled flush failed: #{e.inspect}\n\n#{e.backtrace}")
215
+ logger.error("Intervaled HTTP flush failed: #{e.inspect}\n\n#{e.backtrace}")
195
216
  end
196
217
  end
197
218
  end
@@ -200,50 +221,73 @@ module Timber
200
221
  @last_flush.nil? || (Time.now.to_f - @last_flush.to_f).abs >= @flush_interval
201
222
  end
202
223
 
203
- def outlet
224
+ def build_http
225
+ http = Net::HTTP.new(@timber_url.host, @timber_url.port)
226
+ http.set_debug_output(debug_logger) if debug_logger
227
+ http.use_ssl = true if @timber_url.scheme == 'https'
228
+ http.read_timeout = 30
229
+ http.ssl_timeout = 10
230
+ http.open_timeout = 10
231
+ http
232
+ end
233
+
234
+ def request_outlet
204
235
  loop do
205
- http = Net::HTTP.new(@timber_url.host, @timber_url.port)
206
- http.set_debug_output(debug_logger) if debug_logger
207
- http.use_ssl = true if @timber_url.scheme == 'https'
208
- http.read_timeout = 30
209
- http.ssl_timeout = 10
210
- http.open_timeout = 10
236
+ http = build_http
211
237
 
212
238
  begin
213
- debug_logger.info("Starting Timber HTTP connection") if debug_logger
239
+ debug_logger.info("Starting HTTP connection") if debug_logger
240
+
214
241
  http.start do |conn|
215
- num_reqs = 0
216
- while num_reqs < @requests_per_conn
217
- if debug_logger
218
- debug_logger.debug("Waiting on next Timber request")
219
- debug_logger.debug("Number of threads waiting on Timber request queue: #{@request_queue.num_waiting}")
220
- end
221
-
222
- # Blocks waiting for a request.
223
- req = @request_queue.deq
224
- @requests_in_flight += 1
225
- resp = nil
226
- begin
227
- resp = conn.request(req)
228
- rescue => e
229
- debug_logger.error("Timber request error: #{e.message}") if debug_logger
230
- next
231
- ensure
232
- @requests_in_flight -= 1
233
- end
234
- num_reqs += 1
235
- debug_logger.debug("Timber request successful: #{resp.code}") if debug_logger
236
- end
242
+ deliver_requests(conn)
237
243
  end
238
244
  rescue => e
239
- debug_logger.error("Timber request error: #{e.message}") if debug_logger
245
+ debug_logger.error("#request_outlet error: #{e.message}") if debug_logger
240
246
  ensure
241
- debug_logger.debug("Finishing Timber HTTP connection") if debug_logger
247
+ debug_logger.info("Finishing HTTP connection") if debug_logger
242
248
  http.finish if http.started?
243
249
  end
244
250
  end
245
251
  end
246
252
 
253
+ def deliver_requests(conn)
254
+ num_reqs = 0
255
+
256
+ while num_reqs < @requests_per_conn
257
+ debug_logger.info("Waiting on next request, threads waiting: #{@request_queue.num_waiting}") if debug_logger
258
+
259
+ # Blocks waiting for a request.
260
+ req = @request_queue.deq
261
+ @requests_in_flight += 1
262
+
263
+ begin
264
+ resp = conn.request(req)
265
+ rescue => e
266
+ debug_logger.error("#deliver_request error: #{e.message}") if debug_logger
267
+
268
+ @successive_error_count += 1
269
+
270
+ # Back off so that we don't hammer the Timber API.
271
+ calculated_backoff = @successive_error_count * 2
272
+ backoff = calculated_backoff > 30 ? 30 : calculated_backoff
273
+
274
+ debug_logger.error("Backing off #{backoff} seconds, error ##{@successive_error_count}") if debug_logger
275
+
276
+ sleep backoff
277
+
278
+ # Throw the request back on the queue for a retry
279
+ @request_queue.enq(req)
280
+ return false
281
+ ensure
282
+ @requests_in_flight -= 1
283
+ end
284
+
285
+ @successive_error_count = 0
286
+ num_reqs += 1
287
+ debug_logger.info("Request successful: #{resp.code}") if debug_logger
288
+ end
289
+ end
290
+
247
291
  def authorization_payload
248
292
  @authorization_payload ||= "Basic #{Base64.urlsafe_encode64(@api_key).chomp}"
249
293
  end
@@ -71,6 +71,10 @@ module Timber
71
71
  Util::Hash.deep_compact(hash)
72
72
  end
73
73
 
74
+ def inspect
75
+ to_s
76
+ end
77
+
74
78
  def to_json(options = {})
75
79
  as_json(options).to_json
76
80
  end
@@ -79,6 +83,26 @@ module Timber
79
83
  as_json.to_msgpack(*args)
80
84
  end
81
85
 
86
+ # This is used when LogEntry objects make it to a non-Timber logger.
87
+ def to_s
88
+ log_message = message
89
+
90
+ if !event.nil?
91
+ event_hash = event.as_json
92
+ event_type = event_hash.keys.first
93
+
94
+ event_type = if event.is_a?(Events::Custom)
95
+ "event:#{event_type}.#{event.type}"
96
+ else
97
+ "event:#{event_type}"
98
+ end
99
+
100
+ log_message = "#{message} [#{event_type}]"
101
+ end
102
+
103
+ log_message + "\n"
104
+ end
105
+
82
106
  private
83
107
  def formatted_dt
84
108
  @formatted_dt ||= time.iso8601(DT_PRECISION)
data/lib/timber/logger.rb CHANGED
@@ -115,7 +115,8 @@ module Timber
115
115
 
116
116
  # This method is invoked when a log event occurs
117
117
  def call(severity, timestamp, progname, msg)
118
- "#{String === msg ? msg : msg.inspect}\n"
118
+ log_entry = build_log_entry(severity, timestamp, progname, msg)
119
+ log_entry.to_s
119
120
  end
120
121
  end
121
122
 
@@ -197,7 +198,7 @@ module Timber
197
198
 
198
199
  self.level = environment_level
199
200
 
200
- after_initialize if respond_to? :after_initialize
201
+ after_initialize if respond_to?(:after_initialize)
201
202
 
202
203
  @initialized = true
203
204
  end
@@ -1,3 +1,3 @@
1
1
  module Timber
2
- VERSION = "2.0.20"
2
+ VERSION = "2.0.21"
3
3
  end
@@ -46,7 +46,12 @@ if defined?(::Rails)
46
46
  #
47
47
  # You can see here that they use simple class attribute, hence the reason we need
48
48
  # to update all of them: https://github.com/rails/rails/blob/700ec897f97c60016ad748236bf3a49ef15a20de/actionview/lib/action_view/base.rb#L157
49
- Timber::Frameworks::Rails.set_logger(logger)
49
+ ::ActionCable::Server::Base.logger = logger if defined?(::ActionCable::Server::Base) && ::ActionCable::Server::Base.respond_to?(:logger=)
50
+ ::ActionController::Base.logger = logger if defined?(::ActionController::Base) && ::ActionController::Base.respond_to?(:logger=)
51
+ ::ActionMailer::Base.logger = logger if defined?(::ActionMailer::Base) && ::ActionMailer::Base.respond_to?(:logger=)
52
+ ::ActionView::Base.logger = logger if defined?(::ActionView::Base) && ::ActionView::Base.respond_to?(:logger=)
53
+ ::ActiveRecord::Base.logger = logger if defined?(::ActiveRecord::Base) && ::ActiveRecord::Base.respond_to?(:logger=)
54
+ ::Rails.logger = logger
50
55
 
51
56
  yield
52
57
 
@@ -1,5 +1,4 @@
1
1
  require "timber"
2
2
 
3
3
  config = Timber::Config.instance
4
- config.append_metadata = true
5
- config.debug_logger = ::Logger.new(STDOUT)
4
+ config.append_metadata = true
@@ -11,7 +11,7 @@ describe Timber::LogDevices::HTTP do
11
11
  # Ensure that threads have not started
12
12
  thread = http.instance_variable_get(:@flush_thread)
13
13
  expect(thread).to be_nil
14
- thread = http.instance_variable_get(:@outlet_thread)
14
+ thread = http.instance_variable_get(:@request_outlet_thread)
15
15
  expect(thread).to be_nil
16
16
  end
17
17
  end
@@ -23,6 +23,7 @@ describe Timber::LogDevices::HTTP do
23
23
  it "should buffer the messages" do
24
24
  http.write("test log message")
25
25
  expect(msg_queue.flush).to eq(["test log message"])
26
+ http.close
26
27
  end
27
28
 
28
29
  it "should start the flush threads" do
@@ -30,8 +31,10 @@ describe Timber::LogDevices::HTTP do
30
31
 
31
32
  thread = http.instance_variable_get(:@flush_thread)
32
33
  expect(thread).to be_alive
33
- thread = http.instance_variable_get(:@outlet_thread)
34
+ thread = http.instance_variable_get(:@request_outlet_thread)
34
35
  expect(thread).to be_alive
36
+ expect(http).to receive(:flush).exactly(1).times
37
+ http.close
35
38
  end
36
39
 
37
40
  context "with a low batch size" do
@@ -41,6 +44,8 @@ describe Timber::LogDevices::HTTP do
41
44
  http.write("test")
42
45
  expect(http).to receive(:flush).exactly(1).times
43
46
  http.write("my log message")
47
+ expect(http).to receive(:flush).exactly(1).times
48
+ http.close
44
49
  end
45
50
  end
46
51
  end
@@ -54,7 +59,7 @@ describe Timber::LogDevices::HTTP do
54
59
  thread = http.instance_variable_get(:@flush_thread)
55
60
  sleep 0.1 # too fast!
56
61
  expect(thread).to_not be_alive
57
- thread = http.instance_variable_get(:@outlet_thread)
62
+ thread = http.instance_variable_get(:@request_outlet_thread)
58
63
  sleep 0.1 # too fast!
59
64
  expect(thread).to_not be_alive
60
65
  end
@@ -86,13 +91,6 @@ describe Timber::LogDevices::HTTP do
86
91
  message_queue = http.instance_variable_get(:@msg_queue)
87
92
  expect(message_queue.size).to eq(0)
88
93
  end
89
-
90
- it "should preserve formatting for mshpack payloads" do
91
- http = described_class.new("MYKEY", flush_continuously: false)
92
- http.write("This is a log message 1".to_msgpack)
93
- http.write("This is a log message 2".to_msgpack)
94
- http.send(:flush)
95
- end
96
94
  end
97
95
 
98
96
  # Testing a private method because it helps break down our tests
@@ -100,18 +98,17 @@ describe Timber::LogDevices::HTTP do
100
98
  it "should start a intervaled flush thread and flush on an interval" do
101
99
  http = described_class.new("MYKEY", flush_interval: 0.1)
102
100
  http.send(:ensure_flush_threads_are_started)
103
- expect(http).to receive(:flush).exactly(1).times
104
- sleep 0.12 # too fast!
105
- expect(http).to receive(:flush).exactly(1).times
106
- sleep 0.12 # too fast!
101
+ expect(http).to receive(:flush).exactly(2).times
102
+ sleep 0.15 # too fast!
103
+ http.close
107
104
  end
108
105
  end
109
106
 
110
107
  # Outlet
111
- describe "#outlet" do
108
+ describe "#request_outlet" do
112
109
  let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
113
110
 
114
- it "should start a intervaled flush thread and flush on an interval" do
111
+ it "should deliver requests on an interval" do
115
112
  stub = stub_request(:post, "https://logs.timber.io/frames").
116
113
  with(
117
114
  :body => start_with("\x92\x85\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1\xA7context\x81\xA6system".force_encoding("ASCII-8BIT")),
@@ -124,13 +121,37 @@ describe Timber::LogDevices::HTTP do
124
121
  to_return(:status => 200, :body => "", :headers => {})
125
122
 
126
123
  http = described_class.new("MYKEY", flush_interval: 0.1)
127
- log_entry = Timber::LogEntry.new("INFO", time, nil, "test log message 1", nil, nil)
128
- http.write(log_entry)
129
- log_entry = Timber::LogEntry.new("INFO", time, nil, "test log message 2", nil, nil)
130
- http.write(log_entry)
131
- sleep 0.3
124
+ log_entry1 = Timber::LogEntry.new("INFO", time, nil, "test log message 1", nil, nil)
125
+ http.write(log_entry1)
126
+ log_entry2 = Timber::LogEntry.new("INFO", time, nil, "test log message 2", nil, nil)
127
+ http.write(log_entry2)
128
+ sleep 1
132
129
 
133
130
  expect(stub).to have_been_requested.times(1)
131
+
132
+ http.close
133
+ end
134
+ end
135
+
136
+ describe "#deliver_requests" do
137
+ it "should handle exceptions properly and return" do
138
+ allow_any_instance_of(Net::HTTP).to receive(:request).and_raise("boom")
139
+
140
+ http_device = described_class.new("MYKEY", flush_continuously: false)
141
+ req_queue = http_device.instance_variable_get(:@request_queue)
142
+
143
+ # Place a request on the queue
144
+ req = Net::HTTP::Post.new("/")
145
+ req_queue.enq(req)
146
+
147
+ # Start a HTTP connection to test the method directly
148
+ http = http_device.send(:build_http)
149
+ http.start do |conn|
150
+ result = http_device.send(:deliver_requests, conn)
151
+ expect(result).to eq(false)
152
+ end
153
+
154
+ expect(req_queue.size).to eq(1)
134
155
  end
135
156
  end
136
157
  end
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.20
4
+ version: 2.0.21
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-05-19 00:00:00.000000000 Z
11
+ date: 2017-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -199,7 +199,6 @@ files:
199
199
  - lib/timber/log_devices/http.rb
200
200
  - lib/timber/log_entry.rb
201
201
  - lib/timber/logger.rb
202
- - lib/timber/logger/metadata_methods.rb
203
202
  - lib/timber/overrides.rb
204
203
  - lib/timber/overrides/lograge.rb
205
204
  - lib/timber/overrides/rails_stdout_logging.rb
@@ -264,7 +263,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
264
263
  version: '0'
265
264
  requirements: []
266
265
  rubyforge_project:
267
- rubygems_version: 2.5.2
266
+ rubygems_version: 2.4.5.2
268
267
  signing_key:
269
268
  specification_version: 4
270
269
  summary: Log Better. Solve Problems Faster. https://timber.io
@@ -1 +0,0 @@
1
- metadata_methods.rb