honeybadger 4.4.2 → 4.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|