honeybadger 4.4.2 → 4.5.0
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/CHANGELOG.md +16 -0
- data/lib/honeybadger/agent.rb +1 -1
- data/lib/honeybadger/config/defaults.rb +1 -1
- data/lib/honeybadger/notice.rb +39 -12
- data/lib/honeybadger/version.rb +1 -1
- data/lib/honeybadger/worker.rb +88 -75
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7cfde073c6afe9ef4757d7aaa4d86913ffb178a8d65907f0c872f1a55467c8bb
|
4
|
+
data.tar.gz: 66b637f1a022ddb27fe6ed8b64af4781d2a90bb7cdb644df349885614e56184b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e4414164bbb59e6d82ff0d4ce1be1e9630f3c3fe8a35ca419740e899d0623d3375e9ef681fb402e4d5286fa01b9aac55e3190ad85f34467c9323404a6ea8fb4a
|
7
|
+
data.tar.gz: 3e8ffe0c2f9a877ca9abdf910ca556e6f9e11e6e865482807e6f03581e1c8ffc6a811c4629e91e613e037060014b589970a40910eb9758418ec2bcfe041e2e4b
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,22 @@ adheres to [Semantic Versioning](http://semver.org/).
|
|
5
5
|
|
6
6
|
## [Unreleased]
|
7
7
|
|
8
|
+
## [4.5.0] - 2019-08-05
|
9
|
+
### Changed
|
10
|
+
- Default `max_queue_size` has been reduced from 1000 to 100.
|
11
|
+
|
12
|
+
### Added
|
13
|
+
- Added `Notice#causes`, which allows cause data to be mutated in
|
14
|
+
`before_notify` callbacks (useful for filtering purposes).
|
15
|
+
- Added `Notice#cause=`, which allows the cause to be changed or disabled
|
16
|
+
in `before_notify` callbacks.
|
17
|
+
- Added extra shutdown logging.
|
18
|
+
|
19
|
+
### Fixed
|
20
|
+
- `Honeybadger.notify(exception, cause: nil)` will now prevent the cause from
|
21
|
+
being reported.
|
22
|
+
- When throttled, queued notices will be discarded during shutdown.
|
23
|
+
|
8
24
|
## [4.4.2] - 2019-08-01
|
9
25
|
### Fixed
|
10
26
|
- Handle ActiveSupport::Notifications passing nil started or finished time
|
data/lib/honeybadger/agent.rb
CHANGED
data/lib/honeybadger/notice.rb
CHANGED
@@ -55,6 +55,17 @@ module Honeybadger
|
|
55
55
|
# The Regexp used to strip invalid characters from individual tags.
|
56
56
|
TAG_SANITIZER = /[^\w]/.freeze
|
57
57
|
|
58
|
+
# @api private
|
59
|
+
class Cause
|
60
|
+
attr_accessor :error_class, :error_message, :backtrace
|
61
|
+
|
62
|
+
def initialize(cause)
|
63
|
+
self.error_class = cause.class.name
|
64
|
+
self.error_message = cause.message
|
65
|
+
self.backtrace = cause.backtrace
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
58
69
|
# The unique ID of this notice which can be used to reference the error in
|
59
70
|
# Honeybadger.
|
60
71
|
attr_reader :id
|
@@ -64,6 +75,13 @@ module Honeybadger
|
|
64
75
|
|
65
76
|
# The exception cause if available.
|
66
77
|
attr_reader :cause
|
78
|
+
def cause=(cause)
|
79
|
+
@cause = cause
|
80
|
+
@causes = unwrap_causes(cause)
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [Cause] A list of exception causes (see {Cause})
|
84
|
+
attr_reader :causes
|
67
85
|
|
68
86
|
# The backtrace from the given exception or hash.
|
69
87
|
attr_accessor :backtrace
|
@@ -163,15 +181,13 @@ module Honeybadger
|
|
163
181
|
@request_sanitizer = Util::Sanitizer.new(filters: params_filters)
|
164
182
|
|
165
183
|
@exception = unwrap_exception(opts[:exception])
|
166
|
-
@cause = opts[:cause] || exception_cause(@exception) || $!
|
167
|
-
|
168
|
-
@causes = unwrap_causes(@cause)
|
169
184
|
|
170
185
|
self.error_class = exception_attribute(:error_class, 'Notice') {|exception| exception.class.name }
|
171
186
|
self.error_message = exception_attribute(:error_message, 'No message provided') do |exception|
|
172
187
|
"#{exception.class.name}: #{exception.message}"
|
173
188
|
end
|
174
189
|
self.backtrace = exception_attribute(:backtrace, caller)
|
190
|
+
self.cause = opts.key?(:cause) ? opts[:cause] : (exception_cause(@exception) || $!)
|
175
191
|
|
176
192
|
self.context = construct_context_hash(opts, exception)
|
177
193
|
self.local_variables = local_variables_from_exception(exception, config)
|
@@ -213,7 +229,7 @@ module Honeybadger
|
|
213
229
|
backtrace: s(parse_backtrace(backtrace)),
|
214
230
|
fingerprint: fingerprint_hash,
|
215
231
|
tags: s(tags),
|
216
|
-
causes: s(causes)
|
232
|
+
causes: s(prepare_causes(causes))
|
217
233
|
},
|
218
234
|
request: request,
|
219
235
|
server: {
|
@@ -256,8 +272,8 @@ module Honeybadger
|
|
256
272
|
|
257
273
|
private
|
258
274
|
|
259
|
-
attr_reader :config, :opts, :stats, :now, :pid, :
|
260
|
-
:
|
275
|
+
attr_reader :config, :opts, :stats, :now, :pid, :request_sanitizer,
|
276
|
+
:rack_env
|
261
277
|
|
262
278
|
def ignore_by_origin?
|
263
279
|
return false if opts[:origin] != :rake
|
@@ -492,16 +508,12 @@ module Honeybadger
|
|
492
508
|
#
|
493
509
|
# cause - The first cause to unwrap.
|
494
510
|
#
|
495
|
-
# Returns Array
|
511
|
+
# Returns the Array of Cause instances.
|
496
512
|
def unwrap_causes(cause)
|
497
513
|
causes, c, i = [], cause, 0
|
498
514
|
|
499
515
|
while c && i < MAX_EXCEPTION_CAUSES
|
500
|
-
causes <<
|
501
|
-
class: c.class.name,
|
502
|
-
message: c.message,
|
503
|
-
backtrace: parse_backtrace(c.backtrace || caller)
|
504
|
-
}
|
516
|
+
causes << Cause.new(c)
|
505
517
|
i += 1
|
506
518
|
c = exception_cause(c)
|
507
519
|
end
|
@@ -509,6 +521,21 @@ module Honeybadger
|
|
509
521
|
causes
|
510
522
|
end
|
511
523
|
|
524
|
+
# Convert list of causes into payload format.
|
525
|
+
#
|
526
|
+
# causes - Array of Cause instances.
|
527
|
+
#
|
528
|
+
# Returns the Array of causes in Hash payload format.
|
529
|
+
def prepare_causes(causes)
|
530
|
+
causes.map {|c|
|
531
|
+
{
|
532
|
+
class: c.error_class,
|
533
|
+
message: c.error_message,
|
534
|
+
backtrace: parse_backtrace(c.backtrace)
|
535
|
+
}
|
536
|
+
}
|
537
|
+
end
|
538
|
+
|
512
539
|
def params_filters
|
513
540
|
config.params_filters + rails_params_filters
|
514
541
|
end
|
data/lib/honeybadger/version.rb
CHANGED
data/lib/honeybadger/worker.rb
CHANGED
@@ -14,37 +14,34 @@ module Honeybadger
|
|
14
14
|
# Sub-class thread so we have a named thread (useful for debugging in Thread.list).
|
15
15
|
class Thread < ::Thread; end
|
16
16
|
|
17
|
-
#
|
18
|
-
class Queue < ::Queue
|
19
|
-
attr_reader :max_size
|
20
|
-
|
21
|
-
def initialize(max_size)
|
22
|
-
@mutex = Mutex.new
|
23
|
-
@max_size = max_size
|
24
|
-
super()
|
25
|
-
end
|
26
|
-
|
27
|
-
def push(msg)
|
28
|
-
@mutex.synchronize do
|
29
|
-
super unless size >= max_size
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
17
|
+
# Used to signal the worker to shutdown.
|
34
18
|
SHUTDOWN = :__hb_worker_shutdown!
|
35
19
|
|
20
|
+
# The base number for the exponential backoff formula when calculating the
|
21
|
+
# throttle interval. `1.05 ** throttle` will reach an interval of 2 minutes
|
22
|
+
# after around 100 429 responses from the server.
|
23
|
+
BASE_THROTTLE = 1.05
|
24
|
+
|
36
25
|
def initialize(config)
|
37
26
|
@config = config
|
38
|
-
@
|
27
|
+
@throttle = 0
|
28
|
+
@throttle_interval = 0
|
39
29
|
@mutex = Mutex.new
|
40
30
|
@marker = ConditionVariable.new
|
41
|
-
@queue = Queue.new
|
31
|
+
@queue = Queue.new
|
42
32
|
@shutdown = false
|
43
33
|
@start_at = nil
|
34
|
+
@pid = Process.pid
|
44
35
|
end
|
45
36
|
|
46
37
|
def push(msg)
|
47
38
|
return false unless start
|
39
|
+
|
40
|
+
if queue.size >= config.max_queue_size
|
41
|
+
warn { sprintf('Unable to report error; reached max queue size of %s. id=%s', queue.size, msg.id) }
|
42
|
+
return false
|
43
|
+
end
|
44
|
+
|
48
45
|
queue.push(msg)
|
49
46
|
end
|
50
47
|
|
@@ -52,44 +49,28 @@ module Honeybadger
|
|
52
49
|
handle_response(msg, notify_backend(msg))
|
53
50
|
end
|
54
51
|
|
55
|
-
def shutdown
|
52
|
+
def shutdown(force = false)
|
56
53
|
d { 'shutting down worker' }
|
57
54
|
|
58
55
|
mutex.synchronize do
|
59
56
|
@shutdown = true
|
60
|
-
@pid = nil
|
61
|
-
queue.push(SHUTDOWN)
|
62
|
-
end
|
63
|
-
|
64
|
-
return true unless thread
|
65
|
-
|
66
|
-
r = true
|
67
|
-
unless Thread.current.eql?(thread)
|
68
|
-
begin
|
69
|
-
r = !!thread.join
|
70
|
-
ensure
|
71
|
-
shutdown! unless r
|
72
|
-
end
|
73
57
|
end
|
74
58
|
|
75
|
-
|
76
|
-
|
59
|
+
return true if force
|
60
|
+
return true unless thread&.alive?
|
77
61
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
@pid = nil
|
82
|
-
queue.clear
|
62
|
+
if throttled?
|
63
|
+
warn { sprintf('Unable to report %s error(s) to Honeybadger (currently throttled)', queue.size) } unless queue.empty?
|
64
|
+
return true
|
83
65
|
end
|
84
66
|
|
85
|
-
|
86
|
-
|
87
|
-
if thread
|
88
|
-
Thread.kill(thread)
|
89
|
-
thread.join # Allow ensure blocks to execute.
|
90
|
-
end
|
67
|
+
info { sprintf('Waiting to report %s error(s) to Honeybadger', queue.size) } unless queue.empty?
|
91
68
|
|
92
|
-
|
69
|
+
queue.push(SHUTDOWN)
|
70
|
+
!!thread.join
|
71
|
+
ensure
|
72
|
+
queue.clear
|
73
|
+
kill!
|
93
74
|
end
|
94
75
|
|
95
76
|
# Blocks until queue is processed up to this point in time.
|
@@ -109,7 +90,7 @@ module Honeybadger
|
|
109
90
|
@shutdown = false
|
110
91
|
@start_at = nil
|
111
92
|
|
112
|
-
return true if thread
|
93
|
+
return true if thread&.alive?
|
113
94
|
|
114
95
|
@pid = Process.pid
|
115
96
|
@thread = Thread.new { run }
|
@@ -120,24 +101,48 @@ module Honeybadger
|
|
120
101
|
|
121
102
|
private
|
122
103
|
|
123
|
-
attr_reader :config, :queue, :pid, :mutex, :marker,
|
124
|
-
:
|
104
|
+
attr_reader :config, :queue, :pid, :mutex, :marker, :thread, :throttle,
|
105
|
+
:throttle_interval, :start_at
|
125
106
|
|
126
107
|
def_delegator :config, :backend
|
127
108
|
|
109
|
+
def shutdown?
|
110
|
+
mutex.synchronize { @shutdown }
|
111
|
+
end
|
112
|
+
|
113
|
+
def suspended?
|
114
|
+
mutex.synchronize { start_at && Time.now.to_i < start_at }
|
115
|
+
end
|
116
|
+
|
128
117
|
def can_start?
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
118
|
+
return false if shutdown?
|
119
|
+
return false if suspended?
|
120
|
+
true
|
121
|
+
end
|
122
|
+
|
123
|
+
def throttled?
|
124
|
+
mutex.synchronize { throttle > 0 }
|
125
|
+
end
|
126
|
+
|
127
|
+
def kill!
|
128
|
+
d { 'killing worker thread' }
|
129
|
+
|
130
|
+
if thread
|
131
|
+
Thread.kill(thread)
|
132
|
+
thread.join # Allow ensure blocks to execute.
|
133
133
|
end
|
134
|
+
|
135
|
+
true
|
134
136
|
end
|
135
137
|
|
136
138
|
def suspend(interval)
|
137
|
-
mutex.synchronize
|
139
|
+
mutex.synchronize do
|
140
|
+
@start_at = Time.now.to_i + interval
|
141
|
+
queue.clear
|
142
|
+
end
|
138
143
|
|
139
144
|
# Must be performed last since this may kill the current thread.
|
140
|
-
|
145
|
+
kill!
|
141
146
|
end
|
142
147
|
|
143
148
|
def run
|
@@ -155,7 +160,7 @@ module Honeybadger
|
|
155
160
|
end
|
156
161
|
rescue Exception => e
|
157
162
|
error {
|
158
|
-
msg = "
|
163
|
+
msg = "Error in worker thread (shutting down) class=%s message=%s\n\t%s"
|
159
164
|
sprintf(msg, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
|
160
165
|
}
|
161
166
|
ensure
|
@@ -164,46 +169,54 @@ module Honeybadger
|
|
164
169
|
|
165
170
|
def work(msg)
|
166
171
|
send_now(msg)
|
172
|
+
|
173
|
+
if shutdown? && throttled?
|
174
|
+
warn { sprintf('Unable to report %s error(s) to Honeybadger (currently throttled)', queue.size) } if queue.size > 1
|
175
|
+
kill!
|
176
|
+
return
|
177
|
+
end
|
178
|
+
|
167
179
|
sleep(throttle_interval)
|
168
180
|
rescue StandardError => e
|
169
181
|
error {
|
170
182
|
msg = "Error in worker thread class=%s message=%s\n\t%s"
|
171
183
|
sprintf(msg, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
|
172
184
|
}
|
173
|
-
sleep(1)
|
174
|
-
end
|
175
|
-
|
176
|
-
def throttle_interval
|
177
|
-
return 0 unless throttles[0]
|
178
|
-
mutex.synchronize do
|
179
|
-
throttles.reduce(1) {|a,e| a*e }
|
180
|
-
end
|
181
185
|
end
|
182
186
|
|
183
187
|
def notify_backend(payload)
|
184
|
-
|
188
|
+
d { sprintf('worker notifying backend id=%s', payload.id) }
|
185
189
|
backend.notify(:notices, payload)
|
186
190
|
end
|
187
191
|
|
188
|
-
def
|
192
|
+
def calc_throttle_interval
|
193
|
+
((BASE_THROTTLE ** throttle) - 1).round(3)
|
194
|
+
end
|
195
|
+
|
196
|
+
def inc_throttle
|
189
197
|
mutex.synchronize do
|
190
|
-
|
198
|
+
@throttle += 1
|
199
|
+
@throttle_interval = calc_throttle_interval
|
200
|
+
throttle
|
191
201
|
end
|
192
202
|
end
|
193
203
|
|
194
|
-
def
|
204
|
+
def dec_throttle
|
195
205
|
mutex.synchronize do
|
196
|
-
|
206
|
+
return nil if throttle == 0
|
207
|
+
@throttle -= 1
|
208
|
+
@throttle_interval = calc_throttle_interval
|
209
|
+
throttle
|
197
210
|
end
|
198
211
|
end
|
199
212
|
|
200
213
|
def handle_response(msg, response)
|
201
|
-
|
214
|
+
d { sprintf('worker response id=%s code=%s message=%s', msg.id, response.code, response.message.to_s.dump) }
|
202
215
|
|
203
216
|
case response.code
|
204
217
|
when 429, 503
|
205
|
-
|
206
|
-
|
218
|
+
throttle = inc_throttle
|
219
|
+
warn { sprintf('Error report failed: project is sending too many errors. id=%s code=%s throttle=%s interval=%s', msg.id, response.code, throttle, throttle_interval) }
|
207
220
|
when 402
|
208
221
|
warn { sprintf('Error report failed: payment is required. id=%s code=%s', msg.id, response.code) }
|
209
222
|
suspend(3600)
|
@@ -211,8 +224,8 @@ module Honeybadger
|
|
211
224
|
warn { sprintf('Error report failed: API key is invalid. id=%s code=%s', msg.id, response.code) }
|
212
225
|
suspend(3600)
|
213
226
|
when 201
|
214
|
-
if throttle =
|
215
|
-
info { sprintf('Success ⚡ https://app.honeybadger.io/notice/%s id=%s code=%s throttle=%s interval=%s', msg.id, msg.id, response.code,
|
227
|
+
if throttle = dec_throttle
|
228
|
+
info { sprintf('Success ⚡ https://app.honeybadger.io/notice/%s id=%s code=%s throttle=%s interval=%s', msg.id, msg.id, response.code, throttle, throttle_interval) }
|
216
229
|
else
|
217
230
|
info { sprintf('Success ⚡ https://app.honeybadger.io/notice/%s id=%s code=%s', msg.id, msg.id, response.code) }
|
218
231
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: honeybadger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Honeybadger Industries LLC
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-08-
|
11
|
+
date: 2019-08-05 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Make managing application errors a more pleasant experience.
|
14
14
|
email:
|