failbot 2.4.2 → 2.5.4
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.rb +67 -8
- data/lib/failbot/default_backtrace_parser.rb +23 -0
- data/lib/failbot/exception_format/haystack.rb +1 -7
- data/lib/failbot/exception_format/structured.rb +41 -23
- data/lib/failbot/haystack.rb +4 -0
- data/lib/failbot/memory_backend.rb +1 -4
- data/lib/failbot/version.rb +1 -1
- data/lib/failbot/waiter_backend.rb +21 -0
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1bae1044f6d88c6617750ffe63b606591880ebe5132d9714d2e4c9534e309962
|
4
|
+
data.tar.gz: b1d80185dae5e664c049ed38583d25086d3659f517826c38912974d93fed32a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8114032ca98920f4b902447d1b3c69b28ea6bfe14b52d8b1f431d0537295d5c565d9d8a17540afa72f00f439023bc711291542baa2044ada4cd6f547e9506c35
|
7
|
+
data.tar.gz: f0040811958da514da048dd38af5de2cfd3978d6f036dc78c19b6080991b02bf69cca5b79f684ecc3a8c5b3885b577ecf77116fca445439f7ecfa06fbe5a3452
|
data/lib/failbot.rb
CHANGED
@@ -3,6 +3,7 @@ require 'digest/md5'
|
|
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
|
|
@@ -11,6 +12,7 @@ require "failbot/compat"
|
|
11
12
|
require "failbot/sensitive_data_scrubber"
|
12
13
|
require "failbot/exception_format/haystack"
|
13
14
|
require "failbot/exception_format/structured"
|
15
|
+
require "failbot/default_backtrace_parser"
|
14
16
|
|
15
17
|
# Failbot asynchronously takes exceptions and reports them to the
|
16
18
|
# exception logger du jour. Keeps the main app from failing or lagging if
|
@@ -24,6 +26,7 @@ module Failbot
|
|
24
26
|
autoload :HTTPBackend, 'failbot/http_backend'
|
25
27
|
autoload :MemoryBackend, 'failbot/memory_backend'
|
26
28
|
autoload :JSONBackend, 'failbot/json_backend'
|
29
|
+
autoload :WaiterBackend, 'failbot/waiter_backend'
|
27
30
|
|
28
31
|
# Public: Set an instrumenter to be called when exceptions are reported.
|
29
32
|
#
|
@@ -66,6 +69,11 @@ module Failbot
|
|
66
69
|
|
67
70
|
EXCEPTION_DETAIL = 'exception_detail'
|
68
71
|
|
72
|
+
# We'll include this many nested Exception#cause objects in the needle
|
73
|
+
# context. We limit the number of objects to prevent excessive recursion and
|
74
|
+
# large needle contexts.
|
75
|
+
MAXIMUM_CAUSE_DEPTH = 2
|
76
|
+
|
69
77
|
# Enumerates the available exception formats this gem supports. The original
|
70
78
|
# format and the default is :haystack and the newer format is :structured
|
71
79
|
EXCEPTION_FORMATS = {
|
@@ -84,6 +92,27 @@ module Failbot
|
|
84
92
|
# apps opt in to the newer version.
|
85
93
|
self.exception_format = :haystack
|
86
94
|
|
95
|
+
# Set a callable that is responsible for parsing and formatting ruby
|
96
|
+
# backtraces. This is only necessary to set if your app deals with
|
97
|
+
# exceptions that are manipulated to contain something other than actual
|
98
|
+
# stackframe strings in the format produced by `caller`. The argument passed
|
99
|
+
# must respond to `call` with an arity of 1. The callable expects to be
|
100
|
+
# passed Exception instances as its argument.
|
101
|
+
def self.backtrace_parser=(callable)
|
102
|
+
unless callable.respond_to?(:call)
|
103
|
+
raise ArgumentError, "backtrace_parser= passed #{callable.inspect}, which is not callable"
|
104
|
+
end
|
105
|
+
if callable.method(:call).arity != 1
|
106
|
+
raise ArgumentError, "backtrace_parser= passed #{callable.inspect}, whose `#call` has arity =! 1"
|
107
|
+
end
|
108
|
+
@backtrace_parser = callable
|
109
|
+
end
|
110
|
+
|
111
|
+
attr_reader :backtrace_parser
|
112
|
+
|
113
|
+
# Default backtrace parser is provided:
|
114
|
+
self.backtrace_parser = ::Failbot::DefaultBacktraceParser
|
115
|
+
|
87
116
|
# Helpers needed to parse hashes included in e.g. Failbot.reports.
|
88
117
|
def self.exception_message_from_hash(hash)
|
89
118
|
@exception_formatter.exception_message_from_hash(hash)
|
@@ -118,6 +147,8 @@ module Failbot
|
|
118
147
|
case (name = settings["FAILBOT_BACKEND"])
|
119
148
|
when "memory"
|
120
149
|
Failbot::MemoryBackend.new
|
150
|
+
when "waiter"
|
151
|
+
Failbot::WaiterBackend.new
|
121
152
|
when "file"
|
122
153
|
Failbot::FileBackend.new(settings["FAILBOT_BACKEND_FILE_PATH"])
|
123
154
|
when "http"
|
@@ -132,6 +163,11 @@ module Failbot
|
|
132
163
|
|
133
164
|
@raise_errors = !settings["FAILBOT_RAISE"].to_s.empty?
|
134
165
|
@report_errors = settings["FAILBOT_REPORT"] != "0"
|
166
|
+
@enable_timeout = false
|
167
|
+
if settings.key?("FAILBOT_TIMEOUT_MS")
|
168
|
+
@timeout_seconds = settings["FAILBOT_TIMEOUT_MS"].to_f / 1000
|
169
|
+
@enable_timeout = (@timeout_seconds != 0.0)
|
170
|
+
end
|
135
171
|
|
136
172
|
# allows overriding the 'app' value to send to single haystack bucket.
|
137
173
|
# used primarily on ghe.io.
|
@@ -307,17 +343,31 @@ module Failbot
|
|
307
343
|
end
|
308
344
|
|
309
345
|
data = scrub(sanitize(data))
|
346
|
+
rescue Object => i
|
347
|
+
log_failure("processing", data, e, i)
|
348
|
+
self.already_reporting = false
|
349
|
+
return
|
350
|
+
end
|
310
351
|
|
311
|
-
|
312
|
-
|
352
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
353
|
+
instrumentation_data = {
|
354
|
+
"report_status" => "error",
|
355
|
+
}
|
356
|
+
begin
|
357
|
+
if @enable_timeout
|
358
|
+
Timeout.timeout(@timeout_seconds) do
|
359
|
+
backend.report(data)
|
360
|
+
end
|
361
|
+
else
|
362
|
+
backend.report(data)
|
363
|
+
end
|
364
|
+
instrumentation_data["report_status"] = "success"
|
313
365
|
rescue Object => i
|
314
|
-
|
315
|
-
|
316
|
-
logger.debug e.message rescue nil
|
317
|
-
logger.debug e.backtrace.join("\n") rescue nil
|
318
|
-
logger.debug i.message rescue nil
|
319
|
-
logger.debug i.backtrace.join("\n") rescue nil
|
366
|
+
log_failure("reporting", data, e, i)
|
367
|
+
instrumentation_data["exception_type"] = i.class.name
|
320
368
|
ensure
|
369
|
+
instrumentation_data["elapsed_ms"] = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).to_i
|
370
|
+
instrument("report.failbot", data.merge(instrumentation_data)) rescue nil
|
321
371
|
self.already_reporting = false
|
322
372
|
end
|
323
373
|
end
|
@@ -463,6 +513,15 @@ module Failbot
|
|
463
513
|
end
|
464
514
|
end
|
465
515
|
|
516
|
+
def log_failure(action, data, original_exception, exception)
|
517
|
+
# don't fail for any reason
|
518
|
+
logger.debug "FAILBOT EXCEPTION: action=#{action} exception=#{exception.class.name} original_type: #{original_exception.class.name} data=#{data.inspect}" rescue nil
|
519
|
+
logger.debug original_exception.message rescue nil
|
520
|
+
logger.debug original_exception.backtrace.join("\n") rescue nil
|
521
|
+
logger.debug exception.message rescue nil
|
522
|
+
logger.debug exception.backtrace.join("\n") rescue nil
|
523
|
+
end
|
524
|
+
|
466
525
|
extend self
|
467
526
|
|
468
527
|
# If the library was lazy loaded due to failbot/exit_hook.rb and a delayed
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Failbot
|
2
|
+
# This is the default backtrace parser for the Structured formatter.
|
3
|
+
module DefaultBacktraceParser
|
4
|
+
EMPTY_ARRAY = [].freeze
|
5
|
+
|
6
|
+
# Takes an Exception instance, returns an array of hashes with the keys
|
7
|
+
# that the Structured formatter expects.
|
8
|
+
def self.call(exception)
|
9
|
+
if exception.backtrace
|
10
|
+
Backtrace.parse(exception.backtrace).frames.reverse.map do |line|
|
11
|
+
{
|
12
|
+
"filename" => line.file_name,
|
13
|
+
"abs_path" => line.abs_path,
|
14
|
+
"lineno" => line.line_number,
|
15
|
+
"function" => line.method
|
16
|
+
}
|
17
|
+
end
|
18
|
+
else
|
19
|
+
EMPTY_ARRAY
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -30,11 +30,6 @@ module Failbot
|
|
30
30
|
hash["class"]
|
31
31
|
end
|
32
32
|
|
33
|
-
# We'll include this many nested Exception#cause objects in the needle
|
34
|
-
# context. We limit the number of objects to prevent excessive recursion and
|
35
|
-
# large needle contexts.
|
36
|
-
MAXIMUM_CAUSE_DEPTH = 2
|
37
|
-
|
38
33
|
# Pretty-print Exception#cause (and nested causes) for inclusion in needle
|
39
34
|
# context
|
40
35
|
#
|
@@ -59,7 +54,7 @@ module Failbot
|
|
59
54
|
|
60
55
|
result = causes.join("\n\nCAUSED BY:\n\n")
|
61
56
|
|
62
|
-
if current.
|
57
|
+
if current.cause
|
63
58
|
result << "\n\nFurther #cause backtraces were omitted\n"
|
64
59
|
end
|
65
60
|
|
@@ -72,7 +67,6 @@ module Failbot
|
|
72
67
|
#
|
73
68
|
# Returns a String.
|
74
69
|
def self.pretty_print_one_cause(e)
|
75
|
-
return unless e.respond_to?(:cause)
|
76
70
|
cause = e.cause
|
77
71
|
return unless cause
|
78
72
|
|
@@ -13,25 +13,56 @@ module Failbot
|
|
13
13
|
# Format an exception.
|
14
14
|
def self.call(e)
|
15
15
|
message = e.message.to_s
|
16
|
+
class_name = e.class.to_s
|
16
17
|
stacktrace = begin
|
17
|
-
|
18
|
-
rescue
|
19
|
-
message += "\nUnable to parse backtrace
|
18
|
+
Failbot.backtrace_parser.call(e)
|
19
|
+
rescue => ex
|
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")}"
|
21
|
+
class_name += " (backtrace failed to parse)"
|
20
22
|
EMPTY_ARRAY
|
21
23
|
end
|
22
24
|
{
|
23
|
-
"exception_detail" =>
|
24
|
-
{ # hashes generated from subsequent calls to Exception#cause.
|
25
|
-
"type" => e.class.to_s,
|
26
|
-
"value" => message,
|
27
|
-
"stacktrace" => stacktrace
|
28
|
-
}
|
29
|
-
],
|
25
|
+
"exception_detail" => exception_details(e),
|
30
26
|
"ruby" => RUBY_DESCRIPTION,
|
31
27
|
"created_at" => Time.now.utc.iso8601(6)
|
32
28
|
}
|
33
29
|
end
|
34
30
|
|
31
|
+
FURTHER_CAUSES_WERE_OMITTED = {
|
32
|
+
"type" => "Notice",
|
33
|
+
"value" => "further Exception#cause values were omitted",
|
34
|
+
"stacktrace" => EMPTY_ARRAY
|
35
|
+
}.freeze
|
36
|
+
|
37
|
+
def self.exception_details(e)
|
38
|
+
result = []
|
39
|
+
depth = 0
|
40
|
+
|
41
|
+
loop do
|
42
|
+
message = e.message.to_s
|
43
|
+
class_name = e.class.to_s
|
44
|
+
stacktrace = begin
|
45
|
+
Failbot.backtrace_parser.call(e)
|
46
|
+
rescue => ex
|
47
|
+
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")}"
|
48
|
+
class_name += " (backtrace failed to parse)"
|
49
|
+
EMPTY_ARRAY
|
50
|
+
end
|
51
|
+
result.unshift({
|
52
|
+
"type" => class_name,
|
53
|
+
"value" => message,
|
54
|
+
"stacktrace" => stacktrace
|
55
|
+
})
|
56
|
+
depth += 1
|
57
|
+
break unless (e=e.cause)
|
58
|
+
if depth > MAXIMUM_CAUSE_DEPTH
|
59
|
+
result.unshift(FURTHER_CAUSES_WERE_OMITTED)
|
60
|
+
break
|
61
|
+
end
|
62
|
+
end
|
63
|
+
result
|
64
|
+
end
|
65
|
+
|
35
66
|
# given a hash generated by this class, return the exception message.
|
36
67
|
def self.exception_message_from_hash(hash)
|
37
68
|
hash.dig("exception_detail", 0, "value")
|
@@ -41,19 +72,6 @@ module Failbot
|
|
41
72
|
def self.exception_classname_from_hash(hash)
|
42
73
|
hash.dig("exception_detail", 0, "type")
|
43
74
|
end
|
44
|
-
|
45
|
-
def self.formatted_backtrace(e)
|
46
|
-
if e.backtrace
|
47
|
-
Backtrace.parse(e.backtrace).frames.reverse.map do |line|
|
48
|
-
{
|
49
|
-
"filename" => line.file_name,
|
50
|
-
"abs_path" => line.abs_path,
|
51
|
-
"lineno" => line.line_number,
|
52
|
-
"function" => line.method
|
53
|
-
}
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
75
|
end
|
58
76
|
end
|
59
77
|
end
|
data/lib/failbot/haystack.rb
CHANGED
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
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: failbot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.4
|
4
|
+
version: 2.5.4
|
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: 2020-
|
13
|
+
date: 2020-08-17 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rake
|
@@ -93,6 +93,7 @@ files:
|
|
93
93
|
- lib/failbot/backtrace.rb
|
94
94
|
- lib/failbot/compat.rb
|
95
95
|
- lib/failbot/console_backend.rb
|
96
|
+
- lib/failbot/default_backtrace_parser.rb
|
96
97
|
- lib/failbot/exception_format/haystack.rb
|
97
98
|
- lib/failbot/exception_format/structured.rb
|
98
99
|
- lib/failbot/exit_hook.rb
|
@@ -106,11 +107,12 @@ files:
|
|
106
107
|
- lib/failbot/resque_failure_backend.rb
|
107
108
|
- lib/failbot/sensitive_data_scrubber.rb
|
108
109
|
- lib/failbot/version.rb
|
110
|
+
- lib/failbot/waiter_backend.rb
|
109
111
|
homepage: http://github.com/github/failbot#readme
|
110
112
|
licenses:
|
111
113
|
- MIT
|
112
114
|
metadata: {}
|
113
|
-
post_install_message:
|
115
|
+
post_install_message:
|
114
116
|
rdoc_options: []
|
115
117
|
require_paths:
|
116
118
|
- lib
|
@@ -118,15 +120,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
118
120
|
requirements:
|
119
121
|
- - ">="
|
120
122
|
- !ruby/object:Gem::Version
|
121
|
-
version: '
|
123
|
+
version: '2.4'
|
122
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
125
|
requirements:
|
124
126
|
- - ">="
|
125
127
|
- !ruby/object:Gem::Version
|
126
|
-
version:
|
128
|
+
version: '0'
|
127
129
|
requirements: []
|
128
130
|
rubygems_version: 3.0.3
|
129
|
-
signing_key:
|
131
|
+
signing_key:
|
130
132
|
specification_version: 4
|
131
133
|
summary: Deliver exceptions to Haystack
|
132
134
|
test_files: []
|