failbot 2.5.5 → 2.10.0
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/lib/failbot/compat.rb +3 -3
- data/lib/failbot/exception_format/structured.rb +2 -1
- data/lib/failbot/haystack.rb +6 -3
- data/lib/failbot/http_backend.rb +10 -3
- data/lib/failbot/json_backend.rb +1 -1
- data/lib/failbot/middleware.rb +10 -9
- data/lib/failbot/thread_local_variable.rb +27 -0
- data/lib/failbot/version.rb +1 -1
- data/lib/failbot.rb +163 -74
- metadata +25 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2261cff70dfa6a7d6a35eec441d3440055ab89166e3441d61d10ccbd7fe635d
|
4
|
+
data.tar.gz: a163db78583fa4ceb80be66e5edb2477fec732718ff9057d331075163f4a35d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 915c6f252c20266490c1d345bacf92a246dc352fd409126c260954bf65f569a8a8c05566883c592e68de12af3516722c0469526ac3824f52ad670c1dd161eb3a
|
7
|
+
data.tar.gz: '08e40f7a93557a96a8e9b6857eb3302781f360c47b914f587330a62599b7c6d6a9b93984d58c40e2287ec7d1de4bdcd5ddc15e1ed6b25486a7d26f6b240d7678'
|
data/lib/failbot/compat.rb
CHANGED
@@ -76,17 +76,17 @@ module Failbot
|
|
76
76
|
warn "#{caller[0]} Failbot.report_errors? is deprecated and will be " \
|
77
77
|
"removed in subsequent releases."
|
78
78
|
|
79
|
-
if @
|
79
|
+
if @thread_local_report_errors.nil?
|
80
80
|
config['report_errors']
|
81
81
|
else
|
82
|
-
@
|
82
|
+
@thread_local_report_errors.value
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
86
|
def report_errors=(v)
|
87
87
|
warn "#{caller[0]} Failbot.report_errors= is deprecated and will be " \
|
88
88
|
"removed in subsequent releases."
|
89
|
-
@
|
89
|
+
@thread_local_report_errors.value = v
|
90
90
|
end
|
91
91
|
|
92
92
|
# Load and initialize the exception reporting backend as specified by
|
@@ -14,7 +14,7 @@ module Failbot
|
|
14
14
|
def self.call(e)
|
15
15
|
message = e.message.to_s
|
16
16
|
class_name = e.class.to_s
|
17
|
-
|
17
|
+
begin
|
18
18
|
Failbot.backtrace_parser.call(e)
|
19
19
|
rescue => ex
|
20
20
|
message += "\nUnable to parse backtrace (#{ex.inspect})\nDon't put non-backtrace text in Exception#backtrace please!\nSo-called backtrace follows:\n#{e.backtrace.join("\n")}"
|
@@ -22,6 +22,7 @@ module Failbot
|
|
22
22
|
EMPTY_ARRAY
|
23
23
|
end
|
24
24
|
{
|
25
|
+
"platform" => "ruby",
|
25
26
|
"exception_detail" => exception_details(e),
|
26
27
|
"ruby" => RUBY_DESCRIPTION,
|
27
28
|
"created_at" => Time.now.utc.iso8601(6)
|
data/lib/failbot/haystack.rb
CHANGED
@@ -4,10 +4,11 @@ require 'json'
|
|
4
4
|
|
5
5
|
module Failbot
|
6
6
|
class Haystack
|
7
|
-
attr_accessor :connect_timeout
|
8
|
-
def initialize(url, connect_timeout=nil)
|
7
|
+
attr_accessor :connect_timeout, :rw_timeout
|
8
|
+
def initialize(url, connect_timeout=nil, timeout_seconds=nil)
|
9
9
|
@url = url
|
10
10
|
@connect_timeout = connect_timeout
|
11
|
+
@rw_timeout = timeout_seconds - @connect_timeout.to_f if timeout_seconds
|
11
12
|
end
|
12
13
|
|
13
14
|
def user
|
@@ -26,7 +27,7 @@ module Failbot
|
|
26
27
|
|
27
28
|
# Raise if the exception doesn't make it to Haystack, ensures the failure
|
28
29
|
# is logged
|
29
|
-
raise StandardError, "couldn't send exception to Haystack" unless response.code == "201"
|
30
|
+
raise StandardError, "couldn't send exception to Haystack: #{response.code} #{response.message}" unless response.code == "201"
|
30
31
|
end
|
31
32
|
|
32
33
|
def self.send_data(data)
|
@@ -54,6 +55,8 @@ module Failbot
|
|
54
55
|
|
55
56
|
# Set the connect timeout if it was provided
|
56
57
|
http.open_timeout = @connect_timeout if @connect_timeout
|
58
|
+
http.read_timeout = @rw_timeout if @rw_timeout
|
59
|
+
http.write_timeout = @rw_timeout if @rw_timeout
|
57
60
|
|
58
61
|
# push it through
|
59
62
|
http.request(request)
|
data/lib/failbot/http_backend.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
module Failbot
|
2
2
|
class HTTPBackend
|
3
|
-
def initialize(url, connect_timeout = nil)
|
3
|
+
def initialize(url, connect_timeout = nil, timeout_seconds = nil)
|
4
4
|
if url.to_s.empty?
|
5
5
|
raise ArgumentError, "FAILBOT_HAYSTACK_URL setting required."
|
6
6
|
end
|
7
7
|
|
8
|
-
@
|
9
|
-
@haystack = Failbot::Haystack.new(url, connect_timeout)
|
8
|
+
@haystack = Failbot::Haystack.new(url, connect_timeout, timeout_seconds)
|
10
9
|
end
|
11
10
|
|
12
11
|
def report(data)
|
@@ -28,5 +27,13 @@ module Failbot
|
|
28
27
|
def connect_timeout=(timeout)
|
29
28
|
@haystack.connect_timeout = timeout
|
30
29
|
end
|
30
|
+
|
31
|
+
def rw_timeout
|
32
|
+
@haystack.rw_timeout
|
33
|
+
end
|
34
|
+
|
35
|
+
def rw_timeout=(timeout)
|
36
|
+
@haystack.rw_timeout = timeout
|
37
|
+
end
|
31
38
|
end
|
32
39
|
end
|
data/lib/failbot/json_backend.rb
CHANGED
data/lib/failbot/middleware.rb
CHANGED
@@ -10,16 +10,17 @@ module Failbot
|
|
10
10
|
|
11
11
|
def call(env)
|
12
12
|
Failbot.reset!
|
13
|
-
Failbot.push @other.merge(self.class.context(env))
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
13
|
+
Failbot.push @other.merge(self.class.context(env))
|
14
|
+
begin
|
15
|
+
start = Time.now
|
16
|
+
@app.call(env)
|
17
|
+
rescue Object => boom
|
18
|
+
elapsed = Time.now - start
|
19
|
+
Failbot.report(boom, {:time => elapsed.to_s})
|
20
|
+
raise
|
22
21
|
end
|
22
|
+
ensure
|
23
|
+
Failbot.reset!
|
23
24
|
end
|
24
25
|
|
25
26
|
def self.context(env)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Failbot
|
2
|
+
# Public: A simplified implementation of [`::Concurrent::ThreadLocalVar`](https://github.com/ruby-concurrency/concurrent-ruby/blob/7dc6eb04142f008ffa79a59c125669c6fcbb85a8/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb)
|
3
|
+
#
|
4
|
+
# Why not just use `concurrent-ruby`? We wanted to minimize external dependencies to avoid conflicts with gems already installed with `github/github`.
|
5
|
+
#
|
6
|
+
class ThreadLocalVariable
|
7
|
+
def initialize(&block)
|
8
|
+
@default_block = block || proc {}
|
9
|
+
@key = "_thread_local_variable_#{object_id}".to_sym
|
10
|
+
end
|
11
|
+
|
12
|
+
def value
|
13
|
+
value_from_thread(Thread.current)
|
14
|
+
end
|
15
|
+
|
16
|
+
def value=(val)
|
17
|
+
Thread.current.thread_variable_set(@key, val)
|
18
|
+
end
|
19
|
+
|
20
|
+
def value_from_thread(thread)
|
21
|
+
if !thread.thread_variables.include?(@key)
|
22
|
+
thread.thread_variable_set(@key, @default_block.call)
|
23
|
+
end
|
24
|
+
thread.thread_variable_get(@key)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/failbot/version.rb
CHANGED
data/lib/failbot.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'yaml'
|
2
|
-
require 'digest/
|
2
|
+
require 'digest/sha2'
|
3
3
|
require 'logger'
|
4
4
|
require 'socket'
|
5
5
|
require "time"
|
@@ -28,6 +28,8 @@ module Failbot
|
|
28
28
|
autoload :JSONBackend, 'failbot/json_backend'
|
29
29
|
autoload :WaiterBackend, 'failbot/waiter_backend'
|
30
30
|
|
31
|
+
autoload :ThreadLocalVariable, 'failbot/thread_local_variable'
|
32
|
+
|
31
33
|
# Public: Set an instrumenter to be called when exceptions are reported.
|
32
34
|
#
|
33
35
|
# class CustomInstrumenter
|
@@ -46,9 +48,6 @@ module Failbot
|
|
46
48
|
#
|
47
49
|
attr_accessor :instrumenter
|
48
50
|
|
49
|
-
# prevent recursive calls to Failbot.report!
|
50
|
-
attr_accessor :already_reporting
|
51
|
-
|
52
51
|
# Root directory of the project's source. Used to clean up stack traces if the exception format supports it
|
53
52
|
|
54
53
|
def source_root=(str)
|
@@ -90,7 +89,7 @@ module Failbot
|
|
90
89
|
|
91
90
|
# Default to the original exception format used by haystack unless
|
92
91
|
# apps opt in to the newer version.
|
93
|
-
self.exception_format = :
|
92
|
+
self.exception_format = :structured
|
94
93
|
|
95
94
|
# Set a callable that is responsible for parsing and formatting ruby
|
96
95
|
# backtraces. This is only necessary to set if your app deals with
|
@@ -137,9 +136,16 @@ module Failbot
|
|
137
136
|
return setup_deprecated(settings)
|
138
137
|
end
|
139
138
|
|
140
|
-
if default_context.respond_to?(:to_hash) && !default_context.to_hash.empty?
|
141
|
-
|
139
|
+
initial_context = if default_context.respond_to?(:to_hash) && !default_context.to_hash.empty?
|
140
|
+
default_context.to_hash
|
141
|
+
else
|
142
|
+
{ 'server' => hostname }
|
143
|
+
end
|
144
|
+
|
145
|
+
@thread_local_context = ::Failbot::ThreadLocalVariable.new do
|
146
|
+
[initial_context]
|
142
147
|
end
|
148
|
+
@thread_local_already_reporting = ::Failbot::ThreadLocalVariable.new { false }
|
143
149
|
|
144
150
|
populate_context_from_settings(settings)
|
145
151
|
@enable_timeout = false
|
@@ -164,7 +170,7 @@ module Failbot
|
|
164
170
|
when "file"
|
165
171
|
Failbot::FileBackend.new(settings["FAILBOT_BACKEND_FILE_PATH"])
|
166
172
|
when "http"
|
167
|
-
Failbot::HTTPBackend.new(URI(settings["FAILBOT_HAYSTACK_URL"]), @connect_timeout_seconds)
|
173
|
+
Failbot::HTTPBackend.new(URI(settings["FAILBOT_HAYSTACK_URL"]), @connect_timeout_seconds, @timeout_seconds)
|
168
174
|
when 'json'
|
169
175
|
Failbot::JSONBackend.new(settings["FAILBOT_BACKEND_JSON_HOST"], settings["FAILBOT_BACKEND_JSON_PORT"])
|
170
176
|
when 'console'
|
@@ -174,7 +180,9 @@ module Failbot
|
|
174
180
|
end
|
175
181
|
|
176
182
|
@raise_errors = !settings["FAILBOT_RAISE"].to_s.empty?
|
177
|
-
@
|
183
|
+
@thread_local_report_errors = ::Failbot::ThreadLocalVariable.new do
|
184
|
+
settings["FAILBOT_REPORT"] != "0"
|
185
|
+
end
|
178
186
|
|
179
187
|
# allows overriding the 'app' value to send to single haystack bucket.
|
180
188
|
# used primarily on ghe.io.
|
@@ -184,6 +192,10 @@ module Failbot
|
|
184
192
|
if settings["FAILBOT_EXCEPTION_FORMAT"]
|
185
193
|
self.exception_format = settings["FAILBOT_EXCEPTION_FORMAT"].to_sym
|
186
194
|
end
|
195
|
+
|
196
|
+
@ignored_error_classes = settings.fetch("FAILBOT_IGNORED_ERROR_CLASSES", "").split(",").map do |class_name|
|
197
|
+
Module.const_get(class_name.strip)
|
198
|
+
end
|
187
199
|
end
|
188
200
|
|
189
201
|
# Bring in deprecated methods
|
@@ -195,7 +207,7 @@ module Failbot
|
|
195
207
|
# hashes are condensed down into one and included in the next report. Don't
|
196
208
|
# mess with this structure directly - use the #push and #pop methods.
|
197
209
|
def context
|
198
|
-
@
|
210
|
+
@thread_local_context.value
|
199
211
|
end
|
200
212
|
|
201
213
|
# Add info to be sent in the next failbot report, should one occur.
|
@@ -206,6 +218,11 @@ module Failbot
|
|
206
218
|
#
|
207
219
|
# Returns the value returned by the block when given; otherwise, returns nil.
|
208
220
|
def push(info={})
|
221
|
+
info.each do |key, value|
|
222
|
+
if value.kind_of?(Proc)
|
223
|
+
raise ArgumentError, "Proc usage has been removed from Failbot"
|
224
|
+
end
|
225
|
+
end
|
209
226
|
context.push(info)
|
210
227
|
yield if block_given?
|
211
228
|
ensure
|
@@ -219,7 +236,7 @@ module Failbot
|
|
219
236
|
|
220
237
|
# Reset the context stack to a pristine state.
|
221
238
|
def reset!
|
222
|
-
@
|
239
|
+
@thread_local_context.value = [context[0]].dup
|
223
240
|
end
|
224
241
|
|
225
242
|
# Loops through the stack of contexts and deletes the given key if it exists.
|
@@ -280,13 +297,13 @@ module Failbot
|
|
280
297
|
# Default rollup for an exception. Exceptions with the same rollup are
|
281
298
|
# grouped together in Haystack. The rollup is an MD5 hash of the exception
|
282
299
|
# class and the raising file, line, and method.
|
283
|
-
DEFAULT_ROLLUP = lambda do |exception, context|
|
300
|
+
DEFAULT_ROLLUP = lambda do |exception, context, thread|
|
284
301
|
backtrace_line = Array(exception.backtrace).first || ""
|
285
302
|
# We want all exceptions from foo.html.erb:123 to be grouped together, no
|
286
303
|
# matter what wacky generated ERB method name is attached to it.
|
287
304
|
cleaned_line = backtrace_line.sub(/_erb__[_\d]+'\z/, "_erb'")
|
288
305
|
|
289
|
-
Digest::
|
306
|
+
Digest::SHA256.hexdigest("#{exception.class}#{cleaned_line}")
|
290
307
|
end
|
291
308
|
|
292
309
|
private_constant :DEFAULT_ROLLUP
|
@@ -315,6 +332,8 @@ module Failbot
|
|
315
332
|
#
|
316
333
|
# Returns nothing.
|
317
334
|
def report(e, other = {})
|
335
|
+
return if ignore_error?(e)
|
336
|
+
|
318
337
|
if @raise_errors
|
319
338
|
squash_contexts(context, exception_info(e), other) # surface problems squashing
|
320
339
|
raise e
|
@@ -324,59 +343,22 @@ module Failbot
|
|
324
343
|
end
|
325
344
|
|
326
345
|
def report!(e, other = {})
|
327
|
-
|
346
|
+
report_with_context!(Thread.current, context, e, other)
|
347
|
+
end
|
328
348
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
349
|
+
def report_from_thread(thread, e, other = {})
|
350
|
+
if @raise_errors
|
351
|
+
squash_contexts(@thread_local_context.value_from_thread(thread), exception_info(e), other) # surface problems squashing
|
352
|
+
raise e
|
353
|
+
else
|
354
|
+
report_from_thread!(thread, e, other)
|
334
355
|
end
|
335
|
-
|
336
|
-
|
337
|
-
begin
|
338
|
-
data = squash_contexts(context, exception_info(e), other)
|
339
|
-
|
340
|
-
if !data.has_key?("rollup")
|
341
|
-
data = data.merge("rollup" => @rollup.call(e, data))
|
342
|
-
end
|
343
|
-
|
344
|
-
if @before_report
|
345
|
-
data = squash_contexts(data, @before_report.call(e, data))
|
346
|
-
end
|
347
|
-
|
348
|
-
if @app_override
|
349
|
-
data = data.merge("app" => @app_override)
|
350
|
-
end
|
356
|
+
end
|
351
357
|
|
352
|
-
|
353
|
-
|
354
|
-
log_failure("processing", data, e, i)
|
355
|
-
self.already_reporting = false
|
356
|
-
return
|
357
|
-
end
|
358
|
+
def report_from_thread!(thread, e, other = {})
|
359
|
+
return if ignore_error?(e)
|
358
360
|
|
359
|
-
|
360
|
-
instrumentation_data = {
|
361
|
-
"report_status" => "error",
|
362
|
-
}
|
363
|
-
begin
|
364
|
-
if @enable_timeout
|
365
|
-
Timeout.timeout(@timeout_seconds) do
|
366
|
-
backend.report(data)
|
367
|
-
end
|
368
|
-
else
|
369
|
-
backend.report(data)
|
370
|
-
end
|
371
|
-
instrumentation_data["report_status"] = "success"
|
372
|
-
rescue Object => i
|
373
|
-
log_failure("reporting", data, e, i)
|
374
|
-
instrumentation_data["exception_type"] = i.class.name
|
375
|
-
ensure
|
376
|
-
instrumentation_data["elapsed_ms"] = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).to_i
|
377
|
-
instrument("report.failbot", data.merge(instrumentation_data)) rescue nil
|
378
|
-
self.already_reporting = false
|
379
|
-
end
|
361
|
+
report_with_context!(thread, @thread_local_context.value_from_thread(thread), e, other)
|
380
362
|
end
|
381
363
|
|
382
364
|
# Public: Disable exception reporting. This is equivalent to calling
|
@@ -389,14 +371,14 @@ module Failbot
|
|
389
371
|
# block - an optional block to perform while reporting is disabled. If a block
|
390
372
|
# is passed, reporting will be re-enabled after the block is called.
|
391
373
|
def disable(&block)
|
392
|
-
original_report_errors = @
|
393
|
-
@
|
374
|
+
original_report_errors = @thread_local_report_errors.value
|
375
|
+
@thread_local_report_errors.value = false
|
394
376
|
|
395
377
|
if block
|
396
378
|
begin
|
397
379
|
block.call
|
398
380
|
ensure
|
399
|
-
@
|
381
|
+
@thread_local_report_errors.value = original_report_errors
|
400
382
|
end
|
401
383
|
end
|
402
384
|
end
|
@@ -405,7 +387,7 @@ module Failbot
|
|
405
387
|
# this can be called if it is explicitly disabled by calling `Failbot.disable`
|
406
388
|
# or setting `FAILBOT_REPORTING => "0"` in `Failbot.setup`.
|
407
389
|
def enable
|
408
|
-
@
|
390
|
+
@thread_local_report_errors.value = true
|
409
391
|
end
|
410
392
|
|
411
393
|
# Public: exceptions that were reported. Only available when using the
|
@@ -429,7 +411,6 @@ module Failbot
|
|
429
411
|
|
430
412
|
contexts_to_squash.flatten.each do |hash|
|
431
413
|
hash.each do |key, value|
|
432
|
-
value = (value.call rescue nil) if value.kind_of?(Proc)
|
433
414
|
squashed[key.to_s] = value
|
434
415
|
end
|
435
416
|
end
|
@@ -451,6 +432,8 @@ module Failbot
|
|
451
432
|
value
|
452
433
|
when String, true, false
|
453
434
|
value.to_s
|
435
|
+
when Proc
|
436
|
+
"proc usage is deprecated"
|
454
437
|
when Array
|
455
438
|
if key == EXCEPTION_DETAIL
|
456
439
|
# special-casing for the exception_detail key, which is allowed to
|
@@ -492,7 +475,16 @@ module Failbot
|
|
492
475
|
end
|
493
476
|
|
494
477
|
def logger
|
495
|
-
@logger ||= Logger.new($stderr
|
478
|
+
@logger ||= Logger.new($stderr, formatter: proc { |severity, datetime, progname, msg|
|
479
|
+
log = case msg
|
480
|
+
when Hash
|
481
|
+
msg.map { |k,v| "#{k}=#{v.inspect}" }.join(" ")
|
482
|
+
else
|
483
|
+
%Q|msg="#{msg.inspect}"|
|
484
|
+
end
|
485
|
+
log_line = %Q|ts="#{datetime.utc.iso8601}" level=#{severity} logger=Failbot #{log}\n|
|
486
|
+
log_line.lstrip
|
487
|
+
})
|
496
488
|
end
|
497
489
|
|
498
490
|
def logger=(logger)
|
@@ -503,8 +495,73 @@ module Failbot
|
|
503
495
|
@hostname ||= Socket.gethostname
|
504
496
|
end
|
505
497
|
|
498
|
+
def already_reporting=(bool)
|
499
|
+
@thread_local_already_reporting.value = bool
|
500
|
+
end
|
501
|
+
|
502
|
+
def already_reporting
|
503
|
+
@thread_local_already_reporting.value
|
504
|
+
end
|
505
|
+
|
506
506
|
private
|
507
507
|
|
508
|
+
def report_with_context!(thread, provided_context, e, other = {})
|
509
|
+
return unless @thread_local_report_errors.value
|
510
|
+
return if ignore_error?(e)
|
511
|
+
|
512
|
+
if already_reporting
|
513
|
+
logger.warn "FAILBOT: asked to report while reporting!" rescue nil
|
514
|
+
logger.warn e.message rescue nil
|
515
|
+
logger.warn e.backtrace.join("\n") rescue nil
|
516
|
+
return
|
517
|
+
end
|
518
|
+
self.already_reporting = true
|
519
|
+
|
520
|
+
begin
|
521
|
+
data = squash_contexts(provided_context, exception_info(e), other)
|
522
|
+
|
523
|
+
if !data.has_key?("rollup")
|
524
|
+
data = data.merge("rollup" => @rollup.call(e, data, thread))
|
525
|
+
end
|
526
|
+
|
527
|
+
if defined?(@before_report) && @before_report
|
528
|
+
data = squash_contexts(data, @before_report.call(e, data, thread))
|
529
|
+
end
|
530
|
+
|
531
|
+
if @app_override
|
532
|
+
data = data.merge("app" => @app_override)
|
533
|
+
end
|
534
|
+
|
535
|
+
data = scrub(sanitize(data))
|
536
|
+
rescue Object => i
|
537
|
+
log_failure("processing", data, e, i)
|
538
|
+
self.already_reporting = false
|
539
|
+
return
|
540
|
+
end
|
541
|
+
|
542
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
543
|
+
instrumentation_data = {
|
544
|
+
"report_status" => "error",
|
545
|
+
}
|
546
|
+
begin
|
547
|
+
if @enable_timeout
|
548
|
+
Timeout.timeout(@timeout_seconds) do
|
549
|
+
backend.report(data)
|
550
|
+
end
|
551
|
+
else
|
552
|
+
backend.report(data)
|
553
|
+
end
|
554
|
+
instrumentation_data["report_status"] = "success"
|
555
|
+
rescue Object => i
|
556
|
+
log_failure("reporting", data, e, i)
|
557
|
+
instrumentation_data["exception_type"] = i.class.name
|
558
|
+
ensure
|
559
|
+
instrumentation_data["elapsed_ms"] = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).to_i
|
560
|
+
instrument("report.failbot", data.merge(instrumentation_data)) rescue nil
|
561
|
+
self.already_reporting = false
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
508
565
|
# Internal: Publish an event to the instrumenter
|
509
566
|
def instrument(name, payload = {})
|
510
567
|
Failbot.instrumenter.instrument(name, payload) if Failbot.instrumenter
|
@@ -522,12 +579,44 @@ module Failbot
|
|
522
579
|
end
|
523
580
|
|
524
581
|
def log_failure(action, data, original_exception, exception)
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
582
|
+
begin
|
583
|
+
record = {
|
584
|
+
"msg" => "exception",
|
585
|
+
"action" => action,
|
586
|
+
"data" => data,
|
587
|
+
}
|
588
|
+
|
589
|
+
record.merge!(to_semconv(exception))
|
590
|
+
logger.debug record
|
591
|
+
|
592
|
+
record = {
|
593
|
+
"msg" => "report-failed",
|
594
|
+
"action" => action,
|
595
|
+
"data" => data,
|
596
|
+
}
|
597
|
+
record.merge!(to_semconv(original_exception))
|
598
|
+
logger.debug record
|
599
|
+
rescue => e
|
600
|
+
raise e
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
def to_semconv(exception)
|
605
|
+
{
|
606
|
+
"exception.type" => exception.class.to_s,
|
607
|
+
"exception.message" => exception.message.encode("UTF-8", invalid: :replace, undef: :replace, replace: '�'),
|
608
|
+
"exception.backtrace" => exception.full_message(highlight: false, order: :top).encode('UTF-8', invalid: :replace, undef: :replace, replace: '�'),
|
609
|
+
}
|
610
|
+
end
|
611
|
+
|
612
|
+
def ignore_error?(error)
|
613
|
+
@cache ||= Hash.new do |hash, error_class|
|
614
|
+
hash[error_class] = @ignored_error_classes.any? do |ignored_error_class|
|
615
|
+
error_class.ancestors.include?(ignored_error_class)
|
616
|
+
end
|
617
|
+
end
|
618
|
+
|
619
|
+
@cache[error.class]
|
531
620
|
end
|
532
621
|
|
533
622
|
extend self
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: failbot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- "@rtomayko"
|
8
8
|
- "@atmos"
|
9
9
|
- "@sr"
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2022-02-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rake
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
requirements:
|
19
|
-
- - "
|
19
|
+
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: '10.0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
|
-
- - "
|
26
|
+
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: '10.0'
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: rack
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
|
-
- - "
|
33
|
+
- - ">="
|
34
34
|
- !ruby/object:Gem::Version
|
35
35
|
version: 1.6.4
|
36
36
|
type: :development
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
|
-
- - "
|
40
|
+
- - ">="
|
41
41
|
- !ruby/object:Gem::Version
|
42
42
|
version: 1.6.4
|
43
43
|
- !ruby/object:Gem::Dependency
|
@@ -82,6 +82,20 @@ dependencies:
|
|
82
82
|
- - ">="
|
83
83
|
- !ruby/object:Gem::Version
|
84
84
|
version: '0.6'
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: webmock
|
87
|
+
requirement: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '3.0'
|
92
|
+
type: :development
|
93
|
+
prerelease: false
|
94
|
+
version_requirements: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '3.0'
|
85
99
|
description: "..."
|
86
100
|
email:
|
87
101
|
- github+failbot@lists.github.com
|
@@ -106,13 +120,14 @@ files:
|
|
106
120
|
- lib/failbot/middleware.rb
|
107
121
|
- lib/failbot/resque_failure_backend.rb
|
108
122
|
- lib/failbot/sensitive_data_scrubber.rb
|
123
|
+
- lib/failbot/thread_local_variable.rb
|
109
124
|
- lib/failbot/version.rb
|
110
125
|
- lib/failbot/waiter_backend.rb
|
111
126
|
homepage: http://github.com/github/failbot#readme
|
112
127
|
licenses:
|
113
128
|
- MIT
|
114
129
|
metadata: {}
|
115
|
-
post_install_message:
|
130
|
+
post_install_message:
|
116
131
|
rdoc_options: []
|
117
132
|
require_paths:
|
118
133
|
- lib
|
@@ -127,8 +142,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
127
142
|
- !ruby/object:Gem::Version
|
128
143
|
version: '0'
|
129
144
|
requirements: []
|
130
|
-
rubygems_version: 3.
|
131
|
-
signing_key:
|
145
|
+
rubygems_version: 3.1.6
|
146
|
+
signing_key:
|
132
147
|
specification_version: 4
|
133
148
|
summary: Deliver exceptions to Haystack
|
134
149
|
test_files: []
|