honeybadger 5.4.1 → 5.7.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 +28 -0
- data/LICENSE +1 -1
- data/lib/honeybadger/agent.rb +41 -1
- data/lib/honeybadger/backend/base.rb +10 -0
- data/lib/honeybadger/backend/debug.rb +6 -0
- data/lib/honeybadger/backend/null.rb +4 -0
- data/lib/honeybadger/backend/server.rb +14 -3
- data/lib/honeybadger/config/defaults.rb +10 -0
- data/lib/honeybadger/config.rb +8 -0
- data/lib/honeybadger/events_worker.rb +319 -0
- data/lib/honeybadger/plugins/active_job.rb +40 -23
- data/lib/honeybadger/singleton.rb +1 -0
- data/lib/honeybadger/util/http.rb +6 -0
- data/lib/honeybadger/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0fda24e6e314cb0c4d97567f6bd853fb2ea362a3f74811d3f678df5c6e29296b
|
4
|
+
data.tar.gz: 5277c9c0f5387299d254345b9b728d1699b5852bd3a9a63ce3ec7e6c22019546
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81f8c10e7b6bbfa07ad527a713c32252281a3e190c76f708635cfbcb1c9758fb4a195b95576780f0a8181cf81110ea741d05365ccc0cc1404a08ca984b627266
|
7
|
+
data.tar.gz: 36c75e8ed8c8ed6efa638ef367f80d5fa79f9d718b87ebae77db081af629ce64deb36b81c4a3cb22e725b2b24eb9b3aedba1cdec5e33123dcaadff790f6f9b1a
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,33 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [5.7.0](https://github.com/honeybadger-io/honeybadger-ruby/compare/v5.6.0...v5.7.0) (2024-03-12)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* add additional context to ActiveJob notifications ([#528](https://github.com/honeybadger-io/honeybadger-ruby/issues/528)) ([d6ae246](https://github.com/honeybadger-io/honeybadger-ruby/commit/d6ae246a24290d76bcd0c8deb9121707d88976fe))
|
9
|
+
|
10
|
+
## [5.6.0](https://github.com/honeybadger-io/honeybadger-ruby/compare/v5.5.1...v5.6.0) (2024-03-05)
|
11
|
+
|
12
|
+
|
13
|
+
### Features
|
14
|
+
|
15
|
+
* track exceptions in :solid_queue ([#526](https://github.com/honeybadger-io/honeybadger-ruby/issues/526)) ([4e2d428](https://github.com/honeybadger-io/honeybadger-ruby/commit/4e2d4287bbbe0100d6f82a38b7314fc8dc5a1571)), closes [#518](https://github.com/honeybadger-io/honeybadger-ruby/issues/518)
|
16
|
+
|
17
|
+
## [5.5.1](https://github.com/honeybadger-io/honeybadger-ruby/compare/v5.5.0...v5.5.1) (2024-02-26)
|
18
|
+
|
19
|
+
|
20
|
+
### Bug Fixes
|
21
|
+
|
22
|
+
* don't raise an exception when ActiveJob isn't loaded ([#523](https://github.com/honeybadger-io/honeybadger-ruby/issues/523)) ([40c7892](https://github.com/honeybadger-io/honeybadger-ruby/commit/40c7892b9f191eb9159b776880962fc079c5e665))
|
23
|
+
|
24
|
+
## [5.5.0](https://github.com/honeybadger-io/honeybadger-ruby/compare/v5.4.1...v5.5.0) (2024-02-12)
|
25
|
+
|
26
|
+
|
27
|
+
### Features
|
28
|
+
|
29
|
+
* implements honeybadger.event by synchronous log call ([#512](https://github.com/honeybadger-io/honeybadger-ruby/issues/512)) ([dbe7e3d](https://github.com/honeybadger-io/honeybadger-ruby/commit/dbe7e3dc20cbb432254b055b356826a42a76c609))
|
30
|
+
|
3
31
|
## [5.4.1](https://github.com/honeybadger-io/honeybadger-ruby/compare/v5.4.0...v5.4.1) (2023-12-22)
|
4
32
|
|
5
33
|
|
data/LICENSE
CHANGED
data/lib/honeybadger/agent.rb
CHANGED
@@ -7,6 +7,7 @@ require 'honeybadger/notice'
|
|
7
7
|
require 'honeybadger/plugin'
|
8
8
|
require 'honeybadger/logging'
|
9
9
|
require 'honeybadger/worker'
|
10
|
+
require 'honeybadger/events_worker'
|
10
11
|
require 'honeybadger/breadcrumbs'
|
11
12
|
|
12
13
|
module Honeybadger
|
@@ -354,6 +355,7 @@ module Honeybadger
|
|
354
355
|
yield
|
355
356
|
ensure
|
356
357
|
worker.flush
|
358
|
+
events_worker&.flush
|
357
359
|
end
|
358
360
|
|
359
361
|
# Stops the Honeybadger service.
|
@@ -362,9 +364,41 @@ module Honeybadger
|
|
362
364
|
# Honeybadger.stop # => nil
|
363
365
|
def stop(force = false)
|
364
366
|
worker.shutdown(force)
|
367
|
+
events_worker&.shutdown(force)
|
365
368
|
true
|
366
369
|
end
|
367
370
|
|
371
|
+
# Sends event to events backend
|
372
|
+
#
|
373
|
+
# @example
|
374
|
+
# # With event type as first argument (recommended):
|
375
|
+
# Honeybadger.event("user_signed_up", user_id: 123)
|
376
|
+
#
|
377
|
+
# # With just a payload:
|
378
|
+
# Honeybadger.event(event_type: "user_signed_up", user_id: 123)
|
379
|
+
#
|
380
|
+
# @param event_name [String, Hash] a String describing the event or a Hash
|
381
|
+
# when the second argument is omitted.
|
382
|
+
# @param payload [Hash] Additional data to be sent with the event as keyword arguments
|
383
|
+
#
|
384
|
+
# @return [void]
|
385
|
+
def event(event_type, payload = {})
|
386
|
+
init_events_worker
|
387
|
+
|
388
|
+
ts = DateTime.now.new_offset(0).rfc3339
|
389
|
+
merged = {ts: ts}
|
390
|
+
|
391
|
+
if event_type.is_a?(String)
|
392
|
+
merged.merge!(event_type: event_type)
|
393
|
+
else
|
394
|
+
merged.merge!(Hash(event_type))
|
395
|
+
end
|
396
|
+
|
397
|
+
merged.merge!(Hash(payload))
|
398
|
+
|
399
|
+
events_worker.push(merged)
|
400
|
+
end
|
401
|
+
|
368
402
|
# @api private
|
369
403
|
attr_reader :config
|
370
404
|
|
@@ -437,7 +471,7 @@ module Honeybadger
|
|
437
471
|
end
|
438
472
|
|
439
473
|
# @api private
|
440
|
-
attr_reader :worker
|
474
|
+
attr_reader :worker, :events_worker
|
441
475
|
|
442
476
|
# @api private
|
443
477
|
# @!method init!(...)
|
@@ -475,9 +509,15 @@ module Honeybadger
|
|
475
509
|
end
|
476
510
|
|
477
511
|
def init_worker
|
512
|
+
return if @worker
|
478
513
|
@worker = Worker.new(config)
|
479
514
|
end
|
480
515
|
|
516
|
+
def init_events_worker
|
517
|
+
return if @events_worker
|
518
|
+
@events_worker = EventsWorker.new(config)
|
519
|
+
end
|
520
|
+
|
481
521
|
def with_error_handling
|
482
522
|
yield
|
483
523
|
rescue => ex
|
@@ -109,6 +109,16 @@ module Honeybadger
|
|
109
109
|
notify(:deploys, payload)
|
110
110
|
end
|
111
111
|
|
112
|
+
# Send event
|
113
|
+
# @example
|
114
|
+
# backend.event([{event_type: "email_received", ts: "2023-03-04T12:12:00+1:00", subject: 'Re: Aquisition' }})
|
115
|
+
#
|
116
|
+
# @param [Array] payload array of event hashes to send
|
117
|
+
# @raise NotImplementedError
|
118
|
+
def event(payload)
|
119
|
+
raise NotImplementedError, "must define #event on subclass"
|
120
|
+
end
|
121
|
+
|
112
122
|
private
|
113
123
|
|
114
124
|
attr_reader :config
|
@@ -17,6 +17,12 @@ module Honeybadger
|
|
17
17
|
return Response.new(ENV['DEBUG_BACKEND_STATUS'].to_i, nil) if ENV['DEBUG_BACKEND_STATUS']
|
18
18
|
super
|
19
19
|
end
|
20
|
+
|
21
|
+
def event(payload)
|
22
|
+
logger.unknown("sending event to debug backend with event=#{payload.to_json}")
|
23
|
+
return Response.new(ENV['DEBUG_BACKEND_STATUS'].to_i, nil) if ENV['DEBUG_BACKEND_STATUS']
|
24
|
+
super
|
25
|
+
end
|
20
26
|
end
|
21
27
|
end
|
22
28
|
end
|
@@ -11,11 +11,10 @@ module Honeybadger
|
|
11
11
|
class Server < Base
|
12
12
|
ENDPOINTS = {
|
13
13
|
notices: '/v1/notices'.freeze,
|
14
|
-
deploys: '/v1/deploys'.freeze
|
14
|
+
deploys: '/v1/deploys'.freeze,
|
15
15
|
}.freeze
|
16
|
-
|
17
16
|
CHECK_IN_ENDPOINT = '/v1/check_in'.freeze
|
18
|
-
|
17
|
+
EVENTS_ENDPOINT = '/v1/events'.freeze
|
19
18
|
|
20
19
|
HTTP_ERRORS = Util::HTTP::ERRORS
|
21
20
|
|
@@ -48,6 +47,18 @@ module Honeybadger
|
|
48
47
|
Response.new(:error, nil, "HTTP Error: #{e.class}")
|
49
48
|
end
|
50
49
|
|
50
|
+
# Send event
|
51
|
+
# @example
|
52
|
+
# backend.event([{event_type: "email_received", ts: "2023-03-04T12:12:00+1:00", subject: 'Re: Aquisition' }})
|
53
|
+
#
|
54
|
+
# @param [Array] payload array of event hashes to send
|
55
|
+
# @return [Response]
|
56
|
+
def event(payload)
|
57
|
+
Response.new(@http.post_newline_delimited(EVENTS_ENDPOINT, payload))
|
58
|
+
rescue *HTTP_ERRORS => e
|
59
|
+
Response.new(:error, nil, "HTTP Error: #{e.class}")
|
60
|
+
end
|
61
|
+
|
51
62
|
private
|
52
63
|
|
53
64
|
def payload_headers(payload)
|
@@ -91,6 +91,16 @@ module Honeybadger
|
|
91
91
|
default: 100,
|
92
92
|
type: Integer
|
93
93
|
},
|
94
|
+
:'events.batch_size' => {
|
95
|
+
description: 'Send events batch if n events have accumulated',
|
96
|
+
default: 100,
|
97
|
+
type: Integer
|
98
|
+
},
|
99
|
+
:'events.timeout' => {
|
100
|
+
description: 'Timeout after which the events batch will be sent regardless (in milliseconds)',
|
101
|
+
default: 30_000,
|
102
|
+
type: Integer
|
103
|
+
},
|
94
104
|
plugins: {
|
95
105
|
description: 'An optional list of plugins to load. Default is to load all plugins.',
|
96
106
|
default: nil,
|
data/lib/honeybadger/config.rb
CHANGED
@@ -224,6 +224,14 @@ module Honeybadger
|
|
224
224
|
self[:max_queue_size]
|
225
225
|
end
|
226
226
|
|
227
|
+
def events_batch_size
|
228
|
+
self[:'events.batch_size']
|
229
|
+
end
|
230
|
+
|
231
|
+
def events_timeout
|
232
|
+
self[:'events.timeout']
|
233
|
+
end
|
234
|
+
|
227
235
|
def params_filters
|
228
236
|
Array(self[:'request.filter_keys'])
|
229
237
|
end
|
@@ -0,0 +1,319 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
require 'honeybadger/logging'
|
5
|
+
|
6
|
+
module Honeybadger
|
7
|
+
# A concurrent queue to notify the backend.
|
8
|
+
# @api private
|
9
|
+
class EventsWorker
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
include Honeybadger::Logging::Helper
|
13
|
+
|
14
|
+
# Sub-class thread so we have a named thread (useful for debugging in Thread.list).
|
15
|
+
class Thread < ::Thread; end
|
16
|
+
|
17
|
+
# Used to signal the worker to shutdown.
|
18
|
+
SHUTDOWN = :__hb_worker_shutdown!
|
19
|
+
FLUSH = :__hb_worker_flush!
|
20
|
+
CHECK_TIMEOUT = :__hb_worker_check_timeout!
|
21
|
+
|
22
|
+
# The base number for the exponential backoff formula when calculating the
|
23
|
+
# throttle interval. `1.05 ** throttle` will reach an interval of 2 minutes
|
24
|
+
# after around 100 429 responses from the server.
|
25
|
+
BASE_THROTTLE = 1.05
|
26
|
+
|
27
|
+
# TODO: These could be configurable?
|
28
|
+
|
29
|
+
def initialize(config)
|
30
|
+
@config = config
|
31
|
+
@throttle = 0
|
32
|
+
@throttle_interval = 0
|
33
|
+
@mutex = Mutex.new
|
34
|
+
@marker = ConditionVariable.new
|
35
|
+
@queue = Queue.new
|
36
|
+
@send_queue = Queue.new
|
37
|
+
@shutdown = false
|
38
|
+
@start_at = nil
|
39
|
+
@pid = Process.pid
|
40
|
+
@send_queue = []
|
41
|
+
@last_sent = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def push(msg)
|
45
|
+
return false unless start
|
46
|
+
|
47
|
+
if queue.size >= config.max_queue_size
|
48
|
+
warn { sprintf('Unable to send event; reached max queue size of %s.', queue.size) }
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
|
52
|
+
queue.push(msg)
|
53
|
+
end
|
54
|
+
|
55
|
+
def send_now(msg)
|
56
|
+
handle_response(send_to_backend(msg))
|
57
|
+
end
|
58
|
+
|
59
|
+
def shutdown(force = false)
|
60
|
+
d { 'shutting down events worker' }
|
61
|
+
|
62
|
+
mutex.synchronize do
|
63
|
+
@shutdown = true
|
64
|
+
end
|
65
|
+
|
66
|
+
return true if force
|
67
|
+
return true unless thread&.alive?
|
68
|
+
|
69
|
+
if throttled?
|
70
|
+
warn { sprintf('Unable to send %s event(s) to Honeybadger (currently throttled)', queue.size) } unless queue.empty?
|
71
|
+
return true
|
72
|
+
end
|
73
|
+
|
74
|
+
info { sprintf('Waiting to send %s events(s) to Honeybadger', queue.size) } unless queue.empty?
|
75
|
+
queue.push(FLUSH)
|
76
|
+
queue.push(SHUTDOWN)
|
77
|
+
!!thread.join
|
78
|
+
ensure
|
79
|
+
queue.clear
|
80
|
+
kill!
|
81
|
+
end
|
82
|
+
|
83
|
+
# Blocks until queue is processed up to this point in time.
|
84
|
+
def flush
|
85
|
+
mutex.synchronize do
|
86
|
+
if thread && thread.alive?
|
87
|
+
queue.push(FLUSH)
|
88
|
+
queue.push(marker)
|
89
|
+
marker.wait(mutex)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def start
|
95
|
+
return false unless can_start?
|
96
|
+
|
97
|
+
mutex.synchronize do
|
98
|
+
@shutdown = false
|
99
|
+
@start_at = nil
|
100
|
+
|
101
|
+
return true if thread&.alive?
|
102
|
+
|
103
|
+
@pid = Process.pid
|
104
|
+
@thread = Thread.new { run }
|
105
|
+
@timeout_thread = Thread.new { schedule_timeout_check }
|
106
|
+
end
|
107
|
+
|
108
|
+
true
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
attr_reader :config, :queue, :pid, :mutex, :marker, :thread, :timeout_thread, :throttle,
|
114
|
+
:throttle_interval, :start_at, :send_queue, :last_sent
|
115
|
+
|
116
|
+
def_delegator :config, :backend
|
117
|
+
|
118
|
+
def shutdown?
|
119
|
+
mutex.synchronize { @shutdown }
|
120
|
+
end
|
121
|
+
|
122
|
+
def suspended?
|
123
|
+
mutex.synchronize { start_at && Time.now.to_i < start_at }
|
124
|
+
end
|
125
|
+
|
126
|
+
def can_start?
|
127
|
+
return false if shutdown?
|
128
|
+
return false if suspended?
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
def throttled?
|
133
|
+
mutex.synchronize { throttle > 0 }
|
134
|
+
end
|
135
|
+
|
136
|
+
def kill!
|
137
|
+
d { 'killing worker thread' }
|
138
|
+
|
139
|
+
if thread
|
140
|
+
Thread.kill(thread)
|
141
|
+
Thread.kill(timeout_thread)
|
142
|
+
thread.join # Allow ensure blocks to execute.
|
143
|
+
end
|
144
|
+
|
145
|
+
true
|
146
|
+
end
|
147
|
+
|
148
|
+
def suspend(interval)
|
149
|
+
mutex.synchronize do
|
150
|
+
@start_at = Time.now.to_i + interval
|
151
|
+
queue.clear
|
152
|
+
end
|
153
|
+
|
154
|
+
# Must be performed last since this may kill the current thread.
|
155
|
+
kill!
|
156
|
+
end
|
157
|
+
|
158
|
+
def schedule_timeout_check
|
159
|
+
loop do
|
160
|
+
sleep(config.events_timeout / 1000.0)
|
161
|
+
queue.push(CHECK_TIMEOUT)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def run
|
166
|
+
begin
|
167
|
+
d { 'worker started' }
|
168
|
+
mutex.synchronize do
|
169
|
+
@last_sent = Time.now
|
170
|
+
end
|
171
|
+
loop do
|
172
|
+
case msg = queue.pop
|
173
|
+
when SHUTDOWN then break
|
174
|
+
when CHECK_TIMEOUT then check_timeout
|
175
|
+
when FLUSH then flush_send_queue
|
176
|
+
when ConditionVariable then signal_marker(msg)
|
177
|
+
else work(msg)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
ensure
|
181
|
+
d { 'stopping worker' }
|
182
|
+
end
|
183
|
+
rescue Exception => e
|
184
|
+
error {
|
185
|
+
msg = "Error in worker thread (shutting down) class=%s message=%s\n\t%s"
|
186
|
+
sprintf(msg, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
|
187
|
+
}
|
188
|
+
ensure
|
189
|
+
release_marker
|
190
|
+
end
|
191
|
+
|
192
|
+
def check_timeout
|
193
|
+
return if mutex.synchronize { send_queue.empty? }
|
194
|
+
ms_since = (Time.now.to_f - last_sent.to_f) * 1000.0
|
195
|
+
if ms_since >= config.events_timeout
|
196
|
+
send_batch
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def enqueue_msg(msg)
|
201
|
+
mutex.synchronize do
|
202
|
+
@send_queue << msg
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def send_batch
|
207
|
+
send_now(mutex.synchronize { send_queue })
|
208
|
+
mutex.synchronize do
|
209
|
+
@last_sent = Time.now
|
210
|
+
send_queue.clear
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def check_and_send
|
215
|
+
return if mutex.synchronize { send_queue.empty? }
|
216
|
+
if mutex.synchronize { send_queue.length } >= config.events_batch_size
|
217
|
+
send_batch
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def flush_send_queue
|
222
|
+
return if mutex.synchronize { send_queue.empty? }
|
223
|
+
send_batch
|
224
|
+
rescue StandardError => e
|
225
|
+
error {
|
226
|
+
msg = "Error in worker thread class=%s message=%s\n\t%s"
|
227
|
+
sprintf(msg, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
|
228
|
+
}
|
229
|
+
end
|
230
|
+
|
231
|
+
def work(msg)
|
232
|
+
enqueue_msg(msg)
|
233
|
+
check_and_send
|
234
|
+
|
235
|
+
if shutdown? && throttled?
|
236
|
+
warn { sprintf('Unable to send %s events(s) to Honeybadger (currently throttled)', queue.size) } if queue.size > 1
|
237
|
+
kill!
|
238
|
+
return
|
239
|
+
end
|
240
|
+
|
241
|
+
sleep(throttle_interval)
|
242
|
+
rescue StandardError => e
|
243
|
+
error {
|
244
|
+
msg = "Error in worker thread class=%s message=%s\n\t%s"
|
245
|
+
sprintf(msg, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
|
246
|
+
}
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
def send_to_backend(msg)
|
251
|
+
d { 'events_worker sending to backend' }
|
252
|
+
response = backend.event(msg)
|
253
|
+
response
|
254
|
+
end
|
255
|
+
|
256
|
+
def calc_throttle_interval
|
257
|
+
((BASE_THROTTLE ** throttle) - 1).round(3)
|
258
|
+
end
|
259
|
+
|
260
|
+
def inc_throttle
|
261
|
+
mutex.synchronize do
|
262
|
+
@throttle += 1
|
263
|
+
@throttle_interval = calc_throttle_interval
|
264
|
+
throttle
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def dec_throttle
|
269
|
+
mutex.synchronize do
|
270
|
+
return nil if throttle == 0
|
271
|
+
@throttle -= 1
|
272
|
+
@throttle_interval = calc_throttle_interval
|
273
|
+
throttle
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def handle_response(response)
|
278
|
+
d { sprintf('events_worker response code=%s message=%s', response.code, response.message.to_s.dump) }
|
279
|
+
|
280
|
+
case response.code
|
281
|
+
when 429, 503
|
282
|
+
throttle = inc_throttle
|
283
|
+
warn { sprintf('Event send failed: project is sending too many events. code=%s throttle=%s interval=%s', response.code, throttle, throttle_interval) }
|
284
|
+
when 402
|
285
|
+
warn { sprintf('Event send failed: payment is required. code=%s', response.code) }
|
286
|
+
suspend(3600)
|
287
|
+
when 403
|
288
|
+
warn { sprintf('Event send failed: API key is invalid. code=%s', response.code) }
|
289
|
+
suspend(3600)
|
290
|
+
when 413
|
291
|
+
warn { sprintf('Event send failed: Payload is too large. code=%s', response.code) }
|
292
|
+
when 201
|
293
|
+
if throttle = dec_throttle
|
294
|
+
debug { sprintf('Success ⚡ Event sent code=%s throttle=%s interval=%s', response.code, throttle, throttle_interval) }
|
295
|
+
else
|
296
|
+
debug { sprintf('Success ⚡ Event sent code=%s', response.code) }
|
297
|
+
end
|
298
|
+
when :stubbed
|
299
|
+
info { sprintf('Success ⚡ Development mode is enabled; This event will be sent after app is deployed.') }
|
300
|
+
when :error
|
301
|
+
warn { sprintf('Event send failed: an unknown error occurred. code=%s error=%s', response.code, response.message.to_s.dump) }
|
302
|
+
else
|
303
|
+
warn { sprintf('Event send failed: unknown response from server. code=%s', response.code) }
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
# Release the marker. Important to perform during cleanup when shutting
|
308
|
+
# down, otherwise it could end up waiting indefinitely.
|
309
|
+
def release_marker
|
310
|
+
signal_marker(marker)
|
311
|
+
end
|
312
|
+
|
313
|
+
def signal_marker(marker)
|
314
|
+
mutex.synchronize do
|
315
|
+
marker.signal
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
@@ -1,28 +1,45 @@
|
|
1
1
|
module Honeybadger
|
2
2
|
module Plugins
|
3
3
|
module ActiveJob
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
4
|
+
# Ignore inline and test adapters, as well as the adapters that we support with their own plugins
|
5
|
+
EXCLUDED_ADAPTERS = %i[inline test delayed_job faktory karafka resque shoryuken sidekiq sucker_punch].freeze
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def perform_around(job, block)
|
9
|
+
Honeybadger.clear!
|
10
|
+
context = context(job)
|
11
|
+
block.call
|
12
|
+
rescue StandardError => e
|
13
|
+
Honeybadger.notify(e, context: context, parameters: { arguments: job.arguments })
|
14
|
+
raise e
|
15
|
+
end
|
16
|
+
|
17
|
+
def context(job) # rubocop:disable Metrics/MethodLength
|
18
|
+
{
|
19
|
+
component: job.class,
|
20
|
+
action: 'perform',
|
21
|
+
enqueued_at: job.try(:enqueued_at),
|
22
|
+
executions: job.executions,
|
23
|
+
job_class: job.class,
|
24
|
+
job_id: job.job_id,
|
25
|
+
priority: job.priority,
|
26
|
+
queue_name: job.queue_name,
|
27
|
+
scheduled_at: job.scheduled_at
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
Plugin.register do
|
33
|
+
requirement do
|
34
|
+
defined?(::Rails.application) &&
|
35
|
+
::Rails.application.config.respond_to?(:active_job) &&
|
36
|
+
!EXCLUDED_ADAPTERS.include?(::Rails.application.config.active_job[:queue_adapter])
|
37
|
+
end
|
38
|
+
|
39
|
+
execution do
|
40
|
+
::ActiveJob::Base.set_callback(:perform, :around, &ActiveJob.method(:perform_around))
|
41
|
+
end
|
42
|
+
end
|
25
43
|
end
|
26
44
|
end
|
27
|
-
|
28
|
-
end
|
45
|
+
end
|
@@ -38,6 +38,7 @@ module Honeybadger
|
|
38
38
|
def_delegator :'Honeybadger::Agent.instance', :breadcrumbs
|
39
39
|
def_delegator :'Honeybadger::Agent.instance', :clear!
|
40
40
|
def_delegator :'Honeybadger::Agent.instance', :track_deployment
|
41
|
+
def_delegator :'Honeybadger::Agent.instance', :event
|
41
42
|
|
42
43
|
# @!macro [attach] def_delegator
|
43
44
|
# @!method $2(...)
|
@@ -49,6 +49,12 @@ module Honeybadger
|
|
49
49
|
response
|
50
50
|
end
|
51
51
|
|
52
|
+
def post_newline_delimited(endpoint, payload, headers = nil)
|
53
|
+
response = http_connection.post(endpoint, compress(payload.map(&:to_json).join("\n")), http_headers(headers))
|
54
|
+
debug { sprintf("http method=POST path=%s code=%d", endpoint.dump, response.code) }
|
55
|
+
response
|
56
|
+
end
|
57
|
+
|
52
58
|
private
|
53
59
|
|
54
60
|
attr_reader :config
|
data/lib/honeybadger/version.rb
CHANGED
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: 5.
|
4
|
+
version: 5.7.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:
|
11
|
+
date: 2024-03-12 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Make managing application errors a more pleasant experience.
|
14
14
|
email:
|
@@ -55,6 +55,7 @@ files:
|
|
55
55
|
- lib/honeybadger/const.rb
|
56
56
|
- lib/honeybadger/context_manager.rb
|
57
57
|
- lib/honeybadger/conversions.rb
|
58
|
+
- lib/honeybadger/events_worker.rb
|
58
59
|
- lib/honeybadger/init/hanami.rb
|
59
60
|
- lib/honeybadger/init/rails.rb
|
60
61
|
- lib/honeybadger/init/rake.rb
|
@@ -161,7 +162,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
161
162
|
- !ruby/object:Gem::Version
|
162
163
|
version: '0'
|
163
164
|
requirements: []
|
164
|
-
rubygems_version: 3.4.
|
165
|
+
rubygems_version: 3.4.19
|
165
166
|
signing_key:
|
166
167
|
specification_version: 4
|
167
168
|
summary: Error reports you can be happy about.
|