honeybadger 5.4.1 → 5.7.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 +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.
|