failbot 2.5.5 → 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 +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: []
|