failbot 2.4.3 → 2.5.5
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 +55 -10
- data/lib/failbot/exception_format/haystack.rb +1 -7
- data/lib/failbot/exception_format/structured.rb +36 -7
- data/lib/failbot/haystack.rb +10 -1
- data/lib/failbot/http_backend.rb +11 -2
- data/lib/failbot/memory_backend.rb +1 -4
- data/lib/failbot/version.rb +1 -1
- data/lib/failbot/waiter_backend.rb +21 -0
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef2b30021fd84983545c9fa374d030d9c1269d3ef51b778cf84d55fe96fde087
|
4
|
+
data.tar.gz: 998dde878c99c47177d03ff693625e05f1522009d84bf4e5f1a12a0fb398ad0e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 774ccebb4f0b04886309e32840d73f17b0278388a73aa6a15541870c791a832f68c767b140ed1d62527feff87ae880aedebf60e942946c103116103c32bda9be
|
7
|
+
data.tar.gz: 778c39eea60f8acdd0b53246401351e1373bf9386259fde67adfb86c0956baa8780fdda6afcf01f942ee6327aa8672390ec57d699e93ade429c8123b6f5ef3a6
|
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
|
|
@@ -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
|
# Public: Set an instrumenter to be called when exceptions are reported.
|
30
32
|
#
|
@@ -67,6 +69,11 @@ module Failbot
|
|
67
69
|
|
68
70
|
EXCEPTION_DETAIL = 'exception_detail'
|
69
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
|
+
|
70
77
|
# Enumerates the available exception formats this gem supports. The original
|
71
78
|
# format and the default is :haystack and the newer format is :structured
|
72
79
|
EXCEPTION_FORMATS = {
|
@@ -135,15 +142,29 @@ module Failbot
|
|
135
142
|
end
|
136
143
|
|
137
144
|
populate_context_from_settings(settings)
|
145
|
+
@enable_timeout = false
|
146
|
+
if settings.key?("FAILBOT_TIMEOUT_MS")
|
147
|
+
@timeout_seconds = settings["FAILBOT_TIMEOUT_MS"].to_f / 1000
|
148
|
+
@enable_timeout = (@timeout_seconds > 0.0)
|
149
|
+
end
|
150
|
+
|
151
|
+
@connect_timeout_seconds = nil
|
152
|
+
if settings.key?("FAILBOT_CONNECT_TIMEOUT_MS")
|
153
|
+
@connect_timeout_seconds = settings["FAILBOT_CONNECT_TIMEOUT_MS"].to_f / 1000
|
154
|
+
# unset the value if it's not parsing to something valid
|
155
|
+
@connect_timeout_seconds = nil unless @connect_timeout_seconds > 0
|
156
|
+
end
|
138
157
|
|
139
158
|
self.backend =
|
140
159
|
case (name = settings["FAILBOT_BACKEND"])
|
141
160
|
when "memory"
|
142
161
|
Failbot::MemoryBackend.new
|
162
|
+
when "waiter"
|
163
|
+
Failbot::WaiterBackend.new
|
143
164
|
when "file"
|
144
165
|
Failbot::FileBackend.new(settings["FAILBOT_BACKEND_FILE_PATH"])
|
145
166
|
when "http"
|
146
|
-
Failbot::HTTPBackend.new(URI(settings["FAILBOT_HAYSTACK_URL"]))
|
167
|
+
Failbot::HTTPBackend.new(URI(settings["FAILBOT_HAYSTACK_URL"]), @connect_timeout_seconds)
|
147
168
|
when 'json'
|
148
169
|
Failbot::JSONBackend.new(settings["FAILBOT_BACKEND_JSON_HOST"], settings["FAILBOT_BACKEND_JSON_PORT"])
|
149
170
|
when 'console'
|
@@ -329,17 +350,31 @@ module Failbot
|
|
329
350
|
end
|
330
351
|
|
331
352
|
data = scrub(sanitize(data))
|
353
|
+
rescue Object => i
|
354
|
+
log_failure("processing", data, e, i)
|
355
|
+
self.already_reporting = false
|
356
|
+
return
|
357
|
+
end
|
332
358
|
|
333
|
-
|
334
|
-
|
359
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
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"
|
335
372
|
rescue Object => i
|
336
|
-
|
337
|
-
|
338
|
-
logger.debug e.message rescue nil
|
339
|
-
logger.debug e.backtrace.join("\n") rescue nil
|
340
|
-
logger.debug i.message rescue nil
|
341
|
-
logger.debug i.backtrace.join("\n") rescue nil
|
373
|
+
log_failure("reporting", data, e, i)
|
374
|
+
instrumentation_data["exception_type"] = i.class.name
|
342
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
|
343
378
|
self.already_reporting = false
|
344
379
|
end
|
345
380
|
end
|
@@ -480,11 +515,21 @@ module Failbot
|
|
480
515
|
def populate_context_from_settings(settings)
|
481
516
|
settings.each do |key, value|
|
482
517
|
if /\AFAILBOT_CONTEXT_(.+)\z/ =~ key
|
483
|
-
|
518
|
+
key = $1.downcase
|
519
|
+
context[0][key] = value unless context[0][key]
|
484
520
|
end
|
485
521
|
end
|
486
522
|
end
|
487
523
|
|
524
|
+
def log_failure(action, data, original_exception, exception)
|
525
|
+
# don't fail for any reason
|
526
|
+
logger.debug "FAILBOT EXCEPTION: action=#{action} exception=#{exception.class.name} original_type: #{original_exception.class.name} data=#{data.inspect}" rescue nil
|
527
|
+
logger.debug original_exception.message rescue nil
|
528
|
+
logger.debug original_exception.backtrace.join("\n") rescue nil
|
529
|
+
logger.debug exception.message rescue nil
|
530
|
+
logger.debug exception.backtrace.join("\n") rescue nil
|
531
|
+
end
|
532
|
+
|
488
533
|
extend self
|
489
534
|
|
490
535
|
# If the library was lazy loaded due to failbot/exit_hook.rb and a delayed
|
@@ -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
|
|
@@ -22,18 +22,47 @@ module Failbot
|
|
22
22
|
EMPTY_ARRAY
|
23
23
|
end
|
24
24
|
{
|
25
|
-
"exception_detail" =>
|
26
|
-
{ # hashes generated from subsequent calls to Exception#cause.
|
27
|
-
"type" => class_name,
|
28
|
-
"value" => message,
|
29
|
-
"stacktrace" => stacktrace
|
30
|
-
}
|
31
|
-
],
|
25
|
+
"exception_detail" => exception_details(e),
|
32
26
|
"ruby" => RUBY_DESCRIPTION,
|
33
27
|
"created_at" => Time.now.utc.iso8601(6)
|
34
28
|
}
|
35
29
|
end
|
36
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
|
+
|
37
66
|
# given a hash generated by this class, return the exception message.
|
38
67
|
def self.exception_message_from_hash(hash)
|
39
68
|
hash.dig("exception_detail", 0, "value")
|
data/lib/failbot/haystack.rb
CHANGED
@@ -4,8 +4,10 @@ require 'json'
|
|
4
4
|
|
5
5
|
module Failbot
|
6
6
|
class Haystack
|
7
|
-
|
7
|
+
attr_accessor :connect_timeout
|
8
|
+
def initialize(url, connect_timeout=nil)
|
8
9
|
@url = url
|
10
|
+
@connect_timeout = connect_timeout
|
9
11
|
end
|
10
12
|
|
11
13
|
def user
|
@@ -50,8 +52,15 @@ module Failbot
|
|
50
52
|
# use SSL if applicable
|
51
53
|
http.use_ssl = true if @url.scheme == "https"
|
52
54
|
|
55
|
+
# Set the connect timeout if it was provided
|
56
|
+
http.open_timeout = @connect_timeout if @connect_timeout
|
57
|
+
|
53
58
|
# push it through
|
54
59
|
http.request(request)
|
60
|
+
ensure
|
61
|
+
if defined?(http) && http.started?
|
62
|
+
http.finish
|
63
|
+
end
|
55
64
|
end
|
56
65
|
end
|
57
66
|
end
|
data/lib/failbot/http_backend.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
module Failbot
|
2
2
|
class HTTPBackend
|
3
|
-
def initialize(url)
|
3
|
+
def initialize(url, connect_timeout = nil)
|
4
4
|
if url.to_s.empty?
|
5
5
|
raise ArgumentError, "FAILBOT_HAYSTACK_URL setting required."
|
6
6
|
end
|
7
7
|
|
8
|
-
@
|
8
|
+
@connect_timeout = connect_timeout
|
9
|
+
@haystack = Failbot::Haystack.new(url, connect_timeout)
|
9
10
|
end
|
10
11
|
|
11
12
|
def report(data)
|
@@ -19,5 +20,13 @@ module Failbot
|
|
19
20
|
def ping
|
20
21
|
@haystack.ping
|
21
22
|
end
|
23
|
+
|
24
|
+
def connect_timeout
|
25
|
+
@haystack.connect_timeout
|
26
|
+
end
|
27
|
+
|
28
|
+
def connect_timeout=(timeout)
|
29
|
+
@haystack.connect_timeout = timeout
|
30
|
+
end
|
22
31
|
end
|
23
32
|
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
|
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
|
+
version: 2.5.5
|
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-03
|
13
|
+
date: 2020-09-03 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rake
|
@@ -107,11 +107,12 @@ files:
|
|
107
107
|
- lib/failbot/resque_failure_backend.rb
|
108
108
|
- lib/failbot/sensitive_data_scrubber.rb
|
109
109
|
- lib/failbot/version.rb
|
110
|
+
- lib/failbot/waiter_backend.rb
|
110
111
|
homepage: http://github.com/github/failbot#readme
|
111
112
|
licenses:
|
112
113
|
- MIT
|
113
114
|
metadata: {}
|
114
|
-
post_install_message:
|
115
|
+
post_install_message:
|
115
116
|
rdoc_options: []
|
116
117
|
require_paths:
|
117
118
|
- lib
|
@@ -119,15 +120,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
119
120
|
requirements:
|
120
121
|
- - ">="
|
121
122
|
- !ruby/object:Gem::Version
|
122
|
-
version: '
|
123
|
+
version: '2.4'
|
123
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
125
|
requirements:
|
125
126
|
- - ">="
|
126
127
|
- !ruby/object:Gem::Version
|
127
|
-
version:
|
128
|
+
version: '0'
|
128
129
|
requirements: []
|
129
130
|
rubygems_version: 3.0.3
|
130
|
-
signing_key:
|
131
|
+
signing_key:
|
131
132
|
specification_version: 4
|
132
133
|
summary: Deliver exceptions to Haystack
|
133
134
|
test_files: []
|