failbot 2.5.2 → 2.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|