honeybadger 5.17.0 → 5.19.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 +14 -0
- data/lib/honeybadger/config/defaults.rb +20 -0
- data/lib/honeybadger/config.rb +14 -0
- data/lib/honeybadger/histogram.rb +13 -0
- data/lib/honeybadger/init/rails.rb +1 -1
- data/lib/honeybadger/instrumentation.rb +46 -18
- data/lib/honeybadger/instrumentation_helper.rb +17 -11
- data/lib/honeybadger/karafka.rb +298 -0
- data/lib/honeybadger/plugins/karafka.rb +6 -17
- data/lib/honeybadger/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf4c3296c72440877caccd2ada861f4060af897ed517688fb838cd57ae0a58ff
|
4
|
+
data.tar.gz: 25d45250d1f2d2b0fadfa37637446316f12588f3dd134695803c3a068a0513d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c66b0c6fa819e716b18a710c1e11fe8c95d25e06ca69216efb93fb2d4bffd1d65c46e7b00b658c0321ac6608817dec2c894eb68bcc0bf39da858c37c0b96302
|
7
|
+
data.tar.gz: 6ef30a79138840ade5fef06ef5771f96c4a94ea942b6154b322632f73d71f8c72745be940e25c6aa19187411897c528e5f3e0325c3605cce0861cc4a6aa73a9b
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,20 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
3
|
|
4
|
+
## [5.19.0](https://github.com/honeybadger-io/honeybadger-ruby/compare/v5.18.0...v5.19.0) (2024-10-29)
|
5
|
+
|
6
|
+
|
7
|
+
### Features
|
8
|
+
|
9
|
+
* improved karafka plugin for insights ([#625](https://github.com/honeybadger-io/honeybadger-ruby/issues/625)) ([21880fc](https://github.com/honeybadger-io/honeybadger-ruby/commit/21880fc92493abb044ba3c0d64299ad8b92ad5f3))
|
10
|
+
|
11
|
+
## [5.18.0](https://github.com/honeybadger-io/honeybadger-ruby/compare/v5.17.0...v5.18.0) (2024-10-25)
|
12
|
+
|
13
|
+
|
14
|
+
### Features
|
15
|
+
|
16
|
+
* add a separate configuration for Insights in a console ([#623](https://github.com/honeybadger-io/honeybadger-ruby/issues/623)) ([a89488e](https://github.com/honeybadger-io/honeybadger-ruby/commit/a89488e535ff04ec2e362807f0001471ba5c6fe3))
|
17
|
+
|
4
18
|
## [5.17.0](https://github.com/honeybadger-io/honeybadger-ruby/compare/v5.16.0...v5.17.0) (2024-10-22)
|
5
19
|
|
6
20
|
|
@@ -363,6 +363,21 @@ module Honeybadger
|
|
363
363
|
default: 60,
|
364
364
|
type: Integer
|
365
365
|
},
|
366
|
+
:'karafka.insights.enabled' => {
|
367
|
+
description: 'Enable automatic data collection for Karafka.',
|
368
|
+
default: true,
|
369
|
+
type: Boolean
|
370
|
+
},
|
371
|
+
:'karafka.insights.events' => {
|
372
|
+
description: 'Enable automatic event capturing for Karafka.',
|
373
|
+
default: true,
|
374
|
+
type: Boolean
|
375
|
+
},
|
376
|
+
:'karafka.insights.metrics' => {
|
377
|
+
description: 'Enable automatic metric data collection for Karafka.',
|
378
|
+
default: true,
|
379
|
+
type: Boolean
|
380
|
+
},
|
366
381
|
:'net_http.insights.enabled' => {
|
367
382
|
description: 'Allow automatic instrumentation of Net::HTTP requests.',
|
368
383
|
default: true,
|
@@ -411,6 +426,11 @@ module Honeybadger
|
|
411
426
|
default: false,
|
412
427
|
type: Boolean
|
413
428
|
},
|
429
|
+
:'insights.console.enabled' => {
|
430
|
+
description: "Enable/Disable Honeybadger Insights built-in instrumentation in a Rails console.",
|
431
|
+
default: false,
|
432
|
+
type: Boolean
|
433
|
+
},
|
414
434
|
:'insights.registry_flush_interval' => {
|
415
435
|
description: "Number of seconds between registry flushes.",
|
416
436
|
default: 60,
|
data/lib/honeybadger/config.rb
CHANGED
@@ -309,6 +309,20 @@ module Honeybadger
|
|
309
309
|
!!self[:"#{name}.insights.enabled"]
|
310
310
|
end
|
311
311
|
|
312
|
+
def load_plugin_insights_events?(name)
|
313
|
+
return false unless insights_enabled?
|
314
|
+
return false unless load_plugin_insights?(name)
|
315
|
+
return true if self[:"#{name}.insights.events"].nil?
|
316
|
+
!!self[:"#{name}.insights.events"]
|
317
|
+
end
|
318
|
+
|
319
|
+
def load_plugin_insights_metrics?(name)
|
320
|
+
return false unless insights_enabled?
|
321
|
+
return false unless load_plugin_insights?(name)
|
322
|
+
return true if self[:"#{name}.insights.metrics"].nil?
|
323
|
+
!!self[:"#{name}.insights.metrics"]
|
324
|
+
end
|
325
|
+
|
312
326
|
def root_regexp
|
313
327
|
return @root_regexp if @root_regexp
|
314
328
|
return nil if @no_root
|
@@ -9,6 +9,15 @@ module Honeybadger
|
|
9
9
|
return unless value
|
10
10
|
|
11
11
|
@samples += 1
|
12
|
+
|
13
|
+
@total ||= 0
|
14
|
+
@total = @total + value
|
15
|
+
|
16
|
+
@min = value if @min.nil? || @min > value
|
17
|
+
@max = value if @max.nil? || @max < value
|
18
|
+
@avg = @total.to_f / @samples
|
19
|
+
@latest = value
|
20
|
+
|
12
21
|
@bin_counts ||= Hash.new(0)
|
13
22
|
@bin_counts[find_bin(value)] += 1
|
14
23
|
end
|
@@ -25,6 +34,10 @@ module Honeybadger
|
|
25
34
|
|
26
35
|
def payloads
|
27
36
|
[{
|
37
|
+
min: @min,
|
38
|
+
max: @max,
|
39
|
+
avg: @avg,
|
40
|
+
latest: @latest,
|
28
41
|
bins: (bins + [INFINITY]).map { |bin| [bin.to_f, @bin_counts[bin]] }
|
29
42
|
}]
|
30
43
|
end
|
@@ -34,7 +34,7 @@ module Honeybadger
|
|
34
34
|
end
|
35
35
|
|
36
36
|
console do
|
37
|
-
Honeybadger::Agent.instance.config[:'insights.enabled'] =
|
37
|
+
Honeybadger::Agent.instance.config[:'insights.enabled'] = Honeybadger::Agent.instance.config[:'insights.console.enabled']
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -47,63 +47,91 @@ module Honeybadger
|
|
47
47
|
def time(name, *args)
|
48
48
|
attributes = extract_attributes(args)
|
49
49
|
callable = extract_callable(args)
|
50
|
-
|
50
|
+
value = nil
|
51
51
|
|
52
52
|
if callable
|
53
|
-
|
53
|
+
value = monotonic_timer{ callable.call }[0]
|
54
54
|
elsif block_given?
|
55
|
-
|
55
|
+
value = monotonic_timer{ yield }[0]
|
56
|
+
else
|
57
|
+
value = attributes.delete(:duration) || attributes.delete(:value)
|
56
58
|
end
|
57
59
|
|
58
|
-
raise 'No
|
60
|
+
raise 'No value found' if value.nil?
|
59
61
|
|
60
62
|
Honeybadger::Timer.register(registry, name, attributes).tap do |timer|
|
61
|
-
timer.record(
|
63
|
+
timer.record(value)
|
62
64
|
end
|
63
65
|
end
|
64
66
|
|
65
67
|
def histogram(name, *args)
|
66
68
|
attributes = extract_attributes(args)
|
67
69
|
callable = extract_callable(args)
|
68
|
-
|
70
|
+
value = nil
|
69
71
|
|
70
72
|
if callable
|
71
|
-
|
73
|
+
value = monotonic_timer{ callable.call }[0]
|
72
74
|
elsif block_given?
|
73
|
-
|
75
|
+
value = monotonic_timer{ yield }[0]
|
76
|
+
else
|
77
|
+
value = attributes.delete(:duration) || attributes.delete(:value)
|
74
78
|
end
|
75
79
|
|
76
|
-
raise 'No
|
80
|
+
raise 'No value found' if value.nil?
|
77
81
|
|
78
82
|
Honeybadger::Histogram.register(registry, name, attributes).tap do |histogram|
|
79
|
-
histogram.record(
|
83
|
+
histogram.record(value)
|
80
84
|
end
|
81
85
|
end
|
82
86
|
|
83
87
|
def increment_counter(name, *args)
|
84
88
|
attributes = extract_attributes(args)
|
85
|
-
|
86
|
-
|
89
|
+
callable = extract_callable(args)
|
90
|
+
value = nil
|
91
|
+
|
92
|
+
if callable
|
93
|
+
value = callable.call
|
94
|
+
elsif block_given?
|
95
|
+
value = yield
|
96
|
+
else
|
97
|
+
value = attributes.delete(:by) || attributes.delete(:value) || 1
|
98
|
+
end
|
87
99
|
|
88
100
|
Honeybadger::Counter.register(registry, name, attributes).tap do |counter|
|
89
|
-
counter.count(
|
101
|
+
counter.count(value)
|
90
102
|
end
|
91
103
|
end
|
92
104
|
|
93
105
|
def decrement_counter(name, *args)
|
94
106
|
attributes = extract_attributes(args)
|
95
|
-
|
96
|
-
|
107
|
+
callable = extract_callable(args)
|
108
|
+
value = nil
|
109
|
+
|
110
|
+
if callable
|
111
|
+
value = callable.call
|
112
|
+
elsif block_given?
|
113
|
+
value = yield
|
114
|
+
else
|
115
|
+
value = attributes.delete(:by) || attributes.delete(:value) || 1
|
116
|
+
end
|
97
117
|
|
98
118
|
Honeybadger::Counter.register(registry, name, attributes).tap do |counter|
|
99
|
-
counter.count(
|
119
|
+
counter.count(value * -1)
|
100
120
|
end
|
101
121
|
end
|
102
122
|
|
103
123
|
def gauge(name, *args)
|
104
124
|
attributes = extract_attributes(args)
|
105
|
-
|
106
|
-
value =
|
125
|
+
callable = extract_callable(args)
|
126
|
+
value = nil
|
127
|
+
|
128
|
+
if callable
|
129
|
+
value = callable.call
|
130
|
+
elsif block_given?
|
131
|
+
value = yield
|
132
|
+
else
|
133
|
+
value = attributes.delete(:value)
|
134
|
+
end
|
107
135
|
|
108
136
|
Honeybadger::Gauge.register(registry, name, attributes).tap do |gauge|
|
109
137
|
gauge.record(value)
|
@@ -56,7 +56,7 @@ module Honeybadger
|
|
56
56
|
metric_instrumentation.time(name, attributes, ->{ callable.call })
|
57
57
|
elsif block_given?
|
58
58
|
metric_instrumentation.time(name, attributes, ->{ yield })
|
59
|
-
|
59
|
+
else
|
60
60
|
metric_instrumentation.time(name, attributes)
|
61
61
|
end
|
62
62
|
end
|
@@ -68,38 +68,44 @@ module Honeybadger
|
|
68
68
|
metric_instrumentation.histogram(name, attributes, ->{ callable.call })
|
69
69
|
elsif block_given?
|
70
70
|
metric_instrumentation.histogram(name, attributes, ->{ yield })
|
71
|
-
|
71
|
+
else
|
72
72
|
metric_instrumentation.histogram(name, attributes)
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
76
|
def increment_counter(name, *args)
|
77
77
|
attributes = extract_attributes(args)
|
78
|
-
|
79
|
-
if
|
78
|
+
callable = extract_callable(args)
|
79
|
+
if callable
|
80
|
+
metric_instrumentation.increment_counter(name, attributes, ->{ callable.call })
|
81
|
+
elsif block_given?
|
80
82
|
metric_instrumentation.increment_counter(name, attributes, ->{ yield })
|
81
83
|
else
|
82
|
-
metric_instrumentation.increment_counter(name, attributes
|
84
|
+
metric_instrumentation.increment_counter(name, attributes)
|
83
85
|
end
|
84
86
|
end
|
85
87
|
|
86
88
|
def decrement_counter(name, *args)
|
87
89
|
attributes = extract_attributes(args)
|
88
|
-
|
89
|
-
if
|
90
|
+
callable = extract_callable(args)
|
91
|
+
if callable
|
92
|
+
metric_instrumentation.decrement_counter(name, attributes, ->{ callable.call })
|
93
|
+
elsif block_given?
|
90
94
|
metric_instrumentation.decrement_counter(name, attributes, ->{ yield })
|
91
95
|
else
|
92
|
-
metric_instrumentation.decrement_counter(name, attributes
|
96
|
+
metric_instrumentation.decrement_counter(name, attributes)
|
93
97
|
end
|
94
98
|
end
|
95
99
|
|
96
100
|
def gauge(name, *args)
|
97
101
|
attributes = extract_attributes(args)
|
98
|
-
|
99
|
-
if
|
102
|
+
callable = extract_callable(args)
|
103
|
+
if callable
|
104
|
+
metric_instrumentation.gauge(name, attributes, ->{ callable.call })
|
105
|
+
elsif block_given?
|
100
106
|
metric_instrumentation.gauge(name, attributes, ->{ yield })
|
101
107
|
else
|
102
|
-
metric_instrumentation.gauge(name, attributes
|
108
|
+
metric_instrumentation.gauge(name, attributes)
|
103
109
|
end
|
104
110
|
end
|
105
111
|
|
@@ -0,0 +1,298 @@
|
|
1
|
+
require 'honeybadger/instrumentation_helper'
|
2
|
+
|
3
|
+
module Honeybadger
|
4
|
+
module Karafka
|
5
|
+
class ErrorsListener
|
6
|
+
# Sends error details to Honeybadger
|
7
|
+
#
|
8
|
+
# @param event [Karafka::Core::Monitoring::Event]
|
9
|
+
def on_error_occurred(event)
|
10
|
+
context = {
|
11
|
+
type: event[:type]
|
12
|
+
}
|
13
|
+
tags = ["type:#{event[:type]}"]
|
14
|
+
|
15
|
+
if (consumer = event.payload[:caller]).respond_to?(:messages)
|
16
|
+
messages = consumer.messages
|
17
|
+
metadata = messages.metadata
|
18
|
+
consumer_group_id = consumer.topic.consumer_group.id
|
19
|
+
|
20
|
+
context[:topic] = metadata.topic
|
21
|
+
context[:partition] = metadata.partition
|
22
|
+
context[:consumer_group] = consumer_group_id
|
23
|
+
end
|
24
|
+
|
25
|
+
Honeybadger.notify(event[:error], context: context)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class InsightsListener
|
30
|
+
include ::Honeybadger::InstrumentationHelper
|
31
|
+
|
32
|
+
# Value object for storing a single rdkafka metric publishing details
|
33
|
+
RdKafkaMetric = Struct.new(:type, :scope, :name, :key_location)
|
34
|
+
|
35
|
+
# All the rdkafka metrics we want to publish
|
36
|
+
#
|
37
|
+
# By default we publish quite a lot so this can be tuned
|
38
|
+
# Note, that the once with `_d` come from Karafka, not rdkafka or Kafka
|
39
|
+
RD_KAFKA_METRICS = [
|
40
|
+
# Client metrics
|
41
|
+
RdKafkaMetric.new(:increment_counter, :root, 'messages_consumed', 'rxmsgs_d'),
|
42
|
+
RdKafkaMetric.new(:increment_counter, :root, 'messages_consumed_bytes', 'rxmsg_bytes'),
|
43
|
+
|
44
|
+
# Broker metrics
|
45
|
+
RdKafkaMetric.new(:increment_counter, :brokers, 'consume_attempts', 'txretries_d'),
|
46
|
+
RdKafkaMetric.new(:increment_counter, :brokers, 'consume_errors', 'txerrs_d'),
|
47
|
+
RdKafkaMetric.new(:increment_counter, :brokers, 'receive_errors', 'rxerrs_d'),
|
48
|
+
RdKafkaMetric.new(:increment_counter, :brokers, 'connection_connects', 'connects_d'),
|
49
|
+
RdKafkaMetric.new(:increment_counter, :brokers, 'connection_disconnects', 'disconnects_d'),
|
50
|
+
RdKafkaMetric.new(:gauge, :brokers, 'network_latency_avg', %w[rtt avg]),
|
51
|
+
RdKafkaMetric.new(:gauge, :brokers, 'network_latency_p95', %w[rtt p95]),
|
52
|
+
RdKafkaMetric.new(:gauge, :brokers, 'network_latency_p99', %w[rtt p99]),
|
53
|
+
|
54
|
+
# Topics metrics
|
55
|
+
RdKafkaMetric.new(:gauge, :topics, 'consumer_lags', 'consumer_lag_stored'),
|
56
|
+
RdKafkaMetric.new(:gauge, :topics, 'consumer_lags_delta', 'consumer_lag_stored_d')
|
57
|
+
].freeze
|
58
|
+
|
59
|
+
# Metrics that sum values on topics levels and not on partition levels
|
60
|
+
AGGREGATED_RD_KAFKA_METRICS = [
|
61
|
+
# Topic aggregated metrics
|
62
|
+
RdKafkaMetric.new(:gauge, :topics, 'consumer_aggregated_lag', 'consumer_lag_stored')
|
63
|
+
].freeze
|
64
|
+
|
65
|
+
def initialize
|
66
|
+
metric_source("karafka")
|
67
|
+
end
|
68
|
+
|
69
|
+
# Hooks up to Karafka instrumentation for emitted statistics
|
70
|
+
#
|
71
|
+
# @param event [Karafka::Core::Monitoring::Event]
|
72
|
+
def on_statistics_emitted(event)
|
73
|
+
return unless Honeybadger.config.load_plugin_insights_metrics?(:karafka)
|
74
|
+
|
75
|
+
statistics = event[:statistics]
|
76
|
+
consumer_group_id = event[:consumer_group_id]
|
77
|
+
|
78
|
+
base_tags = { consumer_group: consumer_group_id }
|
79
|
+
|
80
|
+
RD_KAFKA_METRICS.each do |metric|
|
81
|
+
report_metric(metric, statistics, base_tags)
|
82
|
+
end
|
83
|
+
|
84
|
+
report_aggregated_topics_metrics(statistics, consumer_group_id)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Publishes aggregated topic-level metrics that are sum of per partition metrics
|
88
|
+
#
|
89
|
+
# @param statistics [Hash] hash with all the statistics emitted
|
90
|
+
# @param consumer_group_id [String] cg in context which we operate
|
91
|
+
def report_aggregated_topics_metrics(statistics, consumer_group_id)
|
92
|
+
AGGREGATED_RD_KAFKA_METRICS.each do |metric|
|
93
|
+
statistics.fetch('topics').each do |topic_name, topic_values|
|
94
|
+
sum = 0
|
95
|
+
|
96
|
+
topic_values['partitions'].each do |partition_name, partition_statistics|
|
97
|
+
next if partition_name == '-1'
|
98
|
+
# Skip until lag info is available
|
99
|
+
next if partition_statistics['consumer_lag'] == -1
|
100
|
+
next if partition_statistics['consumer_lag_stored'] == -1
|
101
|
+
|
102
|
+
sum += partition_statistics.dig(*metric.key_location)
|
103
|
+
end
|
104
|
+
|
105
|
+
public_send(
|
106
|
+
metric.type,
|
107
|
+
metric.name,
|
108
|
+
value: sum,
|
109
|
+
consumer_group: consumer_group_id,
|
110
|
+
topic: topic_name
|
111
|
+
)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Increases the errors count by 1
|
117
|
+
#
|
118
|
+
# @param event [Karafka::Core::Monitoring::Event]
|
119
|
+
def on_error_occurred(event)
|
120
|
+
extra_tags = { type: event[:type] }
|
121
|
+
|
122
|
+
if event.payload[:caller].respond_to?(:messages)
|
123
|
+
extra_tags.merge!(consumer_tags(event.payload[:caller]))
|
124
|
+
end
|
125
|
+
|
126
|
+
if Honeybadger.config.load_plugin_insights_events?(:karafka)
|
127
|
+
Honeybadger.event("error.occurred.karafka", error: event[:error], **extra_tags)
|
128
|
+
end
|
129
|
+
|
130
|
+
if Honeybadger.config.load_plugin_insights_metrics?(:karafka)
|
131
|
+
increment_counter('error_occurred', value: 1, **extra_tags)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Reports how many messages we've polled and how much time did we spend on it
|
136
|
+
#
|
137
|
+
# @param event [Karafka::Core::Monitoring::Event]
|
138
|
+
def on_connection_listener_fetch_loop_received(event)
|
139
|
+
time_taken = event[:time]
|
140
|
+
messages_count = event[:messages_buffer].size
|
141
|
+
|
142
|
+
consumer_group_id = event[:subscription_group].consumer_group.id
|
143
|
+
|
144
|
+
extra_tags = { consumer_group: consumer_group_id }
|
145
|
+
|
146
|
+
if Honeybadger.config.load_plugin_insights_metrics?(:karafka)
|
147
|
+
histogram('listener_polling_time_taken', value: time_taken, **extra_tags)
|
148
|
+
histogram('listener_polling_messages', value: messages_count, **extra_tags)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Here we report majority of things related to processing as we have access to the
|
153
|
+
# consumer
|
154
|
+
# @param event [Karafka::Core::Monitoring::Event]
|
155
|
+
def on_consumer_consumed(event)
|
156
|
+
consumer = event.payload[:caller]
|
157
|
+
messages = consumer.messages
|
158
|
+
metadata = messages.metadata
|
159
|
+
|
160
|
+
tags = consumer_tags(consumer)
|
161
|
+
|
162
|
+
if Honeybadger.config.load_plugin_insights_events?(:karafka)
|
163
|
+
event_context = tags.merge({
|
164
|
+
consumer: consumer.class.name,
|
165
|
+
duration: event[:time],
|
166
|
+
processing_lag: metadata.processing_lag,
|
167
|
+
consumption_lag: metadata.consumption_lag,
|
168
|
+
processed: messages.count
|
169
|
+
})
|
170
|
+
Honeybadger.event("consumer.consumed.karafka", event_context)
|
171
|
+
end
|
172
|
+
|
173
|
+
if Honeybadger.config.load_plugin_insights_metrics?(:karafka)
|
174
|
+
increment_counter('consumer_messages', value: messages.count, **tags)
|
175
|
+
increment_counter('consumer_batches', value: 1, **tags)
|
176
|
+
gauge('consumer_offset', value: metadata.last_offset, **tags)
|
177
|
+
histogram('consumer_consumed_time_taken', value: event[:time], **tags)
|
178
|
+
histogram('consumer_batch_size', value: messages.count, **tags)
|
179
|
+
histogram('consumer_processing_lag', value: metadata.processing_lag, **tags)
|
180
|
+
histogram('consumer_consumption_lag', value: metadata.consumption_lag, **tags)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
{
|
185
|
+
revoked: :revoked,
|
186
|
+
shutdown: :shutdown,
|
187
|
+
ticked: :tick
|
188
|
+
}.each do |after, name|
|
189
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
190
|
+
# Keeps track of user code execution
|
191
|
+
#
|
192
|
+
# @param event [Karafka::Core::Monitoring::Event]
|
193
|
+
def on_consumer_#{after}(event)
|
194
|
+
tags = consumer_tags(event.payload[:caller])
|
195
|
+
|
196
|
+
increment_counter('consumer_#{name}', value: 1, **tags)
|
197
|
+
end
|
198
|
+
RUBY
|
199
|
+
end
|
200
|
+
|
201
|
+
# Worker related metrics
|
202
|
+
# @param event [Karafka::Core::Monitoring::Event]
|
203
|
+
def on_worker_process(event)
|
204
|
+
jq_stats = event[:jobs_queue].statistics
|
205
|
+
|
206
|
+
if Honeybadger.config.load_plugin_insights_metrics?(:karafka)
|
207
|
+
gauge('worker_total_threads', value: ::Karafka::App.config.concurrency)
|
208
|
+
histogram('worker_processing', value: jq_stats[:busy])
|
209
|
+
histogram('worker_enqueued_jobs', value: jq_stats[:enqueued])
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# We report this metric before and after processing for higher accuracy
|
214
|
+
# Without this, the utilization would not be fully reflected
|
215
|
+
# @param event [Karafka::Core::Monitoring::Event]
|
216
|
+
def on_worker_processed(event)
|
217
|
+
jq_stats = event[:jobs_queue].statistics
|
218
|
+
|
219
|
+
if Honeybadger.config.load_plugin_insights_metrics?(:karafka)
|
220
|
+
histogram('worker_processing', value: jq_stats[:busy])
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
private
|
225
|
+
|
226
|
+
# Reports a given metric statistics to Honeybadger
|
227
|
+
# @param metric [RdKafkaMetric] metric value object
|
228
|
+
# @param statistics [Hash] hash with all the statistics emitted
|
229
|
+
# @param base_tags [Array<String>] base tags we want to start with
|
230
|
+
def report_metric(metric, statistics, base_tags)
|
231
|
+
case metric.scope
|
232
|
+
when :root
|
233
|
+
public_send(
|
234
|
+
metric.type,
|
235
|
+
metric.name,
|
236
|
+
value: statistics.fetch(*metric.key_location),
|
237
|
+
**base_tags
|
238
|
+
)
|
239
|
+
when :brokers
|
240
|
+
statistics.fetch('brokers').each_value do |broker_statistics|
|
241
|
+
# Skip bootstrap nodes
|
242
|
+
# Bootstrap nodes have nodeid -1, other nodes have positive
|
243
|
+
# node ids
|
244
|
+
next if broker_statistics['nodeid'] == -1
|
245
|
+
|
246
|
+
public_send(
|
247
|
+
metric.type,
|
248
|
+
metric.name,
|
249
|
+
value: broker_statistics.dig(*metric.key_location),
|
250
|
+
**base_tags.merge(broker: broker_statistics['nodename'])
|
251
|
+
)
|
252
|
+
end
|
253
|
+
when :topics
|
254
|
+
statistics.fetch('topics').each do |topic_name, topic_values|
|
255
|
+
topic_values['partitions'].each do |partition_name, partition_statistics|
|
256
|
+
next if partition_name == '-1'
|
257
|
+
# Skip until lag info is available
|
258
|
+
next if partition_statistics['consumer_lag'] == -1
|
259
|
+
next if partition_statistics['consumer_lag_stored'] == -1
|
260
|
+
|
261
|
+
# Skip if we do not own the fetch assignment
|
262
|
+
next if partition_statistics['fetch_state'] == 'stopped'
|
263
|
+
next if partition_statistics['fetch_state'] == 'none'
|
264
|
+
|
265
|
+
public_send(
|
266
|
+
metric.type,
|
267
|
+
metric.name,
|
268
|
+
value: partition_statistics.dig(*metric.key_location),
|
269
|
+
**base_tags.merge({
|
270
|
+
topic: topic_name,
|
271
|
+
partition: partition_name
|
272
|
+
})
|
273
|
+
)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
else
|
277
|
+
raise ArgumentError, metric.scope
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Builds basic per consumer tags for publication
|
282
|
+
#
|
283
|
+
# @param consumer [Karafka::BaseConsumer]
|
284
|
+
# @return [Array<String>]
|
285
|
+
def consumer_tags(consumer)
|
286
|
+
messages = consumer.messages
|
287
|
+
metadata = messages.metadata
|
288
|
+
consumer_group_id = consumer.topic.consumer_group.id
|
289
|
+
|
290
|
+
{
|
291
|
+
topic: metadata.topic,
|
292
|
+
partition: metadata.partition,
|
293
|
+
consumer_group: consumer_group_id
|
294
|
+
}
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
@@ -6,25 +6,14 @@ module Honeybadger
|
|
6
6
|
requirement { defined?(::Karafka) && ::Karafka.respond_to?(:monitor) }
|
7
7
|
|
8
8
|
execution do
|
9
|
-
|
10
|
-
Honeybadger.notify(event[:error])
|
11
|
-
Honeybadger.event("error.occurred.karafka", error: event[:error]) if config.load_plugin_insights?(:karafka)
|
12
|
-
end
|
9
|
+
require 'honeybadger/karafka'
|
13
10
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
duration: event.payload[:time],
|
18
|
-
consumer: event.payload[:caller].class.to_s,
|
19
|
-
id: event.payload[:caller].id,
|
20
|
-
topic: event.payload[:caller].messages.metadata.topic,
|
21
|
-
messages_count: event.payload[:caller].messages.metadata.size,
|
22
|
-
processing_lag: event.payload[:caller].messages.metadata.processing_lag,
|
23
|
-
partition: event.payload[:caller].messages.metadata.partition
|
24
|
-
}
|
11
|
+
errors_listener = ::Honeybadger::Karafka::ErrorsListener.new
|
12
|
+
::Karafka.monitor.subscribe(errors_listener)
|
13
|
+
::Karafka.producer.monitor.subscribe(errors_listener) if ::Karafka.respond_to?(:producer)
|
25
14
|
|
26
|
-
|
27
|
-
|
15
|
+
if config.load_plugin_insights?(:karafka)
|
16
|
+
::Karafka.monitor.subscribe(::Honeybadger::Karafka::InsightsListener.new)
|
28
17
|
end
|
29
18
|
end
|
30
19
|
end
|
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.19.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: 2024-10-
|
11
|
+
date: 2024-10-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: logger
|
@@ -81,6 +81,7 @@ files:
|
|
81
81
|
- lib/honeybadger/init/sinatra.rb
|
82
82
|
- lib/honeybadger/instrumentation.rb
|
83
83
|
- lib/honeybadger/instrumentation_helper.rb
|
84
|
+
- lib/honeybadger/karafka.rb
|
84
85
|
- lib/honeybadger/logging.rb
|
85
86
|
- lib/honeybadger/metric.rb
|
86
87
|
- lib/honeybadger/metrics_worker.rb
|