airbrake-ruby 4.6.0 → 6.2.2
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 +5 -5
- data/lib/airbrake-ruby/async_sender.rb +24 -109
- data/lib/airbrake-ruby/backlog.rb +123 -0
- data/lib/airbrake-ruby/backtrace.rb +8 -7
- data/lib/airbrake-ruby/benchmark.rb +1 -1
- data/lib/airbrake-ruby/code_hunk.rb +1 -1
- data/lib/airbrake-ruby/config/processor.rb +77 -0
- data/lib/airbrake-ruby/config/validator.rb +9 -3
- data/lib/airbrake-ruby/config.rb +78 -19
- data/lib/airbrake-ruby/context.rb +51 -0
- data/lib/airbrake-ruby/deploy_notifier.rb +1 -1
- data/lib/airbrake-ruby/file_cache.rb +6 -0
- data/lib/airbrake-ruby/filter_chain.rb +18 -1
- data/lib/airbrake-ruby/filters/context_filter.rb +4 -5
- data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +3 -3
- data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +7 -7
- data/lib/airbrake-ruby/filters/git_repository_filter.rb +11 -2
- data/lib/airbrake-ruby/filters/git_revision_filter.rb +3 -1
- data/lib/airbrake-ruby/filters/{keys_whitelist.rb → keys_allowlist.rb} +3 -3
- data/lib/airbrake-ruby/filters/{keys_blacklist.rb → keys_blocklist.rb} +3 -3
- data/lib/airbrake-ruby/filters/keys_filter.rb +41 -22
- data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/sql_filter.rb +35 -11
- data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/thread_filter.rb +6 -5
- data/lib/airbrake-ruby/grouppable.rb +12 -0
- data/lib/airbrake-ruby/ignorable.rb +1 -2
- data/lib/airbrake-ruby/inspectable.rb +2 -2
- data/lib/airbrake-ruby/loggable.rb +1 -1
- data/lib/airbrake-ruby/mergeable.rb +12 -0
- data/lib/airbrake-ruby/monotonic_time.rb +6 -1
- data/lib/airbrake-ruby/nested_exception.rb +22 -1
- data/lib/airbrake-ruby/notice.rb +12 -17
- data/lib/airbrake-ruby/notice_notifier.rb +15 -7
- data/lib/airbrake-ruby/performance_breakdown.rb +16 -9
- data/lib/airbrake-ruby/performance_notifier.rb +106 -54
- data/lib/airbrake-ruby/promise.rb +1 -0
- data/lib/airbrake-ruby/query.rb +20 -14
- data/lib/airbrake-ruby/queue.rb +65 -0
- data/lib/airbrake-ruby/remote_settings/callback.rb +44 -0
- data/lib/airbrake-ruby/remote_settings/settings_data.rb +116 -0
- data/lib/airbrake-ruby/remote_settings.rb +128 -0
- data/lib/airbrake-ruby/request.rb +14 -11
- data/lib/airbrake-ruby/response.rb +56 -5
- data/lib/airbrake-ruby/stat.rb +27 -34
- data/lib/airbrake-ruby/sync_sender.rb +43 -11
- data/lib/airbrake-ruby/tdigest.rb +52 -66
- data/lib/airbrake-ruby/thread_pool.rb +139 -0
- data/lib/airbrake-ruby/time_truncate.rb +2 -2
- data/lib/airbrake-ruby/timed_trace.rb +1 -3
- data/lib/airbrake-ruby/truncator.rb +10 -4
- data/lib/airbrake-ruby/version.rb +11 -1
- data/lib/airbrake-ruby.rb +157 -78
- metadata +122 -116
- data/spec/airbrake_spec.rb +0 -324
- data/spec/async_sender_spec.rb +0 -155
- data/spec/backtrace_spec.rb +0 -427
- data/spec/benchmark_spec.rb +0 -33
- data/spec/code_hunk_spec.rb +0 -115
- data/spec/config/validator_spec.rb +0 -184
- data/spec/config_spec.rb +0 -154
- data/spec/deploy_notifier_spec.rb +0 -48
- data/spec/file_cache.rb +0 -36
- data/spec/filter_chain_spec.rb +0 -92
- data/spec/filters/context_filter_spec.rb +0 -23
- data/spec/filters/dependency_filter_spec.rb +0 -12
- data/spec/filters/exception_attributes_filter_spec.rb +0 -50
- data/spec/filters/gem_root_filter_spec.rb +0 -41
- data/spec/filters/git_last_checkout_filter_spec.rb +0 -46
- data/spec/filters/git_repository_filter.rb +0 -61
- data/spec/filters/git_revision_filter_spec.rb +0 -126
- data/spec/filters/keys_blacklist_spec.rb +0 -225
- data/spec/filters/keys_whitelist_spec.rb +0 -194
- data/spec/filters/root_directory_filter_spec.rb +0 -39
- data/spec/filters/sql_filter_spec.rb +0 -219
- data/spec/filters/system_exit_filter_spec.rb +0 -14
- data/spec/filters/thread_filter_spec.rb +0 -277
- data/spec/fixtures/notroot.txt +0 -7
- data/spec/fixtures/project_root/code.rb +0 -221
- data/spec/fixtures/project_root/empty_file.rb +0 -0
- data/spec/fixtures/project_root/long_line.txt +0 -1
- data/spec/fixtures/project_root/short_file.rb +0 -3
- data/spec/fixtures/project_root/vendor/bundle/ignored_file.rb +0 -5
- data/spec/helpers.rb +0 -9
- data/spec/ignorable_spec.rb +0 -14
- data/spec/inspectable_spec.rb +0 -45
- data/spec/monotonic_time_spec.rb +0 -12
- data/spec/nested_exception_spec.rb +0 -73
- data/spec/notice_notifier_spec/options_spec.rb +0 -259
- data/spec/notice_notifier_spec.rb +0 -356
- data/spec/notice_spec.rb +0 -296
- data/spec/performance_breakdown_spec.rb +0 -12
- data/spec/performance_notifier_spec.rb +0 -435
- data/spec/promise_spec.rb +0 -197
- data/spec/query_spec.rb +0 -11
- data/spec/request_spec.rb +0 -11
- data/spec/response_spec.rb +0 -88
- data/spec/spec_helper.rb +0 -100
- data/spec/stashable_spec.rb +0 -23
- data/spec/stat_spec.rb +0 -47
- data/spec/sync_sender_spec.rb +0 -133
- data/spec/tdigest_spec.rb +0 -230
- data/spec/time_truncate_spec.rb +0 -13
- data/spec/timed_trace_spec.rb +0 -125
- data/spec/truncator_spec.rb +0 -238
|
@@ -24,6 +24,7 @@ module Airbrake
|
|
|
24
24
|
# @since v3.2.0
|
|
25
25
|
class Centroid
|
|
26
26
|
attr_accessor :mean, :n, :cumn, :mean_cumn
|
|
27
|
+
|
|
27
28
|
def initialize(mean, n, cumn, mean_cumn = nil)
|
|
28
29
|
@mean = mean
|
|
29
30
|
@n = n
|
|
@@ -37,14 +38,15 @@ module Airbrake
|
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
attr_accessor :centroids
|
|
41
|
+
attr_reader :size
|
|
42
|
+
|
|
40
43
|
def initialize(delta = 0.01, k = 25, cx = 1.1)
|
|
41
44
|
@delta = delta
|
|
42
45
|
@k = k
|
|
43
46
|
@cx = cx
|
|
44
47
|
@centroids = RBTree.new
|
|
45
|
-
@
|
|
46
|
-
@
|
|
47
|
-
reset!
|
|
48
|
+
@size = 0
|
|
49
|
+
@last_cumulate = 0
|
|
48
50
|
end
|
|
49
51
|
|
|
50
52
|
def +(other)
|
|
@@ -59,8 +61,8 @@ module Airbrake
|
|
|
59
61
|
# compression as defined by Java implementation
|
|
60
62
|
size = @centroids.size
|
|
61
63
|
output = [VERBOSE_ENCODING, compression, size]
|
|
62
|
-
output += @centroids.map
|
|
63
|
-
output += @centroids.map
|
|
64
|
+
output += @centroids.each_value.map(&:mean)
|
|
65
|
+
output += @centroids.each_value.map(&:n)
|
|
64
66
|
output.pack("NGNG#{size}N#{size}")
|
|
65
67
|
end
|
|
66
68
|
|
|
@@ -70,14 +72,14 @@ module Airbrake
|
|
|
70
72
|
output = [self.class::SMALL_ENCODING, compression, size]
|
|
71
73
|
x = 0
|
|
72
74
|
# delta encoding allows saving 4-bytes floats
|
|
73
|
-
mean_arr = @centroids.map do |
|
|
75
|
+
mean_arr = @centroids.each_value.map do |c|
|
|
74
76
|
val = c.mean - x
|
|
75
77
|
x = c.mean
|
|
76
78
|
val
|
|
77
79
|
end
|
|
78
80
|
output += mean_arr
|
|
79
81
|
# Variable length encoding of numbers
|
|
80
|
-
c_arr = @centroids.
|
|
82
|
+
c_arr = @centroids.each_value.with_object([]) do |c, arr|
|
|
81
83
|
k = 0
|
|
82
84
|
n = c.n
|
|
83
85
|
while n < 0 || n > 0x7f
|
|
@@ -95,7 +97,7 @@ module Airbrake
|
|
|
95
97
|
# rubocop:enable Metrics/AbcSize
|
|
96
98
|
|
|
97
99
|
def as_json(_ = nil)
|
|
98
|
-
@centroids.map
|
|
100
|
+
@centroids.each_value.map(&:as_json)
|
|
99
101
|
end
|
|
100
102
|
|
|
101
103
|
def bound_mean(x)
|
|
@@ -129,7 +131,7 @@ module Airbrake
|
|
|
129
131
|
points = to_a
|
|
130
132
|
reset!
|
|
131
133
|
push_centroid(points.shuffle)
|
|
132
|
-
_cumulate(true, true)
|
|
134
|
+
_cumulate(exact: true, force: true)
|
|
133
135
|
nil
|
|
134
136
|
end
|
|
135
137
|
|
|
@@ -138,21 +140,17 @@ module Airbrake
|
|
|
138
140
|
end
|
|
139
141
|
|
|
140
142
|
def find_nearest(x)
|
|
141
|
-
return
|
|
142
|
-
|
|
143
|
-
ceil = @centroids.upper_bound(x)
|
|
144
|
-
floor = @centroids.lower_bound(x)
|
|
143
|
+
return if size == 0
|
|
145
144
|
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
upper_key, upper = @centroids.upper_bound(x)
|
|
146
|
+
lower_key, lower = @centroids.lower_bound(x)
|
|
147
|
+
return lower unless upper_key
|
|
148
|
+
return upper unless lower_key
|
|
148
149
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (floor_key - x).abs < (ceil_key - x).abs
|
|
153
|
-
floor[1]
|
|
150
|
+
if (lower_key - x).abs < (upper_key - x).abs
|
|
151
|
+
lower
|
|
154
152
|
else
|
|
155
|
-
|
|
153
|
+
upper
|
|
156
154
|
end
|
|
157
155
|
end
|
|
158
156
|
|
|
@@ -178,7 +176,7 @@ module Airbrake
|
|
|
178
176
|
elsif item > max[1].mean
|
|
179
177
|
1.0
|
|
180
178
|
else
|
|
181
|
-
_cumulate(true)
|
|
179
|
+
_cumulate(exact: true)
|
|
182
180
|
bound = bound_mean(item)
|
|
183
181
|
lower, upper = bound
|
|
184
182
|
mean_cumn = lower.mean_cumn
|
|
@@ -186,7 +184,7 @@ module Airbrake
|
|
|
186
184
|
mean_cumn += (item - lower.mean) * (upper.mean_cumn - lower.mean_cumn) \
|
|
187
185
|
/ (upper.mean - lower.mean)
|
|
188
186
|
end
|
|
189
|
-
mean_cumn / @
|
|
187
|
+
mean_cumn / @size
|
|
190
188
|
end
|
|
191
189
|
end
|
|
192
190
|
is_array ? x : x.first
|
|
@@ -203,11 +201,12 @@ module Airbrake
|
|
|
203
201
|
unless (0..1).cover?(item)
|
|
204
202
|
raise ArgumentError, "p should be in [0,1], got #{item}"
|
|
205
203
|
end
|
|
204
|
+
|
|
206
205
|
if size == 0
|
|
207
206
|
nil
|
|
208
207
|
else
|
|
209
|
-
_cumulate(true)
|
|
210
|
-
h = @
|
|
208
|
+
_cumulate(exact: true)
|
|
209
|
+
h = @size * item
|
|
211
210
|
lower, upper = bound_mean_cumn(h)
|
|
212
211
|
if lower.nil? && upper.nil?
|
|
213
212
|
nil
|
|
@@ -237,17 +236,12 @@ module Airbrake
|
|
|
237
236
|
|
|
238
237
|
def reset!
|
|
239
238
|
@centroids.clear
|
|
240
|
-
@
|
|
241
|
-
@nreset += 1
|
|
239
|
+
@size = 0
|
|
242
240
|
@last_cumulate = 0
|
|
243
241
|
end
|
|
244
242
|
|
|
245
|
-
def size
|
|
246
|
-
@n || 0
|
|
247
|
-
end
|
|
248
|
-
|
|
249
243
|
def to_a
|
|
250
|
-
@centroids.
|
|
244
|
+
@centroids.each_value.to_a
|
|
251
245
|
end
|
|
252
246
|
|
|
253
247
|
# rubocop:disable Metrics/PerceivedComplexity, Metrics/MethodLength
|
|
@@ -262,7 +256,7 @@ module Airbrake
|
|
|
262
256
|
array = bytes[start_idx..-1].unpack("G#{size}N#{size}")
|
|
263
257
|
means, counts = array.each_slice(size).to_a if array.any?
|
|
264
258
|
when SMALL_ENCODING
|
|
265
|
-
means = bytes[start_idx..(start_idx + 4 * size)].unpack("g#{size}")
|
|
259
|
+
means = bytes[start_idx..(start_idx + (4 * size))].unpack("g#{size}")
|
|
266
260
|
# Decode delta encoding of means
|
|
267
261
|
x = 0
|
|
268
262
|
means.map! do |m|
|
|
@@ -270,7 +264,7 @@ module Airbrake
|
|
|
270
264
|
x = m
|
|
271
265
|
m
|
|
272
266
|
end
|
|
273
|
-
counts_bytes = bytes[(start_idx + 4 * size)..-1].unpack('C*')
|
|
267
|
+
counts_bytes = bytes[(start_idx + (4 * size))..-1].unpack('C*')
|
|
274
268
|
counts = []
|
|
275
269
|
# Decode variable length integer bytes
|
|
276
270
|
size.times do
|
|
@@ -279,6 +273,7 @@ module Airbrake
|
|
|
279
273
|
shift = 7
|
|
280
274
|
while (v & 0x80) != 0
|
|
281
275
|
raise 'Shift too large in decode' if shift > 28
|
|
276
|
+
|
|
282
277
|
v = counts_bytes.shift || 0
|
|
283
278
|
z += (v & 0x7f) << shift
|
|
284
279
|
shift += 7
|
|
@@ -307,35 +302,35 @@ module Airbrake
|
|
|
307
302
|
|
|
308
303
|
private
|
|
309
304
|
|
|
310
|
-
def _add_weight(
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
305
|
+
def _add_weight(centroid, x, n)
|
|
306
|
+
unless x == centroid.mean
|
|
307
|
+
centroid.mean += n * (x - centroid.mean) / (centroid.n + n)
|
|
308
|
+
end
|
|
314
309
|
|
|
315
|
-
|
|
316
|
-
nearest.mean_cumn += n / 2.0
|
|
317
|
-
nearest.n += n
|
|
310
|
+
_cumulate(exact: false, force: true) if centroid.mean_cumn.nil?
|
|
318
311
|
|
|
319
|
-
|
|
312
|
+
centroid.cumn += n
|
|
313
|
+
centroid.mean_cumn += n / 2.0
|
|
314
|
+
centroid.n += n
|
|
320
315
|
end
|
|
321
316
|
|
|
322
317
|
# rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
|
323
|
-
def _cumulate(exact
|
|
318
|
+
def _cumulate(exact: false, force: false)
|
|
324
319
|
unless force
|
|
325
320
|
factor = if @last_cumulate == 0
|
|
326
321
|
Float::INFINITY
|
|
327
322
|
else
|
|
328
|
-
(@
|
|
323
|
+
(@size.to_f / @last_cumulate)
|
|
329
324
|
end
|
|
330
|
-
return if @
|
|
325
|
+
return if @size == @last_cumulate || (!exact && @cx && @cx > factor)
|
|
331
326
|
end
|
|
332
327
|
|
|
333
328
|
cumn = 0
|
|
334
|
-
@centroids.
|
|
335
|
-
c.mean_cumn = cumn + c.n / 2.0
|
|
329
|
+
@centroids.each_value do |c|
|
|
330
|
+
c.mean_cumn = cumn + (c.n / 2.0)
|
|
336
331
|
cumn = c.cumn = cumn + c.n
|
|
337
332
|
end
|
|
338
|
-
@
|
|
333
|
+
@size = @last_cumulate = cumn
|
|
339
334
|
nil
|
|
340
335
|
end
|
|
341
336
|
# rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
|
@@ -345,32 +340,29 @@ module Airbrake
|
|
|
345
340
|
def _digest(x, n)
|
|
346
341
|
# Use 'first' and 'last' instead of min/max because of performance reasons
|
|
347
342
|
# This works because RBTree is sorted
|
|
348
|
-
min = @centroids.first
|
|
349
|
-
max = @centroids.last
|
|
350
|
-
|
|
351
|
-
min = min.nil? ? nil : min[1]
|
|
352
|
-
max = max.nil? ? nil : max[1]
|
|
343
|
+
min = min.last if (min = @centroids.first)
|
|
344
|
+
max = max.last if (max = @centroids.last)
|
|
353
345
|
nearest = find_nearest(x)
|
|
354
346
|
|
|
355
|
-
@
|
|
347
|
+
@size += n
|
|
356
348
|
|
|
357
349
|
if nearest && nearest.mean == x
|
|
358
350
|
_add_weight(nearest, x, n)
|
|
359
351
|
elsif nearest == min
|
|
360
|
-
|
|
352
|
+
@centroids[x] = Centroid.new(x, n, 0)
|
|
361
353
|
elsif nearest == max
|
|
362
|
-
|
|
354
|
+
@centroids[x] = Centroid.new(x, n, @size)
|
|
363
355
|
else
|
|
364
|
-
p = nearest.mean_cumn.to_f / @
|
|
365
|
-
max_n = (4 * @
|
|
356
|
+
p = nearest.mean_cumn.to_f / @size
|
|
357
|
+
max_n = (4 * @size * @delta * p * (1 - p)).floor
|
|
366
358
|
if max_n - nearest.n >= n
|
|
367
359
|
_add_weight(nearest, x, n)
|
|
368
360
|
else
|
|
369
|
-
|
|
361
|
+
@centroids[x] = Centroid.new(x, n, nearest.cumn)
|
|
370
362
|
end
|
|
371
363
|
end
|
|
372
364
|
|
|
373
|
-
_cumulate(false)
|
|
365
|
+
_cumulate(exact: false)
|
|
374
366
|
|
|
375
367
|
# If the number of centroids has grown to a very large size,
|
|
376
368
|
# it may be due to values being inserted in sorted order.
|
|
@@ -382,12 +374,6 @@ module Airbrake
|
|
|
382
374
|
end
|
|
383
375
|
# rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity,
|
|
384
376
|
# rubocop:enable Metrics/AbcSize
|
|
385
|
-
|
|
386
|
-
def _new_centroid(x, n, cumn)
|
|
387
|
-
c = Centroid.new(x, n, cumn)
|
|
388
|
-
@centroids[x] = c
|
|
389
|
-
c
|
|
390
|
-
end
|
|
391
377
|
end
|
|
392
378
|
# rubocop:enable Metrics/ClassLength
|
|
393
379
|
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
module Airbrake
|
|
2
|
+
# ThreadPool implements a simple thread pool that can configure the number of
|
|
3
|
+
# worker threads and the size of the queue to process.
|
|
4
|
+
#
|
|
5
|
+
# @example
|
|
6
|
+
# # Initialize a new thread pool with 5 workers and a queue size of 100. Set
|
|
7
|
+
# # the block to be run concurrently.
|
|
8
|
+
# thread_pool = ThreadPool.new(
|
|
9
|
+
# name: 'performance-notifier',
|
|
10
|
+
# worker_size: 5,
|
|
11
|
+
# queue_size: 100,
|
|
12
|
+
# block: proc { |message| print "ECHO: #{message}..."}
|
|
13
|
+
# )
|
|
14
|
+
#
|
|
15
|
+
# # Send work.
|
|
16
|
+
# 10.times { |i| thread_pool << i }
|
|
17
|
+
# #=> ECHO: 0...ECHO: 1...ECHO: 2...
|
|
18
|
+
#
|
|
19
|
+
# @api private
|
|
20
|
+
# @since v4.6.1
|
|
21
|
+
class ThreadPool
|
|
22
|
+
include Loggable
|
|
23
|
+
|
|
24
|
+
# @return [ThreadGroup] the list of workers
|
|
25
|
+
# @note This is exposed for eaiser unit testing
|
|
26
|
+
attr_reader :workers
|
|
27
|
+
|
|
28
|
+
def initialize(worker_size:, queue_size:, block:, name: nil)
|
|
29
|
+
@name = name
|
|
30
|
+
@worker_size = worker_size
|
|
31
|
+
@queue_size = queue_size
|
|
32
|
+
@block = block
|
|
33
|
+
|
|
34
|
+
@queue = SizedQueue.new(queue_size)
|
|
35
|
+
@workers = ThreadGroup.new
|
|
36
|
+
@mutex = Mutex.new
|
|
37
|
+
@pid = nil
|
|
38
|
+
@closed = false
|
|
39
|
+
|
|
40
|
+
has_workers?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Adds a new message to the thread pool. Rejects messages if the queue is at
|
|
44
|
+
# its capacity.
|
|
45
|
+
#
|
|
46
|
+
# @param [Object] message The message that gets passed to the block
|
|
47
|
+
# @return [Boolean] true if the message was successfully sent to the pool,
|
|
48
|
+
# false if the queue is full
|
|
49
|
+
def <<(message)
|
|
50
|
+
if backlog >= @queue_size
|
|
51
|
+
logger.info do
|
|
52
|
+
"#{LOG_LABEL} ThreadPool has reached its capacity of " \
|
|
53
|
+
"#{@queue_size} and the following message will not be " \
|
|
54
|
+
"processed: #{message.inspect}"
|
|
55
|
+
end
|
|
56
|
+
return false
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
@queue << message
|
|
60
|
+
true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @return [Integer] how big the queue is at the moment
|
|
64
|
+
def backlog
|
|
65
|
+
@queue.size
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Checks if a thread pool has any workers. A thread pool doesn't have any
|
|
69
|
+
# workers only in two cases: when it was closed or when all workers
|
|
70
|
+
# crashed. An *active* thread pool doesn't have any workers only when
|
|
71
|
+
# something went wrong.
|
|
72
|
+
#
|
|
73
|
+
# Workers are expected to crash when you +fork+ the process the workers are
|
|
74
|
+
# living in. In this case we detect a +fork+ and try to revive them here.
|
|
75
|
+
#
|
|
76
|
+
# Another possible scenario that crashes workers is when you close the
|
|
77
|
+
# instance on +at_exit+, but some other +at_exit+ hook prevents the process
|
|
78
|
+
# from exiting.
|
|
79
|
+
#
|
|
80
|
+
# @return [Boolean] true if an instance wasn't closed, but has no workers
|
|
81
|
+
# @see https://goo.gl/oydz8h Example of at_exit that prevents exit
|
|
82
|
+
def has_workers?
|
|
83
|
+
@mutex.synchronize do
|
|
84
|
+
return false if @closed
|
|
85
|
+
|
|
86
|
+
if @pid != Process.pid && @workers.list.empty?
|
|
87
|
+
@pid = Process.pid
|
|
88
|
+
@workers = ThreadGroup.new
|
|
89
|
+
spawn_workers
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
!@closed && @workers.list.any?
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Closes the thread pool making it a no-op (it shut downs all worker
|
|
97
|
+
# threads). Before closing, waits on all unprocessed tasks to be processed.
|
|
98
|
+
#
|
|
99
|
+
# @return [void]
|
|
100
|
+
# @raise [Airbrake::Error] when invoked more than one time
|
|
101
|
+
def close
|
|
102
|
+
threads = @mutex.synchronize do
|
|
103
|
+
raise Airbrake::Error, 'this thread pool is closed already' if @closed
|
|
104
|
+
|
|
105
|
+
unless @queue.empty?
|
|
106
|
+
msg = "#{LOG_LABEL} waiting to process #{@queue.size} task(s)..."
|
|
107
|
+
logger.debug("#{msg} (Ctrl-C to abort)")
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
@worker_size.times { @queue << :stop }
|
|
111
|
+
@closed = true
|
|
112
|
+
@workers.list.dup
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
threads.each(&:join)
|
|
116
|
+
logger.debug("#{LOG_LABEL} #{@name} thread pool closed")
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def closed?
|
|
120
|
+
@closed
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def spawn_workers
|
|
124
|
+
@worker_size.times { @workers.add(spawn_worker) }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
def spawn_worker
|
|
130
|
+
Thread.new do
|
|
131
|
+
while (message = @queue.pop)
|
|
132
|
+
break if message == :stop
|
|
133
|
+
|
|
134
|
+
@block.call(message)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -6,10 +6,10 @@ module Airbrake
|
|
|
6
6
|
module TimeTruncate
|
|
7
7
|
# Truncate +time+ to floor minute and turn it into an RFC3339 timestamp.
|
|
8
8
|
#
|
|
9
|
-
# @param [Time] time
|
|
9
|
+
# @param [Time, Integer, Float] time
|
|
10
10
|
# @return [String]
|
|
11
11
|
def self.utc_truncate_minutes(time)
|
|
12
|
-
tm = time.getutc
|
|
12
|
+
tm = Time.at(time).getutc
|
|
13
13
|
|
|
14
14
|
Time.utc(tm.year, tm.month, tm.day, tm.hour, tm.min).to_datetime.rfc3339
|
|
15
15
|
end
|
|
@@ -12,6 +12,10 @@ module Airbrake
|
|
|
12
12
|
# strings with +ENCODING_OPTIONS+
|
|
13
13
|
TEMP_ENCODING = 'utf-16'.freeze
|
|
14
14
|
|
|
15
|
+
# @return [Array<Encoding>] encodings that are eligible for fixing invalid
|
|
16
|
+
# characters
|
|
17
|
+
SUPPORTED_ENCODINGS = [Encoding::UTF_8, Encoding::ASCII].freeze
|
|
18
|
+
|
|
15
19
|
# @return [String] what to append when something is a circular reference
|
|
16
20
|
CIRCULAR = '[Circular]'.freeze
|
|
17
21
|
|
|
@@ -35,6 +39,7 @@ module Airbrake
|
|
|
35
39
|
def truncate(object, seen = Set.new)
|
|
36
40
|
if seen.include?(object.object_id)
|
|
37
41
|
return CIRCULAR if CIRCULAR_TYPES.any? { |t| object.is_a?(t) }
|
|
42
|
+
|
|
38
43
|
return object
|
|
39
44
|
end
|
|
40
45
|
truncate_object(object, seen << object.object_id)
|
|
@@ -63,6 +68,7 @@ module Airbrake
|
|
|
63
68
|
def truncate_string(str)
|
|
64
69
|
fixed_str = replace_invalid_characters(str)
|
|
65
70
|
return fixed_str if fixed_str.length <= @max_size
|
|
71
|
+
|
|
66
72
|
(fixed_str.slice(0, @max_size) + TRUNCATED).freeze
|
|
67
73
|
end
|
|
68
74
|
|
|
@@ -76,6 +82,7 @@ module Airbrake
|
|
|
76
82
|
truncated_hash = {}
|
|
77
83
|
hash.each_with_index do |(key, val), idx|
|
|
78
84
|
break if idx + 1 > @max_size
|
|
85
|
+
|
|
79
86
|
truncated_hash[key] = truncate(val, seen)
|
|
80
87
|
end
|
|
81
88
|
|
|
@@ -103,13 +110,12 @@ module Airbrake
|
|
|
103
110
|
# @return [String] a UTF-8 encoded string
|
|
104
111
|
# @see https://github.com/flori/json/commit/3e158410e81f94dbbc3da6b7b35f4f64983aa4e3
|
|
105
112
|
def replace_invalid_characters(str)
|
|
106
|
-
|
|
107
|
-
utf8_string = (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII)
|
|
113
|
+
utf8_string = SUPPORTED_ENCODINGS.include?(str.encoding)
|
|
108
114
|
return str if utf8_string && str.valid_encoding?
|
|
109
115
|
|
|
110
116
|
temp_str = str.dup
|
|
111
|
-
temp_str.encode!(TEMP_ENCODING, ENCODING_OPTIONS) if utf8_string
|
|
112
|
-
temp_str.encode!('utf-8', ENCODING_OPTIONS)
|
|
117
|
+
temp_str.encode!(TEMP_ENCODING, **ENCODING_OPTIONS) if utf8_string
|
|
118
|
+
temp_str.encode!('utf-8', **ENCODING_OPTIONS)
|
|
113
119
|
end
|
|
114
120
|
end
|
|
115
121
|
end
|
|
@@ -2,5 +2,15 @@
|
|
|
2
2
|
# More information: http://semver.org/
|
|
3
3
|
module Airbrake
|
|
4
4
|
# @return [String] the library version
|
|
5
|
-
|
|
5
|
+
# @api public
|
|
6
|
+
AIRBRAKE_RUBY_VERSION = '6.2.2'.freeze
|
|
7
|
+
|
|
8
|
+
# @return [Hash{Symbol=>String}] the information about the notifier library
|
|
9
|
+
# @since v5.0.0
|
|
10
|
+
# @api public
|
|
11
|
+
NOTIFIER_INFO = {
|
|
12
|
+
name: 'airbrake-ruby'.freeze,
|
|
13
|
+
version: Airbrake::AIRBRAKE_RUBY_VERSION,
|
|
14
|
+
url: 'https://github.com/airbrake/airbrake-ruby'.freeze,
|
|
15
|
+
}.freeze
|
|
6
16
|
end
|