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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5d1f28105f9f590c7c9160004bfa4a15101da5a1372ee0e40e882c493dad5fd
4
- data.tar.gz: 5b09392f7d2243343a7982c6ae031f80d8501eb946d687490be6ad88e7321b1f
3
+ metadata.gz: ef2b30021fd84983545c9fa374d030d9c1269d3ef51b778cf84d55fe96fde087
4
+ data.tar.gz: 998dde878c99c47177d03ff693625e05f1522009d84bf4e5f1a12a0fb398ad0e
5
5
  SHA512:
6
- metadata.gz: 011af8c261c0826685972133268b52b85dcd358c402ee662575ef853b36f7c8ea6ff527673f2ec1210316d9cefc053aa8a20b119bfd55c09cf9666cbd6a0eeb8
7
- data.tar.gz: 58bc429f5b2e27ef75f570eb2d194d2e956dc935bc5ec471042a0029ae8e14a929ee486abe25d2bd9e52ee9f052eccec559b18f31bf3ee292441e51820880bf1
6
+ metadata.gz: 774ccebb4f0b04886309e32840d73f17b0278388a73aa6a15541870c791a832f68c767b140ed1d62527feff87ae880aedebf60e942946c103116103c32bda9be
7
+ data.tar.gz: 778c39eea60f8acdd0b53246401351e1373bf9386259fde67adfb86c0956baa8780fdda6afcf01f942ee6327aa8672390ec57d699e93ade429c8123b6f5ef3a6
@@ -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
- backend.report(data)
334
- instrument("report.failbot", data)
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
- # don't fail for any reason
337
- logger.debug "FAILBOT: #{data.inspect}" rescue nil
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
- context[0][$1.downcase] = value
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.respond_to?(:cause) && current.cause
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" => [ # TODO Once supported in failbotg, this should be an array of
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")
@@ -4,8 +4,10 @@ require 'json'
4
4
 
5
5
  module Failbot
6
6
  class Haystack
7
- def initialize(url)
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
@@ -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
- @haystack = Failbot::Haystack.new(url)
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
@@ -12,11 +12,8 @@ module Failbot
12
12
  end
13
13
 
14
14
  def report(data)
15
- if @fail
16
- fail
17
- end
18
-
19
15
  @reports << data
16
+ fail if @fail
20
17
  end
21
18
 
22
19
  def ping
@@ -1,3 +1,3 @@
1
1
  module Failbot
2
- VERSION = "2.4.3"
2
+ VERSION = "2.5.5"
3
3
  end
@@ -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.3
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-24 00:00:00.000000000 Z
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: '0'
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: 1.3.6
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: []