failbot 2.4.3 → 2.5.5
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|