timber 2.0.24 → 2.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/CHANGELOG +3 -0
- data/README.md +314 -59
- data/bin/timber +11 -2
- data/lib/timber.rb +2 -7
- data/lib/timber/cli.rb +16 -28
- data/lib/timber/cli/api.rb +80 -14
- data/lib/timber/cli/api/application.rb +30 -0
- data/lib/timber/cli/config_file.rb +66 -0
- data/lib/timber/cli/file_helper.rb +43 -0
- data/lib/timber/cli/installer.rb +58 -0
- data/lib/timber/cli/installers.rb +37 -0
- data/lib/timber/cli/installers/other.rb +47 -0
- data/lib/timber/cli/installers/rails.rb +255 -0
- data/lib/timber/cli/installers/root.rb +189 -0
- data/lib/timber/cli/io.rb +97 -0
- data/lib/timber/cli/io/ansi.rb +22 -0
- data/lib/timber/cli/io/messages.rb +213 -0
- data/lib/timber/cli/os_helper.rb +53 -0
- data/lib/timber/config.rb +97 -43
- data/lib/timber/config/integrations.rb +63 -0
- data/lib/timber/config/integrations/rack.rb +74 -0
- data/lib/timber/context.rb +13 -10
- data/lib/timber/contexts.rb +1 -0
- data/lib/timber/contexts/custom.rb +16 -3
- data/lib/timber/contexts/http.rb +10 -3
- data/lib/timber/contexts/organization.rb +4 -0
- data/lib/timber/contexts/release.rb +46 -0
- data/lib/timber/contexts/runtime.rb +7 -1
- data/lib/timber/contexts/session.rb +8 -1
- data/lib/timber/contexts/system.rb +5 -1
- data/lib/timber/contexts/user.rb +9 -2
- data/lib/timber/current_context.rb +43 -11
- data/lib/timber/events/controller_call.rb +4 -0
- data/lib/timber/events/custom.rb +13 -5
- data/lib/timber/events/exception.rb +4 -0
- data/lib/timber/events/http_client_request.rb +4 -0
- data/lib/timber/events/http_client_response.rb +4 -0
- data/lib/timber/events/http_server_request.rb +5 -0
- data/lib/timber/events/http_server_response.rb +15 -3
- data/lib/timber/events/sql_query.rb +3 -0
- data/lib/timber/events/template_render.rb +3 -0
- data/lib/timber/integration.rb +40 -0
- data/lib/timber/integrations.rb +21 -14
- data/lib/timber/integrations/action_controller.rb +18 -0
- data/lib/timber/integrations/action_controller/log_subscriber.rb +2 -0
- data/lib/timber/integrations/action_controller/log_subscriber/timber_log_subscriber.rb +6 -0
- data/lib/timber/integrations/action_dispatch.rb +23 -0
- data/lib/timber/integrations/action_dispatch/debug_exceptions.rb +2 -0
- data/lib/timber/integrations/action_view.rb +18 -0
- data/lib/timber/integrations/action_view/log_subscriber.rb +2 -0
- data/lib/timber/integrations/action_view/log_subscriber/timber_log_subscriber.rb +10 -0
- data/lib/timber/integrations/active_record.rb +18 -0
- data/lib/timber/integrations/active_record/log_subscriber.rb +2 -0
- data/lib/timber/integrations/active_record/log_subscriber/timber_log_subscriber.rb +8 -0
- data/lib/timber/integrations/rack.rb +12 -2
- data/lib/timber/integrations/rack/exception_event.rb +38 -5
- data/lib/timber/integrations/rack/http_context.rb +4 -6
- data/lib/timber/integrations/rack/http_events.rb +177 -27
- data/lib/timber/integrations/rack/middleware.rb +28 -0
- data/lib/timber/integrations/rack/session_context.rb +5 -6
- data/lib/timber/integrations/rack/user_context.rb +90 -43
- data/lib/timber/integrations/rails.rb +22 -0
- data/lib/timber/integrations/rails/rack_logger.rb +2 -0
- data/lib/timber/integrator.rb +18 -3
- data/lib/timber/log_devices/http.rb +107 -99
- data/lib/timber/log_devices/http/dropping_sized_queue.rb +26 -0
- data/lib/timber/log_devices/http/flushable_sized_queue.rb +42 -0
- data/lib/timber/log_entry.rb +14 -2
- data/lib/timber/logger.rb +51 -36
- data/lib/timber/overrides.rb +2 -0
- data/lib/timber/overrides/active_support_3_tagged_logging.rb +103 -0
- data/lib/timber/overrides/active_support_tagged_logging.rb +53 -90
- data/lib/timber/timer.rb +21 -0
- data/lib/timber/util/hash.rb +1 -1
- data/lib/timber/util/http_event.rb +16 -3
- data/lib/timber/version.rb +1 -1
- data/spec/support/timber.rb +2 -3
- data/spec/timber/cli/installers/rails_spec.rb +160 -0
- data/spec/timber/cli/installers/root_spec.rb +100 -0
- data/spec/timber/config_spec.rb +28 -0
- data/spec/timber/current_context_spec.rb +61 -12
- data/spec/timber/events/custom_spec.rb +13 -2
- data/spec/timber/events/exception_spec.rb +15 -0
- data/spec/timber/events/http_server_request_spec.rb +3 -3
- data/spec/timber/integrations/rack/http_events_spec.rb +101 -0
- data/spec/timber/log_devices/http_spec.rb +20 -4
- data/spec/timber/log_entry_spec.rb +2 -1
- data/spec/timber/logger_spec.rb +8 -8
- metadata +40 -9
- data/benchmarks/rails.rb +0 -122
- data/lib/timber/cli/application.rb +0 -28
- data/lib/timber/cli/install.rb +0 -196
- data/lib/timber/cli/io_helper.rb +0 -65
- data/lib/timber/cli/messages.rb +0 -180
- data/lib/timber/integrations/active_support/tagged_logging.rb +0 -71
@@ -0,0 +1,22 @@
|
|
1
|
+
require "timber/integration"
|
2
|
+
require "timber/integrations/rack/http_events"
|
3
|
+
require "timber/integrations/rails/rack_logger"
|
4
|
+
|
5
|
+
module Timber
|
6
|
+
module Integrations
|
7
|
+
# Module for holding *all* Rails integrations. This module does *not*
|
8
|
+
# extend {Integration} because it's dependent on {Rack::HTTPEvents}. This
|
9
|
+
# module simply disables the default HTTP request logging.
|
10
|
+
module Rails
|
11
|
+
def self.enabled?
|
12
|
+
Rack::HTTPEvents.enabled?
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.integrate!
|
16
|
+
return false if !enabled?
|
17
|
+
|
18
|
+
RackLogger.integrate!
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/timber/integrator.rb
CHANGED
@@ -1,17 +1,31 @@
|
|
1
1
|
module Timber
|
2
|
-
# Base class for `Timber::Integrations::*`.
|
3
|
-
#
|
4
|
-
#
|
2
|
+
# Base class for `Timber::Integrations::*`. Provides a common interface for all integrators.
|
3
|
+
# An integrator is a single specific integration into a part of a library. See
|
4
|
+
# {Integration} for higher library level integration settings.
|
5
5
|
class Integrator
|
6
|
+
# Raised when an integrators requirements are not met. For example, this will be raised
|
7
|
+
# in the ActiveRecord integration if ActiveRecord is not available as a dependency in
|
8
|
+
# the current application.
|
6
9
|
class RequirementNotMetError < StandardError; end
|
7
10
|
|
8
11
|
class << self
|
9
12
|
attr_writer :enabled
|
10
13
|
|
14
|
+
# Allows you to enable / disable specific integrations.
|
15
|
+
#
|
16
|
+
# @note Disabling specific low level integrations should only be needed for edge cases.
|
17
|
+
# If you want to disable integration with an entire library, we recommend doing so
|
18
|
+
# at a higher level. Ex: `Timber::Integrations::ActiveRecord.enabled = false`.
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# Timber::Integrations::ActiveRecord::LogSubscriber.enabled = false
|
11
22
|
def enabled?
|
12
23
|
@enabled != false
|
13
24
|
end
|
14
25
|
|
26
|
+
# Convenience class level method that runs the integrator by instantiating a new
|
27
|
+
# object and calling {#integrate!}. It also takes care to look at the if the integrator
|
28
|
+
# is enabled, skipping it if not.
|
15
29
|
def integrate!(*args)
|
16
30
|
if !enabled?
|
17
31
|
Config.instance.debug_logger.debug("#{name} integration disabled, skipping") if Config.instance.debug_logger
|
@@ -28,6 +42,7 @@ module Timber
|
|
28
42
|
end
|
29
43
|
end
|
30
44
|
|
45
|
+
# Abstract method that each integration must implement.
|
31
46
|
def integrate!
|
32
47
|
raise NotImplementedError.new
|
33
48
|
end
|
@@ -1,81 +1,38 @@
|
|
1
1
|
require "base64"
|
2
|
+
require "msgpack"
|
2
3
|
require "net/https"
|
3
4
|
|
5
|
+
require "timber/config"
|
6
|
+
require "timber/log_devices/http/dropping_sized_queue"
|
7
|
+
require "timber/log_devices/http/flushable_sized_queue"
|
8
|
+
require "timber/version"
|
9
|
+
|
4
10
|
module Timber
|
5
11
|
module LogDevices
|
6
12
|
# A highly efficient log device that buffers and delivers log messages over HTTPS to
|
7
13
|
# the Timber API. It uses batches, keep-alive connections, and msgpack to deliver logs with
|
8
14
|
# high-throughput and little overhead. All log preparation and delivery is done asynchronously
|
9
|
-
# in a thread as not to block application execution
|
15
|
+
# in a thread as not to block application execution and efficient deliver logs for
|
16
|
+
# multi-threaded environments.
|
10
17
|
#
|
11
18
|
# See {#initialize} for options and more details.
|
12
19
|
class HTTP
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@lock = Mutex.new
|
17
|
-
@max_size = max_size
|
18
|
-
@array = []
|
19
|
-
end
|
20
|
-
|
21
|
-
def enqueue(msg)
|
22
|
-
@lock.synchronize do
|
23
|
-
@array << msg
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def flush
|
28
|
-
@lock.synchronize do
|
29
|
-
old = @array
|
30
|
-
@array = []
|
31
|
-
return old
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def full?
|
36
|
-
size >= @max_size
|
37
|
-
end
|
38
|
-
|
39
|
-
def size
|
40
|
-
@array.size
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# Works like SizedQueue, but drops message instead of blocking. Pass one of these in
|
45
|
-
# to {HTTP#intiialize} via the :request_queue option if you'd prefer to drop messages
|
46
|
-
# in the event of a buffer overflow instead of applying back pressure.
|
47
|
-
class DroppingSizedQueue < SizedQueue
|
48
|
-
# Returns true/false depending on whether the queue is full or not
|
49
|
-
def push(obj)
|
50
|
-
@mutex.synchronize do
|
51
|
-
return false unless @que.length < @max
|
52
|
-
|
53
|
-
@que.push obj
|
54
|
-
begin
|
55
|
-
t = @waiting.shift
|
56
|
-
t.wakeup if t
|
57
|
-
rescue ThreadError
|
58
|
-
retry
|
59
|
-
end
|
60
|
-
return true
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
TIMBER_URL = "https://logs.timber.io/frames".freeze
|
20
|
+
TIMBER_STAGING_URL = "https://logs-staging.timber.io/frames".freeze
|
21
|
+
TIMBER_PRODUCTION_URL = "https://logs.timber.io/frames".freeze
|
22
|
+
TIMBER_URL = ENV['TIMBER_STAGING'] ? TIMBER_STAGING_URL : TIMBER_PRODUCTION_URL
|
66
23
|
CONTENT_TYPE = "application/msgpack".freeze
|
67
24
|
USER_AGENT = "Timber Ruby/#{Timber::VERSION} (HTTP)".freeze
|
68
25
|
|
69
|
-
|
70
26
|
# Instantiates a new HTTP log device that can be passed to {Timber::Logger#initialize}.
|
71
27
|
#
|
72
28
|
# The class maintains a buffer which is flushed in batches to the Timber API. 2
|
73
29
|
# options control when the flush happens, `:batch_byte_size` and `:flush_interval`.
|
74
30
|
# If either of these are surpassed, the buffer will be flushed.
|
75
31
|
#
|
76
|
-
# By default, the buffer will apply back pressure
|
77
|
-
# the
|
78
|
-
# {DroppingSizedQueue} via the
|
32
|
+
# By default, the buffer will apply back pressure when the rate of log messages exceeds
|
33
|
+
# the maximum delivery rate. If you don't want to sacrifice app performance in this case
|
34
|
+
# you can drop the log messages instead by passing a {DroppingSizedQueue} via the
|
35
|
+
# `:request_queue` option.
|
79
36
|
#
|
80
37
|
# @param api_key [String] The API key provided to you after you add your application to
|
81
38
|
# [Timber](https://timber.io).
|
@@ -117,14 +74,16 @@ module Timber
|
|
117
74
|
@flush_continuously = options[:flush_continuously] != false
|
118
75
|
@flush_interval = options[:flush_interval] || 1 # 1 second
|
119
76
|
@requests_per_conn = options[:requests_per_conn] || 2_500
|
120
|
-
@msg_queue =
|
77
|
+
@msg_queue = FlushableSizedQueue.new(@batch_size)
|
121
78
|
@request_queue = options[:request_queue] || SizedQueue.new(3)
|
122
79
|
@successive_error_count = 0
|
123
80
|
@requests_in_flight = 0
|
124
81
|
end
|
125
82
|
|
126
|
-
# Write a new log line message to the buffer, and
|
127
|
-
#
|
83
|
+
# Write a new log line message to the buffer, and flush asynchronously if the
|
84
|
+
# message queue is full. We flush asynchronously because the maximum message batch
|
85
|
+
# size is constricted by the Timber API. The actual application limit is a multiple
|
86
|
+
# of this. Hence the `@request_queue`.
|
128
87
|
def write(msg)
|
129
88
|
@msg_queue.enqueue(msg)
|
130
89
|
|
@@ -135,23 +94,19 @@ module Timber
|
|
135
94
|
ensure_flush_threads_are_started
|
136
95
|
|
137
96
|
if @msg_queue.full?
|
138
|
-
|
139
|
-
|
97
|
+
debug { "Flushing HTTP buffer via write" }
|
98
|
+
flush_async
|
140
99
|
end
|
141
100
|
true
|
142
101
|
end
|
143
102
|
|
103
|
+
# Flush all log messages in the buffer synchronously. This method will not return
|
104
|
+
# until delivery of the messages has been successful. If you want to flush
|
105
|
+
# asynchronously see {#flush_async}.
|
144
106
|
def flush
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
req = Net::HTTP::Post.new(@timber_url.path)
|
150
|
-
req['Authorization'] = authorization_payload
|
151
|
-
req['Content-Type'] = CONTENT_TYPE
|
152
|
-
req['User-Agent'] = USER_AGENT
|
153
|
-
req.body = msgs.to_msgpack
|
154
|
-
@request_queue.enq(req)
|
107
|
+
flush_async
|
108
|
+
wait_on_request_queue
|
109
|
+
true
|
155
110
|
end
|
156
111
|
|
157
112
|
# Closes the log device, cleans up, and attempts one last delivery.
|
@@ -162,20 +117,8 @@ module Timber
|
|
162
117
|
# Flush all remaining messages
|
163
118
|
flush
|
164
119
|
|
165
|
-
# Kill the
|
166
|
-
|
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
|
120
|
+
# Kill the request queue thread. Flushing ensures that no requests are pending.
|
121
|
+
@request_outlet_thread.kill if @request_outlet_thread
|
179
122
|
end
|
180
123
|
|
181
124
|
private
|
@@ -183,8 +126,16 @@ module Timber
|
|
183
126
|
Timber::Config.instance.debug_logger
|
184
127
|
end
|
185
128
|
|
129
|
+
# Convenience method for writing debug messages.
|
130
|
+
def debug(&block)
|
131
|
+
if debug_logger
|
132
|
+
message = yield
|
133
|
+
debug_logger.debug(message)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
186
137
|
# This is a convenience method to ensure the flush thread are
|
187
|
-
# started. This is called lazily from #write so that we
|
138
|
+
# started. This is called lazily from {#write} so that we
|
188
139
|
# only start the threads as needed, but it also ensures
|
189
140
|
# threads are started after process forking.
|
190
141
|
def ensure_flush_threads_are_started
|
@@ -199,6 +150,53 @@ module Timber
|
|
199
150
|
end
|
200
151
|
end
|
201
152
|
|
153
|
+
# Builds an HTTP request based on the current messages queued.
|
154
|
+
def build_request
|
155
|
+
msgs = @msg_queue.flush
|
156
|
+
return if msgs.empty?
|
157
|
+
|
158
|
+
req = Net::HTTP::Post.new(@timber_url.path)
|
159
|
+
req['Authorization'] = authorization_payload
|
160
|
+
req['Content-Type'] = CONTENT_TYPE
|
161
|
+
req['User-Agent'] = USER_AGENT
|
162
|
+
req.body = msgs.to_msgpack
|
163
|
+
req
|
164
|
+
end
|
165
|
+
|
166
|
+
# Flushes the message buffer asynchronously. The reason we provide this
|
167
|
+
# method is because the message buffer limit is constricted by the
|
168
|
+
# Timber API. The application limit is multiples of the buffer limit,
|
169
|
+
# hence the `@request_queue`, allowing us to buffer beyond the Timber API
|
170
|
+
# imposed limit.
|
171
|
+
def flush_async
|
172
|
+
@last_async_flush = Time.now
|
173
|
+
req = build_request
|
174
|
+
if !req.nil?
|
175
|
+
debug { "New request placed on queue" }
|
176
|
+
@request_queue.enq(req)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Waits on the request queue. This is used in {#flush} to ensure
|
181
|
+
# the log data has been delivered before returning.
|
182
|
+
def wait_on_request_queue
|
183
|
+
# Wait 20 seconds
|
184
|
+
40.times do |i|
|
185
|
+
if @request_queue.size == 0 && @requests_in_flight == 0
|
186
|
+
debug { "Request queue is empty and no requests are in flight, finish waiting" }
|
187
|
+
return true
|
188
|
+
end
|
189
|
+
debug do
|
190
|
+
"Request size #{@request_queue.size}, reqs in-flight #{@requests_in_flight}, " \
|
191
|
+
"continue waiting (iteration #{i + 1})"
|
192
|
+
end
|
193
|
+
sleep 0.5
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# Flushes the message queue on an interval. You will notice that {#write} also
|
198
|
+
# flushes the buffer if it is full. This method takes note of this via the
|
199
|
+
# `@last_async_flush` variable as to not flush immediately after a write flush.
|
202
200
|
def intervaled_flush
|
203
201
|
# Wait specified time period before starting
|
204
202
|
sleep @flush_interval
|
@@ -206,21 +204,25 @@ module Timber
|
|
206
204
|
loop do
|
207
205
|
begin
|
208
206
|
if intervaled_flush_ready?
|
209
|
-
|
210
|
-
|
207
|
+
debug { "Flushing HTTP buffer via the interval" }
|
208
|
+
flush_async
|
211
209
|
end
|
212
210
|
|
213
211
|
sleep(0.5)
|
214
212
|
rescue Exception => e
|
215
|
-
|
213
|
+
debug { "Intervaled HTTP flush failed: #{e.inspect}\n\n#{e.backtrace}" }
|
216
214
|
end
|
217
215
|
end
|
218
216
|
end
|
219
217
|
|
218
|
+
# Determines if the loop in {#intervaled_flush} is ready to be flushed again. It
|
219
|
+
# uses the `@last_async_flush` variable to ensure that a flush does not happen
|
220
|
+
# too rapidly ({#write} also triggers a flush).
|
220
221
|
def intervaled_flush_ready?
|
221
|
-
@
|
222
|
+
@last_async_flush.nil? || (Time.now.to_f - @last_async_flush.to_f).abs >= @flush_interval
|
222
223
|
end
|
223
224
|
|
225
|
+
# Builds an `Net::HTTP` object to deliver requests over.
|
224
226
|
def build_http
|
225
227
|
http = Net::HTTP.new(@timber_url.host, @timber_url.port)
|
226
228
|
http.set_debug_output(debug_logger) if debug_logger
|
@@ -231,30 +233,35 @@ module Timber
|
|
231
233
|
http
|
232
234
|
end
|
233
235
|
|
236
|
+
# Creates a loop that processes the `@request_queue` on an interval.
|
234
237
|
def request_outlet
|
235
238
|
loop do
|
236
239
|
http = build_http
|
237
240
|
|
238
241
|
begin
|
239
|
-
|
242
|
+
debug { "Starting HTTP connection" }
|
240
243
|
|
241
244
|
http.start do |conn|
|
242
245
|
deliver_requests(conn)
|
243
246
|
end
|
244
247
|
rescue => e
|
245
|
-
|
248
|
+
debug { "#request_outlet error: #{e.message}" }
|
246
249
|
ensure
|
247
|
-
|
250
|
+
debug { "Finishing HTTP connection" }
|
248
251
|
http.finish if http.started?
|
249
252
|
end
|
250
253
|
end
|
251
254
|
end
|
252
255
|
|
256
|
+
# Creates a loop that delivers requests over an open (kept alive) HTTP connection.
|
257
|
+
# If the connection dies, the request is thrown back onto the queue and
|
258
|
+
# the method returns. It is the responsibility of the caller to implement retries
|
259
|
+
# and establish a new connection.
|
253
260
|
def deliver_requests(conn)
|
254
261
|
num_reqs = 0
|
255
262
|
|
256
263
|
while num_reqs < @requests_per_conn
|
257
|
-
|
264
|
+
debug { "Waiting on next request, threads waiting: #{@request_queue.num_waiting}" }
|
258
265
|
|
259
266
|
# Blocks waiting for a request.
|
260
267
|
req = @request_queue.deq
|
@@ -263,7 +270,7 @@ module Timber
|
|
263
270
|
begin
|
264
271
|
resp = conn.request(req)
|
265
272
|
rescue => e
|
266
|
-
|
273
|
+
debug { "#deliver_request error: #{e.message}" }
|
267
274
|
|
268
275
|
@successive_error_count += 1
|
269
276
|
|
@@ -271,7 +278,7 @@ module Timber
|
|
271
278
|
calculated_backoff = @successive_error_count * 2
|
272
279
|
backoff = calculated_backoff > 30 ? 30 : calculated_backoff
|
273
280
|
|
274
|
-
|
281
|
+
debug { "Backing off #{backoff} seconds, error ##{@successive_error_count}" }
|
275
282
|
|
276
283
|
sleep backoff
|
277
284
|
|
@@ -284,10 +291,11 @@ module Timber
|
|
284
291
|
|
285
292
|
@successive_error_count = 0
|
286
293
|
num_reqs += 1
|
287
|
-
|
294
|
+
debug { "Request successful: #{resp.code}" }
|
288
295
|
end
|
289
296
|
end
|
290
297
|
|
298
|
+
# Builds the `Authorization` header value for HTTP delivery to the Timber API.
|
291
299
|
def authorization_payload
|
292
300
|
@authorization_payload ||= "Basic #{Base64.urlsafe_encode64(@api_key).chomp}"
|
293
301
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Timber
|
2
|
+
module LogDevices
|
3
|
+
class HTTP
|
4
|
+
# Works like SizedQueue, but drops message instead of blocking. Pass one of these in
|
5
|
+
# to {HTTP#intiialize} via the :request_queue option if you'd prefer to drop messages
|
6
|
+
# in the event of a buffer overflow instead of applying back pressure.
|
7
|
+
class DroppingSizedQueue < SizedQueue
|
8
|
+
# Returns true/false depending on whether the queue is full or not
|
9
|
+
def push(obj)
|
10
|
+
@mutex.synchronize do
|
11
|
+
return false unless @que.length < @max
|
12
|
+
|
13
|
+
@que.push obj
|
14
|
+
begin
|
15
|
+
t = @waiting.shift
|
16
|
+
t.wakeup if t
|
17
|
+
rescue ThreadError
|
18
|
+
retry
|
19
|
+
end
|
20
|
+
return true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Timber
|
2
|
+
module LogDevices
|
3
|
+
class HTTP
|
4
|
+
# A simple thread-safe queue implementation that provides a #flush method.
|
5
|
+
# The built-in ruby Queue class does not provide a #flush method. It also
|
6
|
+
# implement thread waiting which is something we do not want. To keep things
|
7
|
+
# simple and straight-forward we designed our own simple queue class.
|
8
|
+
# @private
|
9
|
+
class FlushableSizedQueue
|
10
|
+
def initialize(max_size)
|
11
|
+
@lock = Mutex.new
|
12
|
+
@max_size = max_size
|
13
|
+
@array = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# Adds a message to the queue
|
17
|
+
def enqueue(msg)
|
18
|
+
@lock.synchronize do
|
19
|
+
@array << msg
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Flushes all message from the queue and returns them.
|
24
|
+
def flush
|
25
|
+
@lock.synchronize do
|
26
|
+
old = @array
|
27
|
+
@array = []
|
28
|
+
return old
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def full?
|
33
|
+
size >= @max_size
|
34
|
+
end
|
35
|
+
|
36
|
+
def size
|
37
|
+
@array.size
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|