honeybadger 5.10.2 → 5.11.1
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 +19 -0
- data/lib/honeybadger/agent.rb +70 -3
- data/lib/honeybadger/config/defaults.rb +56 -1
- data/lib/honeybadger/config.rb +32 -0
- data/lib/honeybadger/context_manager.rb +10 -1
- data/lib/honeybadger/counter.rb +18 -0
- data/lib/honeybadger/events_worker.rb +1 -1
- data/lib/honeybadger/gauge.rb +30 -0
- data/lib/honeybadger/histogram.rb +32 -0
- data/lib/honeybadger/instrumentation.rb +124 -0
- data/lib/honeybadger/instrumentation_helper.rb +120 -0
- data/lib/honeybadger/metric.rb +47 -0
- data/lib/honeybadger/metrics_worker.rb +175 -0
- data/lib/honeybadger/notification_subscriber.rb +99 -0
- data/lib/honeybadger/plugin.rb +77 -1
- data/lib/honeybadger/plugins/active_job.rb +16 -4
- data/lib/honeybadger/plugins/autotuner.rb +30 -0
- data/lib/honeybadger/plugins/karafka.rb +17 -2
- data/lib/honeybadger/plugins/net_http.rb +52 -0
- data/lib/honeybadger/plugins/rails.rb +15 -0
- data/lib/honeybadger/plugins/sidekiq.rb +128 -1
- data/lib/honeybadger/plugins/solid_queue.rb +27 -0
- data/lib/honeybadger/plugins/system.rb +16 -0
- data/lib/honeybadger/registry.rb +32 -0
- data/lib/honeybadger/registry_execution.rb +28 -0
- data/lib/honeybadger/singleton.rb +8 -0
- data/lib/honeybadger/timer.rb +6 -0
- data/lib/honeybadger/version.rb +1 -1
- data/lib/puma/plugin/honeybadger.rb +43 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 238281f2485599809a1cbde2313f3b3dbcce4e8ef2fb251b47190d6e2f5c5ce4
|
4
|
+
data.tar.gz: 82c41d922f89c1e9f0d3392086d6e3f2c2437842dba791715df324eacb3bf150
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9186cbd806cd63e77c15b355502a1f3f666469b294324c01c55a92b85f29aaa7d5e5ba51b008f57e8f484ac9089679236cd7038dd8af40a7f97b91f05ef0cfde
|
7
|
+
data.tar.gz: 0f95f4ed9bd07fa00d57c0443b279283275824acd6d7dba60dc36b04cd26c1575b706ae633c7bb579a6d6cc44a328b70c898de68998940512b70563608fca442
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,25 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
3
|
|
4
|
+
## [5.11.1](https://github.com/honeybadger-io/honeybadger-ruby/compare/v5.11.0...v5.11.1) (2024-06-07)
|
5
|
+
|
6
|
+
|
7
|
+
### Bug Fixes
|
8
|
+
|
9
|
+
* do GoodJob.on_thread_error check via hash instead of method ([#558](https://github.com/honeybadger-io/honeybadger-ruby/issues/558)) ([d2aa464](https://github.com/honeybadger-io/honeybadger-ruby/commit/d2aa4640e371e3985310fb30ad5a356807d2bab3))
|
10
|
+
|
11
|
+
## [5.11.0](https://github.com/honeybadger-io/honeybadger-ruby/compare/v5.10.2...v5.11.0) (2024-06-04)
|
12
|
+
|
13
|
+
|
14
|
+
### Features
|
15
|
+
|
16
|
+
* add insights instrumentation - events and metrics ([#539](https://github.com/honeybadger-io/honeybadger-ruby/issues/539)) ([d173ac5](https://github.com/honeybadger-io/honeybadger-ruby/commit/d173ac53b45be6b9036c292d8efc5002d8b354b1))
|
17
|
+
|
18
|
+
|
19
|
+
### Bug Fixes
|
20
|
+
|
21
|
+
* access GoodJob config via Rails.application.config ([#554](https://github.com/honeybadger-io/honeybadger-ruby/issues/554)) ([37b7786](https://github.com/honeybadger-io/honeybadger-ruby/commit/37b7786e9fefdaa23ccd45ca55a0573b0a832f58))
|
22
|
+
|
4
23
|
## [5.10.2](https://github.com/honeybadger-io/honeybadger-ruby/compare/v5.10.1...v5.10.2) (2024-05-24)
|
5
24
|
|
6
25
|
|
data/lib/honeybadger/agent.rb
CHANGED
@@ -8,7 +8,10 @@ require 'honeybadger/plugin'
|
|
8
8
|
require 'honeybadger/logging'
|
9
9
|
require 'honeybadger/worker'
|
10
10
|
require 'honeybadger/events_worker'
|
11
|
+
require 'honeybadger/metrics_worker'
|
11
12
|
require 'honeybadger/breadcrumbs'
|
13
|
+
require 'honeybadger/registry'
|
14
|
+
require 'honeybadger/registry_execution'
|
12
15
|
|
13
16
|
module Honeybadger
|
14
17
|
# The Honeybadger agent contains all the methods for interacting with the
|
@@ -358,6 +361,7 @@ module Honeybadger
|
|
358
361
|
ensure
|
359
362
|
worker.flush
|
360
363
|
events_worker&.flush
|
364
|
+
metrics_worker&.flush
|
361
365
|
end
|
362
366
|
|
363
367
|
# Stops the Honeybadger service.
|
@@ -367,6 +371,7 @@ module Honeybadger
|
|
367
371
|
def stop(force = false)
|
368
372
|
worker.shutdown(force)
|
369
373
|
events_worker&.shutdown(force)
|
374
|
+
metrics_worker&.shutdown(force)
|
370
375
|
true
|
371
376
|
end
|
372
377
|
|
@@ -387,20 +392,46 @@ module Honeybadger
|
|
387
392
|
def event(event_type, payload = {})
|
388
393
|
init_events_worker
|
389
394
|
|
390
|
-
ts =
|
395
|
+
ts = Time.now.utc.strftime("%FT%T.%LZ")
|
391
396
|
merged = {ts: ts}
|
392
397
|
|
393
398
|
if event_type.is_a?(String)
|
394
|
-
merged
|
399
|
+
merged[:event_type] = event_type
|
395
400
|
else
|
396
401
|
merged.merge!(Hash(event_type))
|
397
402
|
end
|
398
403
|
|
404
|
+
if (request_id = context_manager.get_request_id)
|
405
|
+
merged[:request_id] = request_id
|
406
|
+
end
|
407
|
+
|
408
|
+
if config[:'events.attach_hostname']
|
409
|
+
merged[:hostname] = config[:hostname].to_s
|
410
|
+
end
|
411
|
+
|
399
412
|
merged.merge!(Hash(payload))
|
400
413
|
|
414
|
+
return if config.ignored_events.any? { |check| merged[:event_type]&.match?(check) }
|
415
|
+
|
401
416
|
events_worker.push(merged)
|
402
417
|
end
|
403
418
|
|
419
|
+
# @api private
|
420
|
+
def collect(collector)
|
421
|
+
return unless config.insights_enabled?
|
422
|
+
|
423
|
+
init_metrics_worker
|
424
|
+
metrics_worker.push(collector)
|
425
|
+
end
|
426
|
+
|
427
|
+
# @api private
|
428
|
+
def registry
|
429
|
+
return @registry if defined?(@registry)
|
430
|
+
@registry = Honeybadger::Registry.new.tap do |r|
|
431
|
+
collect(Honeybadger::RegistryExecution.new(r, config, {}))
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
404
435
|
# @api private
|
405
436
|
attr_reader :config
|
406
437
|
|
@@ -467,13 +498,15 @@ module Honeybadger
|
|
467
498
|
# @api private
|
468
499
|
def with_rack_env(rack_env, &block)
|
469
500
|
context_manager.set_rack_env(rack_env)
|
501
|
+
context_manager.set_request_id(rack_env["action_dispatch.request_id"] || SecureRandom.uuid)
|
470
502
|
yield
|
471
503
|
ensure
|
472
504
|
context_manager.set_rack_env(nil)
|
505
|
+
context_manager.set_request_id(nil)
|
473
506
|
end
|
474
507
|
|
475
508
|
# @api private
|
476
|
-
attr_reader :worker, :events_worker
|
509
|
+
attr_reader :worker, :events_worker, :metrics_worker
|
477
510
|
|
478
511
|
# @api private
|
479
512
|
# @!method init!(...)
|
@@ -485,6 +518,35 @@ module Honeybadger
|
|
485
518
|
# @see Config#backend
|
486
519
|
def_delegators :config, :backend
|
487
520
|
|
521
|
+
# @api private
|
522
|
+
# @!method time
|
523
|
+
# @see Honeybadger::Instrumentation#time
|
524
|
+
def_delegator :instrumentation, :time
|
525
|
+
|
526
|
+
# @api private
|
527
|
+
# @!method histogram
|
528
|
+
# @see Honeybadger::Instrumentation#histogram
|
529
|
+
def_delegator :instrumentation, :histogram
|
530
|
+
|
531
|
+
# @api private
|
532
|
+
# @!method gauge
|
533
|
+
# @see Honeybadger::Instrumentation#gauge
|
534
|
+
def_delegator :instrumentation, :gauge
|
535
|
+
|
536
|
+
# @api private
|
537
|
+
# @!method increment_counter
|
538
|
+
# @see Honeybadger::Instrumentation#increment_counter
|
539
|
+
def_delegator :instrumentation, :increment_counter
|
540
|
+
|
541
|
+
# @api private
|
542
|
+
# @!method decrement_counter
|
543
|
+
# @see Honeybadger::Instrumentation#decrement_counter
|
544
|
+
def_delegator :instrumentation, :decrement_counter
|
545
|
+
|
546
|
+
def instrumentation
|
547
|
+
@instrumentation ||= Honeybadger::Instrumentation.new(self)
|
548
|
+
end
|
549
|
+
|
488
550
|
private
|
489
551
|
|
490
552
|
def validate_notify_opts!(opts)
|
@@ -520,6 +582,11 @@ module Honeybadger
|
|
520
582
|
@events_worker = EventsWorker.new(config)
|
521
583
|
end
|
522
584
|
|
585
|
+
def init_metrics_worker
|
586
|
+
return if @metrics_worker
|
587
|
+
@metrics_worker = MetricsWorker.new(config)
|
588
|
+
end
|
589
|
+
|
523
590
|
def with_error_handling
|
524
591
|
yield
|
525
592
|
rescue => ex
|
@@ -91,9 +91,14 @@ module Honeybadger
|
|
91
91
|
default: 100,
|
92
92
|
type: Integer
|
93
93
|
},
|
94
|
+
:'events.max_queue_size' => {
|
95
|
+
description: 'Maximum number of event for the event worker queue.',
|
96
|
+
default: 10000,
|
97
|
+
type: Integer
|
98
|
+
},
|
94
99
|
:'events.batch_size' => {
|
95
100
|
description: 'Send events batch if n events have accumulated',
|
96
|
-
default:
|
101
|
+
default: 1000,
|
97
102
|
type: Integer
|
98
103
|
},
|
99
104
|
:'events.timeout' => {
|
@@ -101,6 +106,16 @@ module Honeybadger
|
|
101
106
|
default: 30_000,
|
102
107
|
type: Integer
|
103
108
|
},
|
109
|
+
:'events.attach_hostname' => {
|
110
|
+
description: 'Add the hostname to all event paylaods.',
|
111
|
+
default: true,
|
112
|
+
type: Boolean
|
113
|
+
},
|
114
|
+
:'events.ignore' => {
|
115
|
+
description: 'A list of events to ignore. Use a string to specify exact matches, or regex for more flexibility.',
|
116
|
+
default: [],
|
117
|
+
type: Array
|
118
|
+
},
|
104
119
|
plugins: {
|
105
120
|
description: 'An optional list of plugins to load. Default is to load all plugins.',
|
106
121
|
default: nil,
|
@@ -311,6 +326,36 @@ module Honeybadger
|
|
311
326
|
default: true,
|
312
327
|
type: Boolean
|
313
328
|
},
|
329
|
+
:'sidekiq.insights.cluster_collection' => {
|
330
|
+
description: 'Collect cluster based metrics for Sidekiq.',
|
331
|
+
default: true,
|
332
|
+
type: Boolean
|
333
|
+
},
|
334
|
+
:'sidekiq.insights.collection_interval' => {
|
335
|
+
description: 'The frequency in which Sidekiq cluster metrics are sampled.',
|
336
|
+
default: 60,
|
337
|
+
type: Integer
|
338
|
+
},
|
339
|
+
:'solid_queue.insights.cluster_collection' => {
|
340
|
+
description: 'Collect cluster based metrics for SolidQueue.',
|
341
|
+
default: true,
|
342
|
+
type: Boolean
|
343
|
+
},
|
344
|
+
:'solid_queue.insights.collection_interval' => {
|
345
|
+
description: 'The frequency in which SolidQueue cluster metrics are sampled.',
|
346
|
+
default: 60,
|
347
|
+
type: Integer
|
348
|
+
},
|
349
|
+
:'net_http.insights.enabled' => {
|
350
|
+
description: 'Allow automatic instrumentation of Net::HTTP requests.',
|
351
|
+
default: true,
|
352
|
+
type: Boolean
|
353
|
+
},
|
354
|
+
:'net_http.insights.full_url' => {
|
355
|
+
description: 'Record the full request url during instrumentation.',
|
356
|
+
default: false,
|
357
|
+
type: Boolean
|
358
|
+
},
|
314
359
|
:'sinatra.enabled' => {
|
315
360
|
description: 'Enable Sinatra auto-initialization.',
|
316
361
|
default: true,
|
@@ -343,6 +388,16 @@ module Honeybadger
|
|
343
388
|
description: 'Enable/Disable automatic breadcrumbs from log messages.',
|
344
389
|
default: true,
|
345
390
|
type: Boolean
|
391
|
+
},
|
392
|
+
:'insights.enabled' => {
|
393
|
+
description: "Enable/Disable Honeybadger Insights built-in instrumentation.",
|
394
|
+
default: false,
|
395
|
+
type: Boolean
|
396
|
+
},
|
397
|
+
:'insights.registry_flush_interval' => {
|
398
|
+
description: "Number of seconds between registry flushes.",
|
399
|
+
default: 60,
|
400
|
+
type: Integer
|
346
401
|
}
|
347
402
|
}.freeze
|
348
403
|
|
data/lib/honeybadger/config.rb
CHANGED
@@ -188,6 +188,12 @@ module Honeybadger
|
|
188
188
|
DEFAULTS[:'exceptions.ignore'] | Array(ignore)
|
189
189
|
end
|
190
190
|
|
191
|
+
def ignored_events
|
192
|
+
self[:'events.ignore'].map do |check|
|
193
|
+
check.is_a?(String) ? /^#{check}$/ : check
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
191
197
|
def ca_bundle_path
|
192
198
|
if self[:'connection.system_ssl_cert_chain'] && File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE)
|
193
199
|
OpenSSL::X509::DEFAULT_CERT_FILE
|
@@ -224,6 +230,10 @@ module Honeybadger
|
|
224
230
|
self[:max_queue_size]
|
225
231
|
end
|
226
232
|
|
233
|
+
def events_max_queue_size
|
234
|
+
self[:'events.max_queue_size']
|
235
|
+
end
|
236
|
+
|
227
237
|
def events_batch_size
|
228
238
|
self[:'events.batch_size']
|
229
239
|
end
|
@@ -262,6 +272,28 @@ module Honeybadger
|
|
262
272
|
includes_token?(self[:plugins], name)
|
263
273
|
end
|
264
274
|
|
275
|
+
def insights_enabled?
|
276
|
+
return false if defined?(::Rails.application) && ::Rails.const_defined?("Console")
|
277
|
+
!!self[:'insights.enabled']
|
278
|
+
end
|
279
|
+
|
280
|
+
def cluster_collection?(name)
|
281
|
+
return false unless insights_enabled?
|
282
|
+
return true if self[:"#{name}.insights.cluster_collection"].nil?
|
283
|
+
!!self[:"#{name}.insights.cluster_collection"]
|
284
|
+
end
|
285
|
+
|
286
|
+
def collection_interval(name)
|
287
|
+
return nil unless insights_enabled?
|
288
|
+
self[:"#{name}.insights.collection_interval"]
|
289
|
+
end
|
290
|
+
|
291
|
+
def load_plugin_insights?(name)
|
292
|
+
return false unless insights_enabled?
|
293
|
+
return true if self[:"#{name}.insights.enabled"].nil?
|
294
|
+
!!self[:"#{name}.insights.enabled"]
|
295
|
+
end
|
296
|
+
|
265
297
|
def root_regexp
|
266
298
|
return @root_regexp if @root_regexp
|
267
299
|
return nil if @no_root
|
@@ -61,15 +61,24 @@ module Honeybadger
|
|
61
61
|
@mutex.synchronize { @rack_env }
|
62
62
|
end
|
63
63
|
|
64
|
+
def set_request_id(request_id)
|
65
|
+
@mutex.synchronize { @request_id = request_id }
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_request_id
|
69
|
+
@mutex.synchronize { @request_id }
|
70
|
+
end
|
71
|
+
|
64
72
|
private
|
65
73
|
|
66
|
-
attr_accessor :custom, :rack_env
|
74
|
+
attr_accessor :custom, :rack_env, :request_id
|
67
75
|
|
68
76
|
def _initialize
|
69
77
|
@mutex.synchronize do
|
70
78
|
@global_context = nil
|
71
79
|
@local_context = nil
|
72
80
|
@rack_env = nil
|
81
|
+
@request_id = nil
|
73
82
|
end
|
74
83
|
end
|
75
84
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'honeybadger/metric'
|
2
|
+
|
3
|
+
module Honeybadger
|
4
|
+
class Counter < Metric
|
5
|
+
def count(by=1)
|
6
|
+
return unless by
|
7
|
+
|
8
|
+
@samples += 1
|
9
|
+
|
10
|
+
@counter ||= 0
|
11
|
+
@counter = @counter + by
|
12
|
+
end
|
13
|
+
|
14
|
+
def payloads
|
15
|
+
[{ counter: @counter }]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -44,7 +44,7 @@ module Honeybadger
|
|
44
44
|
def push(msg)
|
45
45
|
return false unless start
|
46
46
|
|
47
|
-
if queue.size >= config.
|
47
|
+
if queue.size >= config.events_max_queue_size
|
48
48
|
warn { sprintf('Unable to send event; reached max queue size of %s.', queue.size) }
|
49
49
|
return false
|
50
50
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'honeybadger/metric'
|
2
|
+
|
3
|
+
module Honeybadger
|
4
|
+
class Gauge < Metric
|
5
|
+
def record(value)
|
6
|
+
return unless value
|
7
|
+
|
8
|
+
@samples += 1
|
9
|
+
|
10
|
+
@total ||= 0
|
11
|
+
@total = @total + value
|
12
|
+
|
13
|
+
@min = value if @min.nil? || @min > value
|
14
|
+
@max = value if @max.nil? || @max < value
|
15
|
+
@avg = @total.to_f / @samples
|
16
|
+
@latest = value
|
17
|
+
end
|
18
|
+
|
19
|
+
def payloads
|
20
|
+
[
|
21
|
+
{
|
22
|
+
min: @min,
|
23
|
+
max: @max,
|
24
|
+
avg: @avg,
|
25
|
+
latest: @latest
|
26
|
+
}
|
27
|
+
]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'honeybadger/metric'
|
2
|
+
|
3
|
+
module Honeybadger
|
4
|
+
class Histogram < Metric
|
5
|
+
DEFAULT_BINS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
|
6
|
+
INFINITY = 1e20.to_f # not quite, but pretty much
|
7
|
+
|
8
|
+
def record(value)
|
9
|
+
return unless value
|
10
|
+
|
11
|
+
@samples += 1
|
12
|
+
@bin_counts ||= Hash.new(0)
|
13
|
+
@bin_counts[find_bin(value)] += 1
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_bin(value)
|
17
|
+
bin = bins.find {|b| b >= value }
|
18
|
+
bin = INFINITY if bin.nil?
|
19
|
+
bin
|
20
|
+
end
|
21
|
+
|
22
|
+
def bins
|
23
|
+
@attributes.fetch(:bins, DEFAULT_BINS).sort
|
24
|
+
end
|
25
|
+
|
26
|
+
def payloads
|
27
|
+
[{
|
28
|
+
bins: (bins + [INFINITY]).map { |bin| [bin.to_f, @bin_counts[bin]] }
|
29
|
+
}]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'honeybadger/histogram'
|
2
|
+
require 'honeybadger/timer'
|
3
|
+
require 'honeybadger/counter'
|
4
|
+
require 'honeybadger/gauge'
|
5
|
+
|
6
|
+
module Honeybadger
|
7
|
+
# +Honeybadger::Instrumentation+ defines the API for collecting metric data from anywhere
|
8
|
+
# in an application. These class methods may be used directly, or from the Honeybadger singleton
|
9
|
+
# instance. There are three usage variations as show in the example below:
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
#
|
13
|
+
# class TicketsController < ApplicationController
|
14
|
+
# def create
|
15
|
+
# # pass a block
|
16
|
+
# Honeybadger.time('create.ticket') { Ticket.create(params[:ticket]) }
|
17
|
+
#
|
18
|
+
# # pass a lambda argument
|
19
|
+
# Honeybadger.time 'create.ticket', ->{ Ticket.create(params[:ticket]) }
|
20
|
+
#
|
21
|
+
# # pass the duration argument
|
22
|
+
# duration = timing_method { Ticket.create(params[:ticket]) }
|
23
|
+
# Honeybadger.time 'create.ticket', duration: duration
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
#
|
28
|
+
class Instrumentation
|
29
|
+
attr_reader :agent
|
30
|
+
|
31
|
+
def initialize(agent)
|
32
|
+
@agent = agent
|
33
|
+
end
|
34
|
+
|
35
|
+
def registry
|
36
|
+
agent.registry
|
37
|
+
end
|
38
|
+
|
39
|
+
# returns two parameters, the first is the duration of the execution, and the second is
|
40
|
+
# the return value of the passed block
|
41
|
+
def monotonic_timer
|
42
|
+
start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
43
|
+
result = yield
|
44
|
+
finish_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
45
|
+
[((finish_time - start_time) * 1000).round(2), result]
|
46
|
+
end
|
47
|
+
|
48
|
+
def time(name, *args)
|
49
|
+
attributes = extract_attributes(args)
|
50
|
+
callable = extract_callable(args)
|
51
|
+
duration = attributes.delete(:duration)
|
52
|
+
|
53
|
+
if callable
|
54
|
+
duration = monotonic_timer{ callable.call }[0]
|
55
|
+
elsif block_given?
|
56
|
+
duration = monotonic_timer{ yield }[0]
|
57
|
+
end
|
58
|
+
|
59
|
+
raise 'No duration found' if duration.nil?
|
60
|
+
|
61
|
+
Honeybadger::Timer.register(registry, name, attributes).tap do |timer|
|
62
|
+
timer.record(duration)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def histogram(name, *args)
|
67
|
+
attributes = extract_attributes(args)
|
68
|
+
callable = extract_callable(args)
|
69
|
+
duration = attributes.delete(:duration)
|
70
|
+
|
71
|
+
if callable
|
72
|
+
duration = monotonic_timer{ callable.call }[0]
|
73
|
+
elsif block_given?
|
74
|
+
duration = monotonic_timer{ yield }[0]
|
75
|
+
end
|
76
|
+
|
77
|
+
raise 'No duration found' if duration.nil?
|
78
|
+
|
79
|
+
Honeybadger::Histogram.register(registry, name, attributes).tap do |histogram|
|
80
|
+
histogram.record(duration)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def increment_counter(name, *args)
|
85
|
+
attributes = extract_attributes(args)
|
86
|
+
by = extract_callable(args)&.call || attributes.delete(:by) || 1
|
87
|
+
by = yield if block_given?
|
88
|
+
|
89
|
+
Honeybadger::Counter.register(registry, name, attributes).tap do |counter|
|
90
|
+
counter.count(by)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def decrement_counter(name, *args)
|
95
|
+
attributes = extract_attributes(args)
|
96
|
+
by = extract_callable(args)&.call || attributes.delete(:by) || 1
|
97
|
+
by = yield if block_given?
|
98
|
+
|
99
|
+
Honeybadger::Counter.register(registry, name, attributes).tap do |counter|
|
100
|
+
counter.count(by * -1)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def gauge(name, *args)
|
105
|
+
attributes = extract_attributes(args)
|
106
|
+
value = extract_callable(args)&.call || attributes.delete(:value)
|
107
|
+
value = yield if block_given?
|
108
|
+
|
109
|
+
Honeybadger::Gauge.register(registry, name, attributes).tap do |gauge|
|
110
|
+
gauge.record(value)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# @api private
|
115
|
+
def extract_attributes(args)
|
116
|
+
args.select { |a| a.is_a?(Hash) }.first || {}
|
117
|
+
end
|
118
|
+
|
119
|
+
# @api private
|
120
|
+
def extract_callable(args)
|
121
|
+
args.select { |a| a.respond_to?(:call) }.first
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'honeybadger/instrumentation'
|
2
|
+
|
3
|
+
module Honeybadger
|
4
|
+
# +Honeybadger::InstrumentationHelper+ is a module that can be included into any class. This module
|
5
|
+
# provides a convenient DSL around the instrumentation methods to prvoide a cleaner interface.
|
6
|
+
# There are three usage variations as show in the example below:
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# class TicketsController < ApplicationController
|
11
|
+
# include Honeybadger::InstrumentationHelper
|
12
|
+
#
|
13
|
+
# def create
|
14
|
+
# metric_source 'controller'
|
15
|
+
# metric_attributes { foo: 'bar' } # These attributes get tagged to all metrics called after.
|
16
|
+
#
|
17
|
+
# # pass a block
|
18
|
+
# time('create.ticket') { Ticket.create(params[:ticket]) }
|
19
|
+
#
|
20
|
+
# # pass a lambda argument
|
21
|
+
# time 'create.ticket', ->{ Ticket.create(params[:ticket]) }
|
22
|
+
#
|
23
|
+
# # pass the duration argument
|
24
|
+
# duration = timing_method { Ticket.create(params[:ticket]) }
|
25
|
+
# time 'create.ticket', duration: duration
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
#
|
30
|
+
module InstrumentationHelper
|
31
|
+
|
32
|
+
# returns two parameters, the first is the duration of the execution, and the second is
|
33
|
+
# the return value of the passed block
|
34
|
+
def monotonic_timer
|
35
|
+
metric_instrumentation.monotonic_timer { yield }
|
36
|
+
end
|
37
|
+
|
38
|
+
def metric_source(source)
|
39
|
+
@metric_source = source
|
40
|
+
end
|
41
|
+
|
42
|
+
def metric_agent(agent)
|
43
|
+
@metric_agent = agent
|
44
|
+
end
|
45
|
+
|
46
|
+
def metric_instrumentation
|
47
|
+
@metric_instrumentation ||= @metric_agent ? Honeybadger::Instrumentation.new(@metric_agent) : Honeybadger.instrumentation
|
48
|
+
end
|
49
|
+
|
50
|
+
def metric_attributes(attributes)
|
51
|
+
raise "metric_attributes expects a hash" unless attributes.is_a?(Hash)
|
52
|
+
@metric_attributes = attributes
|
53
|
+
end
|
54
|
+
|
55
|
+
def time(name, *args)
|
56
|
+
attributes = extract_attributes(args)
|
57
|
+
callable = extract_callable(args)
|
58
|
+
if callable
|
59
|
+
metric_instrumentation.time(name, attributes, ->{ callable.call })
|
60
|
+
elsif block_given?
|
61
|
+
metric_instrumentation.time(name, attributes, ->{ yield })
|
62
|
+
elsif attributes.keys.include?(:duration)
|
63
|
+
metric_instrumentation.time(name, attributes)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def histogram(name, *args)
|
68
|
+
attributes = extract_attributes(args)
|
69
|
+
callable = extract_callable(args)
|
70
|
+
if callable
|
71
|
+
metric_instrumentation.histogram(name, attributes, ->{ callable.call })
|
72
|
+
elsif block_given?
|
73
|
+
metric_instrumentation.histogram(name, attributes, ->{ yield })
|
74
|
+
elsif attributes.keys.include?(:duration)
|
75
|
+
metric_instrumentation.histogram(name, attributes)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def increment_counter(name, *args)
|
80
|
+
attributes = extract_attributes(args)
|
81
|
+
by = extract_callable(args)&.call || attributes.delete(:by) || 1
|
82
|
+
if block_given?
|
83
|
+
metric_instrumentation.increment_counter(name, attributes, ->{ yield })
|
84
|
+
else
|
85
|
+
metric_instrumentation.increment_counter(name, attributes.merge(by: by))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def decrement_counter(name, *args)
|
90
|
+
attributes = extract_attributes(args)
|
91
|
+
by = extract_callable(args)&.call || attributes.delete(:by) || 1
|
92
|
+
if block_given?
|
93
|
+
metric_instrumentation.decrement_counter(name, attributes, ->{ yield })
|
94
|
+
else
|
95
|
+
metric_instrumentation.decrement_counter(name, attributes.merge(by: by))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def gauge(name, *args)
|
100
|
+
attributes = extract_attributes(args)
|
101
|
+
value = extract_callable(args)&.call || attributes.delete(:value)
|
102
|
+
if block_given?
|
103
|
+
metric_instrumentation.gauge(name, attributes, ->{ yield })
|
104
|
+
else
|
105
|
+
metric_instrumentation.gauge(name, attributes.merge(value: value))
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# @api private
|
110
|
+
def extract_attributes(args)
|
111
|
+
attributes = metric_instrumentation.extract_attributes(args)
|
112
|
+
attributes.merge(metric_source: @metric_source).merge(@metric_attributes || {}).compact
|
113
|
+
end
|
114
|
+
|
115
|
+
# @api private
|
116
|
+
def extract_callable(args)
|
117
|
+
metric_instrumentation.extract_callable(args)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|