timber 2.0.24 → 2.1.0.rc1
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 +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
|