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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c626b5316e989c412c5f9a99abdb0c678fd9d9c2f64962d838d884444fd31ff9
4
- data.tar.gz: 6cae1f48c30ee24af06e67f2051131fd31fcc6c88a04f7487bb01a07a4381948
3
+ metadata.gz: 0fda24e6e314cb0c4d97567f6bd853fb2ea362a3f74811d3f678df5c6e29296b
4
+ data.tar.gz: 5277c9c0f5387299d254345b9b728d1699b5852bd3a9a63ce3ec7e6c22019546
5
5
  SHA512:
6
- metadata.gz: dd4b81cea301889471b61883309148588a6674ede84967f854dd324a71853edec14d918b590767c791b770e5f69476cee2137a620ed5fe3796b2ad01446e441b
7
- data.tar.gz: f32eff562df8eacfa51c0461537e8d0f066d174c5a100dc08d8c4fd8c7147338b5a2394b6ce07a01ffba00535a9189e66a1f665ce7b0858f2c525e9143fef57b
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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015 Honeybadger Industries LLC
1
+ Copyright (c) 2024 Honeybadger Industries LLC
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
@@ -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
@@ -24,6 +24,10 @@ module Honeybadger
24
24
  def check_in(id)
25
25
  StubbedResponse.new
26
26
  end
27
+
28
+ def event(payload)
29
+ StubbedResponse.new
30
+ end
27
31
  end
28
32
  end
29
33
  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,
@@ -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
- Plugin.register {
6
- requirement { defined?(::Rails.application) && ::Rails.application }
7
- requirement {
8
- ::Rails.application.config.active_job[:queue_adapter] == :async
9
- }
10
-
11
- execution {
12
- ::ActiveJob::Base.class_eval do |base|
13
- base.set_callback :perform, :around do |param, block|
14
- Honeybadger.clear!
15
- begin
16
- block.call
17
- rescue => error
18
- Honeybadger.notify(error, parameters: { job_arguments: self.arguments })
19
- raise error
20
- end
21
- end
22
- end
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
@@ -1,4 +1,4 @@
1
1
  module Honeybadger
2
2
  # The current String Honeybadger version.
3
- VERSION = '5.4.1'.freeze
3
+ VERSION = '5.7.0'.freeze
4
4
  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: 5.4.1
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: 2023-12-22 00:00:00.000000000 Z
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.10
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.