honeybadger 5.18.0 → 5.19.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/lib/honeybadger/config/defaults.rb +15 -0
- data/lib/honeybadger/config.rb +14 -0
- data/lib/honeybadger/histogram.rb +13 -0
- data/lib/honeybadger/instrumentation.rb +57 -21
- 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/registry_execution.rb +1 -0
- 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: 845eb3bb6f21901ea870b5da7124decc51c01a1f9902330608eb7c9d7d0f0e89
|
4
|
+
data.tar.gz: '0388e922e33e0b116d23df7c53ef3b4d9bf047f319ce60f2548881a8328df758'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17282da0e828893e7b3bee7b34d9da1566eb1eba7843491f8b036db5e846a8641adcef3d923a78d817e9ab5f465b495b5e37aaba87b2d9c78eb9b6c5ce2b8dec
|
7
|
+
data.tar.gz: 6bf054b890c44226db7d4b9d3eed2b2ed5f848a59b882fa0cd1ce0b788d488c492f05b937f673007d41ef2f05298470664efe94531e5a1447ba21a127b7cb929
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,20 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
3
|
|
4
|
+
## [5.19.1](https://github.com/honeybadger-io/honeybadger-ruby/compare/v5.19.0...v5.19.1) (2024-11-02)
|
5
|
+
|
6
|
+
|
7
|
+
### Bug Fixes
|
8
|
+
|
9
|
+
* don't raise errors in instrumentation metrics ([#627](https://github.com/honeybadger-io/honeybadger-ruby/issues/627)) ([1231dae](https://github.com/honeybadger-io/honeybadger-ruby/commit/1231dae0c48a2fab4cdaa14f7308ab3e0b44c287))
|
10
|
+
|
11
|
+
## [5.19.0](https://github.com/honeybadger-io/honeybadger-ruby/compare/v5.18.0...v5.19.0) (2024-10-29)
|
12
|
+
|
13
|
+
|
14
|
+
### Features
|
15
|
+
|
16
|
+
* 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))
|
17
|
+
|
4
18
|
## [5.18.0](https://github.com/honeybadger-io/honeybadger-ruby/compare/v5.17.0...v5.18.0) (2024-10-25)
|
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,
|
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
|
@@ -47,66 +47,102 @@ 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 duration found' if duration.nil?
|
59
|
-
|
60
60
|
Honeybadger::Timer.register(registry, name, attributes).tap do |timer|
|
61
|
-
|
61
|
+
if value.nil?
|
62
|
+
agent.config.logger.warn("No value found for timer #{name}. Must specify either duration or value. Skipping.")
|
63
|
+
else
|
64
|
+
timer.record(value)
|
65
|
+
end
|
62
66
|
end
|
63
67
|
end
|
64
68
|
|
65
69
|
def histogram(name, *args)
|
66
70
|
attributes = extract_attributes(args)
|
67
71
|
callable = extract_callable(args)
|
68
|
-
|
72
|
+
value = nil
|
69
73
|
|
70
74
|
if callable
|
71
|
-
|
75
|
+
value = monotonic_timer{ callable.call }[0]
|
72
76
|
elsif block_given?
|
73
|
-
|
77
|
+
value = monotonic_timer{ yield }[0]
|
78
|
+
else
|
79
|
+
value = attributes.delete(:duration) || attributes.delete(:value)
|
74
80
|
end
|
75
81
|
|
76
|
-
raise 'No duration found' if duration.nil?
|
77
|
-
|
78
82
|
Honeybadger::Histogram.register(registry, name, attributes).tap do |histogram|
|
79
|
-
|
83
|
+
if value.nil?
|
84
|
+
agent.config.logger.warn("No value found for histogram #{name}. Must specify either duration or value. Skipping.")
|
85
|
+
else
|
86
|
+
histogram.record(value)
|
87
|
+
end
|
80
88
|
end
|
81
89
|
end
|
82
90
|
|
83
91
|
def increment_counter(name, *args)
|
84
92
|
attributes = extract_attributes(args)
|
85
|
-
|
86
|
-
|
93
|
+
callable = extract_callable(args)
|
94
|
+
value = nil
|
95
|
+
|
96
|
+
if callable
|
97
|
+
value = callable.call
|
98
|
+
elsif block_given?
|
99
|
+
value = yield
|
100
|
+
else
|
101
|
+
value = attributes.delete(:by) || attributes.delete(:value) || 1
|
102
|
+
end
|
87
103
|
|
88
104
|
Honeybadger::Counter.register(registry, name, attributes).tap do |counter|
|
89
|
-
counter.count(
|
105
|
+
counter.count(value)
|
90
106
|
end
|
91
107
|
end
|
92
108
|
|
93
109
|
def decrement_counter(name, *args)
|
94
110
|
attributes = extract_attributes(args)
|
95
|
-
|
96
|
-
|
111
|
+
callable = extract_callable(args)
|
112
|
+
value = nil
|
113
|
+
|
114
|
+
if callable
|
115
|
+
value = callable.call
|
116
|
+
elsif block_given?
|
117
|
+
value = yield
|
118
|
+
else
|
119
|
+
value = attributes.delete(:by) || attributes.delete(:value) || 1
|
120
|
+
end
|
97
121
|
|
98
122
|
Honeybadger::Counter.register(registry, name, attributes).tap do |counter|
|
99
|
-
counter.count(
|
123
|
+
counter.count(value * -1)
|
100
124
|
end
|
101
125
|
end
|
102
126
|
|
103
127
|
def gauge(name, *args)
|
104
128
|
attributes = extract_attributes(args)
|
105
|
-
|
106
|
-
value =
|
129
|
+
callable = extract_callable(args)
|
130
|
+
value = nil
|
131
|
+
|
132
|
+
if callable
|
133
|
+
value = callable.call
|
134
|
+
elsif block_given?
|
135
|
+
value = yield
|
136
|
+
else
|
137
|
+
value = attributes.delete(:value)
|
138
|
+
end
|
107
139
|
|
108
140
|
Honeybadger::Gauge.register(registry, name, attributes).tap do |gauge|
|
109
|
-
|
141
|
+
if value.nil?
|
142
|
+
agent.config.logger.warn("No value found for gauge #{name}. Must specify value. Skipping.")
|
143
|
+
else
|
144
|
+
gauge.record(value)
|
145
|
+
end
|
110
146
|
end
|
111
147
|
end
|
112
148
|
|
@@ -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.1
|
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-
|
11
|
+
date: 2024-11-02 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
|