failbot 2.5.2 → 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 +14 -2
- data/lib/failbot/http_backend.rb +18 -2
- data/lib/failbot/json_backend.rb +1 -1
- data/lib/failbot/middleware.rb +10 -9
- data/lib/failbot/thread_local_variable.rb +10 -7
- data/lib/failbot/version.rb +1 -1
- data/lib/failbot/waiter_backend.rb +21 -0
- data/lib/failbot.rb +160 -62
- metadata +22 -7
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,8 +4,11 @@ require 'json'
|
|
4
4
|
|
5
5
|
module Failbot
|
6
6
|
class Haystack
|
7
|
-
|
7
|
+
attr_accessor :connect_timeout, :rw_timeout
|
8
|
+
def initialize(url, connect_timeout=nil, timeout_seconds=nil)
|
8
9
|
@url = url
|
10
|
+
@connect_timeout = connect_timeout
|
11
|
+
@rw_timeout = timeout_seconds - @connect_timeout.to_f if timeout_seconds
|
9
12
|
end
|
10
13
|
|
11
14
|
def user
|
@@ -24,7 +27,7 @@ module Failbot
|
|
24
27
|
|
25
28
|
# Raise if the exception doesn't make it to Haystack, ensures the failure
|
26
29
|
# is logged
|
27
|
-
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"
|
28
31
|
end
|
29
32
|
|
30
33
|
def self.send_data(data)
|
@@ -50,8 +53,17 @@ module Failbot
|
|
50
53
|
# use SSL if applicable
|
51
54
|
http.use_ssl = true if @url.scheme == "https"
|
52
55
|
|
56
|
+
# Set the connect timeout if it was provided
|
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
|
60
|
+
|
53
61
|
# push it through
|
54
62
|
http.request(request)
|
63
|
+
ensure
|
64
|
+
if defined?(http) && http.started?
|
65
|
+
http.finish
|
66
|
+
end
|
55
67
|
end
|
56
68
|
end
|
57
69
|
end
|
data/lib/failbot/http_backend.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Failbot
|
2
2
|
class HTTPBackend
|
3
|
-
def initialize(url)
|
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
|
-
@haystack = Failbot::Haystack.new(url)
|
8
|
+
@haystack = Failbot::Haystack.new(url, connect_timeout, timeout_seconds)
|
9
9
|
end
|
10
10
|
|
11
11
|
def report(data)
|
@@ -19,5 +19,21 @@ module Failbot
|
|
19
19
|
def ping
|
20
20
|
@haystack.ping
|
21
21
|
end
|
22
|
+
|
23
|
+
def connect_timeout
|
24
|
+
@haystack.connect_timeout
|
25
|
+
end
|
26
|
+
|
27
|
+
def connect_timeout=(timeout)
|
28
|
+
@haystack.connect_timeout = timeout
|
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
|
22
38
|
end
|
23
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)
|
@@ -6,19 +6,22 @@ module Failbot
|
|
6
6
|
class ThreadLocalVariable
|
7
7
|
def initialize(&block)
|
8
8
|
@default_block = block || proc {}
|
9
|
-
@key = "
|
9
|
+
@key = "_thread_local_variable_#{object_id}".to_sym
|
10
10
|
end
|
11
11
|
|
12
12
|
def value
|
13
|
-
|
14
|
-
Thread.current[@key]
|
15
|
-
else
|
16
|
-
Thread.current[@key] = @default_block.call
|
17
|
-
end
|
13
|
+
value_from_thread(Thread.current)
|
18
14
|
end
|
19
15
|
|
20
16
|
def value=(val)
|
21
|
-
Thread.current
|
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)
|
22
25
|
end
|
23
26
|
end
|
24
27
|
end
|
data/lib/failbot/version.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Failbot
|
2
|
+
class WaiterBackend
|
3
|
+
# This backend waits a configured amount of time before returning. This is
|
4
|
+
# intended be used to test timeouts. Delay is the number of seconds to wait.
|
5
|
+
|
6
|
+
attr_reader :reports
|
7
|
+
def initialize(delay = 5)
|
8
|
+
@delay = delay
|
9
|
+
@reports = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def report(data)
|
13
|
+
@reports << data
|
14
|
+
sleep(@delay)
|
15
|
+
end
|
16
|
+
|
17
|
+
def ping
|
18
|
+
# nop
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/failbot.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'yaml'
|
2
|
-
require 'digest/
|
2
|
+
require 'digest/sha2'
|
3
3
|
require 'logger'
|
4
4
|
require 'socket'
|
5
5
|
require "time"
|
6
|
+
require "timeout"
|
6
7
|
require "date"
|
7
8
|
require "uri"
|
8
9
|
|
@@ -25,6 +26,7 @@ module Failbot
|
|
25
26
|
autoload :HTTPBackend, 'failbot/http_backend'
|
26
27
|
autoload :MemoryBackend, 'failbot/memory_backend'
|
27
28
|
autoload :JSONBackend, 'failbot/json_backend'
|
29
|
+
autoload :WaiterBackend, 'failbot/waiter_backend'
|
28
30
|
|
29
31
|
autoload :ThreadLocalVariable, 'failbot/thread_local_variable'
|
30
32
|
|
@@ -87,7 +89,7 @@ module Failbot
|
|
87
89
|
|
88
90
|
# Default to the original exception format used by haystack unless
|
89
91
|
# apps opt in to the newer version.
|
90
|
-
self.exception_format = :
|
92
|
+
self.exception_format = :structured
|
91
93
|
|
92
94
|
# Set a callable that is responsible for parsing and formatting ruby
|
93
95
|
# backtraces. This is only necessary to set if your app deals with
|
@@ -146,15 +148,29 @@ module Failbot
|
|
146
148
|
@thread_local_already_reporting = ::Failbot::ThreadLocalVariable.new { false }
|
147
149
|
|
148
150
|
populate_context_from_settings(settings)
|
151
|
+
@enable_timeout = false
|
152
|
+
if settings.key?("FAILBOT_TIMEOUT_MS")
|
153
|
+
@timeout_seconds = settings["FAILBOT_TIMEOUT_MS"].to_f / 1000
|
154
|
+
@enable_timeout = (@timeout_seconds > 0.0)
|
155
|
+
end
|
156
|
+
|
157
|
+
@connect_timeout_seconds = nil
|
158
|
+
if settings.key?("FAILBOT_CONNECT_TIMEOUT_MS")
|
159
|
+
@connect_timeout_seconds = settings["FAILBOT_CONNECT_TIMEOUT_MS"].to_f / 1000
|
160
|
+
# unset the value if it's not parsing to something valid
|
161
|
+
@connect_timeout_seconds = nil unless @connect_timeout_seconds > 0
|
162
|
+
end
|
149
163
|
|
150
164
|
self.backend =
|
151
165
|
case (name = settings["FAILBOT_BACKEND"])
|
152
166
|
when "memory"
|
153
167
|
Failbot::MemoryBackend.new
|
168
|
+
when "waiter"
|
169
|
+
Failbot::WaiterBackend.new
|
154
170
|
when "file"
|
155
171
|
Failbot::FileBackend.new(settings["FAILBOT_BACKEND_FILE_PATH"])
|
156
172
|
when "http"
|
157
|
-
Failbot::HTTPBackend.new(URI(settings["FAILBOT_HAYSTACK_URL"]))
|
173
|
+
Failbot::HTTPBackend.new(URI(settings["FAILBOT_HAYSTACK_URL"]), @connect_timeout_seconds, @timeout_seconds)
|
158
174
|
when 'json'
|
159
175
|
Failbot::JSONBackend.new(settings["FAILBOT_BACKEND_JSON_HOST"], settings["FAILBOT_BACKEND_JSON_PORT"])
|
160
176
|
when 'console'
|
@@ -164,7 +180,9 @@ module Failbot
|
|
164
180
|
end
|
165
181
|
|
166
182
|
@raise_errors = !settings["FAILBOT_RAISE"].to_s.empty?
|
167
|
-
@
|
183
|
+
@thread_local_report_errors = ::Failbot::ThreadLocalVariable.new do
|
184
|
+
settings["FAILBOT_REPORT"] != "0"
|
185
|
+
end
|
168
186
|
|
169
187
|
# allows overriding the 'app' value to send to single haystack bucket.
|
170
188
|
# used primarily on ghe.io.
|
@@ -174,6 +192,10 @@ module Failbot
|
|
174
192
|
if settings["FAILBOT_EXCEPTION_FORMAT"]
|
175
193
|
self.exception_format = settings["FAILBOT_EXCEPTION_FORMAT"].to_sym
|
176
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
|
177
199
|
end
|
178
200
|
|
179
201
|
# Bring in deprecated methods
|
@@ -196,6 +218,11 @@ module Failbot
|
|
196
218
|
#
|
197
219
|
# Returns the value returned by the block when given; otherwise, returns nil.
|
198
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
|
199
226
|
context.push(info)
|
200
227
|
yield if block_given?
|
201
228
|
ensure
|
@@ -270,13 +297,13 @@ module Failbot
|
|
270
297
|
# Default rollup for an exception. Exceptions with the same rollup are
|
271
298
|
# grouped together in Haystack. The rollup is an MD5 hash of the exception
|
272
299
|
# class and the raising file, line, and method.
|
273
|
-
DEFAULT_ROLLUP = lambda do |exception, context|
|
300
|
+
DEFAULT_ROLLUP = lambda do |exception, context, thread|
|
274
301
|
backtrace_line = Array(exception.backtrace).first || ""
|
275
302
|
# We want all exceptions from foo.html.erb:123 to be grouped together, no
|
276
303
|
# matter what wacky generated ERB method name is attached to it.
|
277
304
|
cleaned_line = backtrace_line.sub(/_erb__[_\d]+'\z/, "_erb'")
|
278
305
|
|
279
|
-
Digest::
|
306
|
+
Digest::SHA256.hexdigest("#{exception.class}#{cleaned_line}")
|
280
307
|
end
|
281
308
|
|
282
309
|
private_constant :DEFAULT_ROLLUP
|
@@ -305,6 +332,8 @@ module Failbot
|
|
305
332
|
#
|
306
333
|
# Returns nothing.
|
307
334
|
def report(e, other = {})
|
335
|
+
return if ignore_error?(e)
|
336
|
+
|
308
337
|
if @raise_errors
|
309
338
|
squash_contexts(context, exception_info(e), other) # surface problems squashing
|
310
339
|
raise e
|
@@ -314,53 +343,22 @@ module Failbot
|
|
314
343
|
end
|
315
344
|
|
316
345
|
def report!(e, other = {})
|
317
|
-
|
346
|
+
report_with_context!(Thread.current, context, e, other)
|
347
|
+
end
|
318
348
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
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)
|
324
355
|
end
|
325
|
-
|
326
|
-
|
327
|
-
begin
|
328
|
-
data = squash_contexts(context, exception_info(e), other)
|
329
|
-
|
330
|
-
if !data.has_key?("rollup")
|
331
|
-
data = data.merge("rollup" => @rollup.call(e, data))
|
332
|
-
end
|
333
|
-
|
334
|
-
if @before_report
|
335
|
-
data = squash_contexts(data, @before_report.call(e, data))
|
336
|
-
end
|
337
|
-
|
338
|
-
if @app_override
|
339
|
-
data = data.merge("app" => @app_override)
|
340
|
-
end
|
356
|
+
end
|
341
357
|
|
342
|
-
|
343
|
-
|
344
|
-
log_failure("processing", data, e, i)
|
345
|
-
self.already_reporting = false
|
346
|
-
return
|
347
|
-
end
|
358
|
+
def report_from_thread!(thread, e, other = {})
|
359
|
+
return if ignore_error?(e)
|
348
360
|
|
349
|
-
|
350
|
-
instrumentation_data = {
|
351
|
-
"report_status" => "error",
|
352
|
-
}
|
353
|
-
begin
|
354
|
-
backend.report(data)
|
355
|
-
instrumentation_data["report_status"] = "success"
|
356
|
-
rescue Object => i
|
357
|
-
log_failure("reporting", data, e, i)
|
358
|
-
instrumentation_data["exception_type"] = i.class.name
|
359
|
-
ensure
|
360
|
-
instrumentation_data["elapsed_ms"] = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).to_i
|
361
|
-
instrument("report.failbot", data.merge(instrumentation_data)) rescue nil
|
362
|
-
self.already_reporting = false
|
363
|
-
end
|
361
|
+
report_with_context!(thread, @thread_local_context.value_from_thread(thread), e, other)
|
364
362
|
end
|
365
363
|
|
366
364
|
# Public: Disable exception reporting. This is equivalent to calling
|
@@ -373,14 +371,14 @@ module Failbot
|
|
373
371
|
# block - an optional block to perform while reporting is disabled. If a block
|
374
372
|
# is passed, reporting will be re-enabled after the block is called.
|
375
373
|
def disable(&block)
|
376
|
-
original_report_errors = @
|
377
|
-
@
|
374
|
+
original_report_errors = @thread_local_report_errors.value
|
375
|
+
@thread_local_report_errors.value = false
|
378
376
|
|
379
377
|
if block
|
380
378
|
begin
|
381
379
|
block.call
|
382
380
|
ensure
|
383
|
-
@
|
381
|
+
@thread_local_report_errors.value = original_report_errors
|
384
382
|
end
|
385
383
|
end
|
386
384
|
end
|
@@ -389,7 +387,7 @@ module Failbot
|
|
389
387
|
# this can be called if it is explicitly disabled by calling `Failbot.disable`
|
390
388
|
# or setting `FAILBOT_REPORTING => "0"` in `Failbot.setup`.
|
391
389
|
def enable
|
392
|
-
@
|
390
|
+
@thread_local_report_errors.value = true
|
393
391
|
end
|
394
392
|
|
395
393
|
# Public: exceptions that were reported. Only available when using the
|
@@ -413,7 +411,6 @@ module Failbot
|
|
413
411
|
|
414
412
|
contexts_to_squash.flatten.each do |hash|
|
415
413
|
hash.each do |key, value|
|
416
|
-
value = (value.call rescue nil) if value.kind_of?(Proc)
|
417
414
|
squashed[key.to_s] = value
|
418
415
|
end
|
419
416
|
end
|
@@ -435,6 +432,8 @@ module Failbot
|
|
435
432
|
value
|
436
433
|
when String, true, false
|
437
434
|
value.to_s
|
435
|
+
when Proc
|
436
|
+
"proc usage is deprecated"
|
438
437
|
when Array
|
439
438
|
if key == EXCEPTION_DETAIL
|
440
439
|
# special-casing for the exception_detail key, which is allowed to
|
@@ -476,7 +475,16 @@ module Failbot
|
|
476
475
|
end
|
477
476
|
|
478
477
|
def logger
|
479
|
-
@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
|
+
})
|
480
488
|
end
|
481
489
|
|
482
490
|
def logger=(logger)
|
@@ -497,6 +505,63 @@ module Failbot
|
|
497
505
|
|
498
506
|
private
|
499
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
|
+
|
500
565
|
# Internal: Publish an event to the instrumenter
|
501
566
|
def instrument(name, payload = {})
|
502
567
|
Failbot.instrumenter.instrument(name, payload) if Failbot.instrumenter
|
@@ -507,18 +572,51 @@ module Failbot
|
|
507
572
|
def populate_context_from_settings(settings)
|
508
573
|
settings.each do |key, value|
|
509
574
|
if /\AFAILBOT_CONTEXT_(.+)\z/ =~ key
|
510
|
-
|
575
|
+
key = $1.downcase
|
576
|
+
context[0][key] = value unless context[0][key]
|
511
577
|
end
|
512
578
|
end
|
513
579
|
end
|
514
580
|
|
515
581
|
def log_failure(action, data, original_exception, exception)
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
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]
|
522
620
|
end
|
523
621
|
|
524
622
|
extend self
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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"
|
@@ -10,34 +10,34 @@ authors:
|
|
10
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
|
@@ -108,6 +122,7 @@ files:
|
|
108
122
|
- lib/failbot/sensitive_data_scrubber.rb
|
109
123
|
- lib/failbot/thread_local_variable.rb
|
110
124
|
- lib/failbot/version.rb
|
125
|
+
- lib/failbot/waiter_backend.rb
|
111
126
|
homepage: http://github.com/github/failbot#readme
|
112
127
|
licenses:
|
113
128
|
- MIT
|
@@ -127,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
127
142
|
- !ruby/object:Gem::Version
|
128
143
|
version: '0'
|
129
144
|
requirements: []
|
130
|
-
rubygems_version: 3.
|
145
|
+
rubygems_version: 3.1.6
|
131
146
|
signing_key:
|
132
147
|
specification_version: 4
|
133
148
|
summary: Deliver exceptions to Haystack
|