honeybadger 5.10.2 → 5.11.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 +12 -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
@@ -0,0 +1,47 @@
|
|
1
|
+
module Honeybadger
|
2
|
+
class Metric
|
3
|
+
attr_reader :name, :attributes, :samples
|
4
|
+
|
5
|
+
def self.metric_type
|
6
|
+
name.split('::').last.downcase
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.signature(metric_type, name, attributes)
|
10
|
+
Digest::SHA1.hexdigest("#{metric_type}-#{name}-#{attributes.keys.join('-')}-#{attributes.values.join('-')}").to_sym
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.register(registry, metric_name, attributes)
|
14
|
+
registry.get(metric_type, metric_name, attributes) ||
|
15
|
+
registry.register(new(metric_name, attributes))
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(name, attributes)
|
19
|
+
@name = name
|
20
|
+
@attributes = attributes || {}
|
21
|
+
@samples = 0
|
22
|
+
end
|
23
|
+
|
24
|
+
def metric_type
|
25
|
+
self.class.metric_type
|
26
|
+
end
|
27
|
+
|
28
|
+
def signature
|
29
|
+
self.class.signature(metric_type, name, attributes)
|
30
|
+
end
|
31
|
+
|
32
|
+
def base_payload
|
33
|
+
attributes.merge({
|
34
|
+
event_type: "metric.hb",
|
35
|
+
metric_name: name,
|
36
|
+
metric_type: metric_type,
|
37
|
+
samples: samples
|
38
|
+
})
|
39
|
+
end
|
40
|
+
|
41
|
+
def event_payloads
|
42
|
+
payloads.map do |payload|
|
43
|
+
base_payload.merge(payload)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'honeybadger/logging'
|
2
|
+
|
3
|
+
module Honeybadger
|
4
|
+
# A concurrent queue to execute plugin collect blocks and registry.
|
5
|
+
# @api private
|
6
|
+
class MetricsWorker
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
include Honeybadger::Logging::Helper
|
10
|
+
|
11
|
+
# Sub-class thread so we have a named thread (useful for debugging in Thread.list).
|
12
|
+
class Thread < ::Thread; end
|
13
|
+
|
14
|
+
# Used to signal the worker to shutdown.
|
15
|
+
SHUTDOWN = :__hb_worker_shutdown!
|
16
|
+
|
17
|
+
def initialize(config)
|
18
|
+
@config = config
|
19
|
+
@interval_seconds = 1
|
20
|
+
@mutex = Mutex.new
|
21
|
+
@marker = ConditionVariable.new
|
22
|
+
@queue = Queue.new
|
23
|
+
@shutdown = false
|
24
|
+
@start_at = nil
|
25
|
+
@pid = Process.pid
|
26
|
+
end
|
27
|
+
|
28
|
+
def push(msg)
|
29
|
+
return false unless config.insights_enabled?
|
30
|
+
return false unless start
|
31
|
+
|
32
|
+
queue.push(msg)
|
33
|
+
end
|
34
|
+
|
35
|
+
def send_now(msg)
|
36
|
+
return if msg.tick > 0
|
37
|
+
|
38
|
+
msg.call
|
39
|
+
msg.reset
|
40
|
+
end
|
41
|
+
|
42
|
+
def shutdown(force = false)
|
43
|
+
d { 'shutting down worker' }
|
44
|
+
|
45
|
+
mutex.synchronize do
|
46
|
+
@shutdown = true
|
47
|
+
end
|
48
|
+
|
49
|
+
return true if force
|
50
|
+
return true unless thread&.alive?
|
51
|
+
|
52
|
+
queue.push(SHUTDOWN)
|
53
|
+
!!thread.join
|
54
|
+
ensure
|
55
|
+
queue.clear
|
56
|
+
kill!
|
57
|
+
end
|
58
|
+
|
59
|
+
# Blocks until queue is processed up to this point in time.
|
60
|
+
def flush
|
61
|
+
mutex.synchronize do
|
62
|
+
if thread && thread.alive?
|
63
|
+
queue.push(marker)
|
64
|
+
marker.wait(mutex)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def start
|
70
|
+
return false unless can_start?
|
71
|
+
|
72
|
+
mutex.synchronize do
|
73
|
+
@shutdown = false
|
74
|
+
@start_at = nil
|
75
|
+
|
76
|
+
return true if thread&.alive?
|
77
|
+
|
78
|
+
@pid = Process.pid
|
79
|
+
@thread = Thread.new { run }
|
80
|
+
end
|
81
|
+
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
attr_reader :config, :queue, :pid, :mutex, :marker, :thread, :interval_seconds, :start_at
|
88
|
+
|
89
|
+
def shutdown?
|
90
|
+
mutex.synchronize { @shutdown }
|
91
|
+
end
|
92
|
+
|
93
|
+
def suspended?
|
94
|
+
mutex.synchronize { start_at && Time.now.to_i < start_at }
|
95
|
+
end
|
96
|
+
|
97
|
+
def can_start?
|
98
|
+
return false if shutdown?
|
99
|
+
return false if suspended?
|
100
|
+
true
|
101
|
+
end
|
102
|
+
|
103
|
+
def kill!
|
104
|
+
d { 'killing worker thread' }
|
105
|
+
|
106
|
+
if thread
|
107
|
+
Thread.kill(thread)
|
108
|
+
thread.join # Allow ensure blocks to execute.
|
109
|
+
end
|
110
|
+
|
111
|
+
true
|
112
|
+
end
|
113
|
+
|
114
|
+
def suspend(interval)
|
115
|
+
mutex.synchronize do
|
116
|
+
@start_at = Time.now.to_i + interval
|
117
|
+
queue.clear
|
118
|
+
end
|
119
|
+
|
120
|
+
# Must be performed last since this may kill the current thread.
|
121
|
+
kill!
|
122
|
+
end
|
123
|
+
|
124
|
+
def run
|
125
|
+
begin
|
126
|
+
d { 'worker started' }
|
127
|
+
loop do
|
128
|
+
case msg = queue.pop
|
129
|
+
when SHUTDOWN then break
|
130
|
+
when ConditionVariable then signal_marker(msg)
|
131
|
+
else work(msg)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
ensure
|
135
|
+
d { 'stopping worker' }
|
136
|
+
end
|
137
|
+
rescue Exception => e
|
138
|
+
error {
|
139
|
+
msg = "Error in worker thread (shutting down) class=%s message=%s\n\t%s"
|
140
|
+
sprintf(msg, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
|
141
|
+
}
|
142
|
+
ensure
|
143
|
+
release_marker
|
144
|
+
end
|
145
|
+
|
146
|
+
def work(msg)
|
147
|
+
send_now(msg)
|
148
|
+
|
149
|
+
if shutdown?
|
150
|
+
kill!
|
151
|
+
return
|
152
|
+
end
|
153
|
+
rescue StandardError => e
|
154
|
+
error {
|
155
|
+
err = "Error in worker thread class=%s message=%s\n\t%s"
|
156
|
+
sprintf(err, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
|
157
|
+
}
|
158
|
+
ensure
|
159
|
+
queue.push(msg) unless shutdown? || suspended?
|
160
|
+
sleep(interval_seconds)
|
161
|
+
end
|
162
|
+
|
163
|
+
# Release the marker. Important to perform during cleanup when shutting
|
164
|
+
# down, otherwise it could end up waiting indefinitely.
|
165
|
+
def release_marker
|
166
|
+
signal_marker(marker)
|
167
|
+
end
|
168
|
+
|
169
|
+
def signal_marker(marker)
|
170
|
+
mutex.synchronize do
|
171
|
+
marker.signal
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'honeybadger/instrumentation_helper'
|
2
|
+
|
3
|
+
module Honeybadger
|
4
|
+
class NotificationSubscriber
|
5
|
+
def start(name, id, payload)
|
6
|
+
@start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
7
|
+
end
|
8
|
+
|
9
|
+
def finish(name, id, payload)
|
10
|
+
@finish_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
11
|
+
|
12
|
+
return unless process?(name)
|
13
|
+
|
14
|
+
payload = {
|
15
|
+
instrumenter_id: id,
|
16
|
+
duration: ((@finish_time - @start_time) * 1000).round(2)
|
17
|
+
}.merge(format_payload(payload).compact)
|
18
|
+
|
19
|
+
record(name, payload)
|
20
|
+
end
|
21
|
+
|
22
|
+
def record(name, payload)
|
23
|
+
Honeybadger.event(name, payload)
|
24
|
+
end
|
25
|
+
|
26
|
+
def process?(event)
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
def format_payload(payload)
|
31
|
+
payload
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class ActionControllerSubscriber < NotificationSubscriber
|
36
|
+
def format_payload(payload)
|
37
|
+
payload.except(:headers, :request, :response)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class ActionControllerCacheSubscriber < NotificationSubscriber
|
42
|
+
end
|
43
|
+
|
44
|
+
class ActiveSupportCacheSubscriber < NotificationSubscriber
|
45
|
+
end
|
46
|
+
|
47
|
+
class ActionViewSubscriber < NotificationSubscriber
|
48
|
+
PROJECT_ROOT = defined?(::Rails) ? ::Rails.root.to_s : ''
|
49
|
+
|
50
|
+
def format_payload(payload)
|
51
|
+
{
|
52
|
+
view: payload[:identifier].to_s.gsub(PROJECT_ROOT, '[PROJECT_ROOT]'),
|
53
|
+
layout: payload[:layout]
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class ActiveRecordSubscriber < NotificationSubscriber
|
59
|
+
def format_payload(payload)
|
60
|
+
{
|
61
|
+
query: payload[:sql].to_s.gsub(/\s+/, ' ').strip,
|
62
|
+
async: payload[:async]
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class ActiveJobSubscriber < NotificationSubscriber
|
68
|
+
def format_payload(payload)
|
69
|
+
job = payload[:job]
|
70
|
+
payload.except(:job).merge({
|
71
|
+
job_class: job.class,
|
72
|
+
job_id: job.job_id,
|
73
|
+
queue_name: job.queue_name
|
74
|
+
})
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class ActiveJobMetricsSubscriber < NotificationSubscriber
|
79
|
+
include Honeybadger::InstrumentationHelper
|
80
|
+
|
81
|
+
def format_payload(payload)
|
82
|
+
{
|
83
|
+
job_class: payload[:job].class,
|
84
|
+
queue_name: payload[:job].queue_name
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def record(name, payload)
|
89
|
+
metric_source 'active_job'
|
90
|
+
histogram name, { bins: [30, 60, 120, 300, 1800, 3600, 21_600] }.merge(payload)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class ActionMailerSubscriber < NotificationSubscriber
|
95
|
+
end
|
96
|
+
|
97
|
+
class ActiveStorageSubscriber < NotificationSubscriber
|
98
|
+
end
|
99
|
+
end
|
data/lib/honeybadger/plugin.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'forwardable'
|
2
|
+
require 'honeybadger/instrumentation_helper'
|
2
3
|
|
3
4
|
module Honeybadger
|
4
5
|
# +Honeybadger::Plugin+ defines the API for registering plugins with
|
@@ -7,6 +8,11 @@ module Honeybadger
|
|
7
8
|
# optional dependencies and load the plugin for each dependency only if it's
|
8
9
|
# present in the application.
|
9
10
|
#
|
11
|
+
# Plugins may also define a collect block that is repeatedly called from
|
12
|
+
# within a thread. The MetricsWorker contains a loop that will call all
|
13
|
+
# enabled plugins' collect method, and then sleep for 1 second. This block
|
14
|
+
# is useful for collecting and/or sending metrics at regular intervals.
|
15
|
+
#
|
10
16
|
# See the plugins/ directory for examples of official plugins. If you're
|
11
17
|
# interested in developing a plugin for Honeybadger, see the Integration
|
12
18
|
# Guide: https://docs.honeybadger.io/ruby/gem-reference/integration.html
|
@@ -37,6 +43,14 @@ module Honeybadger
|
|
37
43
|
# Honeybadger.notify(exception)
|
38
44
|
# end
|
39
45
|
# end
|
46
|
+
#
|
47
|
+
# collect do
|
48
|
+
# # This block will be periodically called at regular intervals. Here you can
|
49
|
+
# # gather metrics or inspect services. See the Honeybadger::InstrumentationHelper
|
50
|
+
# # module to see availble methods for metric collection.
|
51
|
+
# gauge 'scheduled_jobs', -> { MyFramework.stats.scheduled_jobs.count }
|
52
|
+
# gauge 'latency', -> { MyFramework.stats.latency }
|
53
|
+
# end
|
40
54
|
# end
|
41
55
|
# end
|
42
56
|
# end
|
@@ -53,13 +67,15 @@ module Honeybadger
|
|
53
67
|
@@instances
|
54
68
|
end
|
55
69
|
|
56
|
-
# Register a new plugin with Honeybadger. See {#requirement}
|
70
|
+
# Register a new plugin with Honeybadger. See {#requirement}, {#execution}, and
|
71
|
+
# {#collect}..
|
57
72
|
#
|
58
73
|
# @example
|
59
74
|
#
|
60
75
|
# Honeybadger::Plugin.register 'my_framework' do
|
61
76
|
# requirement { }
|
62
77
|
# execution { }
|
78
|
+
# collect { }
|
63
79
|
# end
|
64
80
|
#
|
65
81
|
# @param [String, Symbol] name The optional name of the plugin. Should use
|
@@ -111,12 +127,41 @@ module Honeybadger
|
|
111
127
|
def_delegator :@config, :logger
|
112
128
|
end
|
113
129
|
|
130
|
+
# @api private
|
131
|
+
class CollectorExecution < Execution
|
132
|
+
include Honeybadger::InstrumentationHelper
|
133
|
+
|
134
|
+
DEFAULT_COLLECTION_INTERVAL = 60
|
135
|
+
|
136
|
+
def initialize(name, config, options, &block)
|
137
|
+
@name = name
|
138
|
+
@config = config
|
139
|
+
@options = options
|
140
|
+
@block = block
|
141
|
+
@interval = config.collection_interval(name) || options.fetch(:interval, DEFAULT_COLLECTION_INTERVAL)
|
142
|
+
@end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + @interval
|
143
|
+
end
|
144
|
+
|
145
|
+
def tick
|
146
|
+
@end_time - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
147
|
+
end
|
148
|
+
|
149
|
+
def reset
|
150
|
+
@end_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + @interval
|
151
|
+
end
|
152
|
+
|
153
|
+
def register!
|
154
|
+
Honeybadger.collect(self)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
114
158
|
# @api private
|
115
159
|
def initialize(name)
|
116
160
|
@name = name
|
117
161
|
@loaded = false
|
118
162
|
@requirements = []
|
119
163
|
@executions = []
|
164
|
+
@collectors = []
|
120
165
|
end
|
121
166
|
|
122
167
|
# Define a requirement. All requirement blocks must return +true+ for the
|
@@ -165,6 +210,31 @@ module Honeybadger
|
|
165
210
|
@executions << block
|
166
211
|
end
|
167
212
|
|
213
|
+
# Define an collect block. Collect blocks will be added to an execution
|
214
|
+
# queue if requirement blocks return +true+. The block will be called as frequently
|
215
|
+
# as once per second, but can be configured to increase it's interval.
|
216
|
+
#
|
217
|
+
# @example
|
218
|
+
#
|
219
|
+
# Honeybadger::Plugin.register 'my_framework' do
|
220
|
+
# requirement { defined?(MyFramework) }
|
221
|
+
#
|
222
|
+
# collect do
|
223
|
+
# stats = MyFramework.stats
|
224
|
+
# gauge 'capacity', -> { stats.capcity }
|
225
|
+
# end
|
226
|
+
#
|
227
|
+
# collect(interval: 10) do
|
228
|
+
# stats = MyFramework.more_expensive_stats
|
229
|
+
# gauge 'other_stat', -> { stats.expensive_metric }
|
230
|
+
# end
|
231
|
+
# end
|
232
|
+
#
|
233
|
+
# @return nil
|
234
|
+
def collect(options={}, &block)
|
235
|
+
@collectors << [options, block]
|
236
|
+
end
|
237
|
+
|
168
238
|
# @api private
|
169
239
|
def ok?(config)
|
170
240
|
@requirements.all? {|r| Execution.new(config, &r).call }
|
@@ -181,6 +251,7 @@ module Honeybadger
|
|
181
251
|
elsif ok?(config)
|
182
252
|
config.logger.debug(sprintf('load plugin name=%s', name))
|
183
253
|
@executions.each {|e| Execution.new(config, &e).call }
|
254
|
+
@collectors.each {|o,b| CollectorExecution.new(name, config, o, &b).register! }
|
184
255
|
@loaded = true
|
185
256
|
else
|
186
257
|
config.logger.debug(sprintf('skip plugin name=%s reason=requirement', name))
|
@@ -193,6 +264,11 @@ module Honeybadger
|
|
193
264
|
false
|
194
265
|
end
|
195
266
|
|
267
|
+
# @api private
|
268
|
+
def collectors
|
269
|
+
@collectors
|
270
|
+
end
|
271
|
+
|
196
272
|
# @api private
|
197
273
|
def loaded?
|
198
274
|
@loaded
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'honeybadger/notification_subscriber'
|
2
|
+
|
1
3
|
module Honeybadger
|
2
4
|
module Plugins
|
3
5
|
module ActiveJob
|
@@ -33,17 +35,27 @@ module Honeybadger
|
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
36
|
-
Plugin.register do
|
38
|
+
Plugin.register :active_job do
|
37
39
|
requirement do
|
38
40
|
defined?(::Rails.application) &&
|
39
41
|
::Rails.application.config.respond_to?(:active_job) &&
|
40
|
-
(
|
41
|
-
|
42
|
-
|
42
|
+
!EXCLUDED_ADAPTERS.include?(::Rails.application.config.active_job[:queue_adapter].to_sym)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Don't report errors if GoodJob is reporting them
|
46
|
+
requirement do
|
47
|
+
::Rails.application.config.active_job[:queue_adapter].to_sym != :good_job ||
|
48
|
+
!::Rails.application.config.respond_to?(:good_job) ||
|
49
|
+
::Rails.application.config.good_job.on_thread_error.nil?
|
43
50
|
end
|
44
51
|
|
45
52
|
execution do
|
46
53
|
::ActiveJob::Base.set_callback(:perform, :around, &ActiveJob.method(:perform_around))
|
54
|
+
|
55
|
+
if config.load_plugin_insights?(:active_job)
|
56
|
+
::ActiveSupport::Notifications.subscribe(/(enqueue_at|enqueue|enqueue_retry|enqueue_all|perform|retry_stopped|discard)\.active_job/, Honeybadger::ActiveJobSubscriber.new)
|
57
|
+
::ActiveSupport::Notifications.subscribe('perform.active_job', Honeybadger::ActiveJobMetricsSubscriber.new)
|
58
|
+
end
|
47
59
|
end
|
48
60
|
end
|
49
61
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'honeybadger/instrumentation_helper'
|
2
|
+
require 'honeybadger/plugin'
|
3
|
+
|
4
|
+
module Honeybadger
|
5
|
+
module Plugins
|
6
|
+
module Autotuner
|
7
|
+
Plugin.register :autotuner do
|
8
|
+
requirement { config.load_plugin_insights?(:autotuner) && defined?(::Autotuner) }
|
9
|
+
|
10
|
+
execution do
|
11
|
+
singleton_class.include(Honeybadger::InstrumentationHelper)
|
12
|
+
|
13
|
+
::Autotuner.enabled = true
|
14
|
+
|
15
|
+
::Autotuner.reporter = proc do |report|
|
16
|
+
Honeybadger.event("report.autotuner", report: report.to_s)
|
17
|
+
end
|
18
|
+
|
19
|
+
metric_source 'autotuner'
|
20
|
+
|
21
|
+
::Autotuner.metrics_reporter = proc do |metrics|
|
22
|
+
metrics.each do |key, val|
|
23
|
+
gauge key, ->{ val }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,14 +1,29 @@
|
|
1
1
|
require 'honeybadger/plugin'
|
2
|
-
require 'honeybadger/ruby'
|
3
2
|
|
4
3
|
module Honeybadger
|
5
4
|
module Plugins
|
6
|
-
Plugin.register do
|
5
|
+
Plugin.register :karafka do
|
7
6
|
requirement { defined?(::Karafka) }
|
8
7
|
|
9
8
|
execution do
|
10
9
|
::Karafka.monitor.subscribe('error.occurred') do |event|
|
11
10
|
Honeybadger.notify(event[:error])
|
11
|
+
Honeybadger.event('error.occurred', error: event[:error]) if config.load_plugin_insights?(:karafka)
|
12
|
+
end
|
13
|
+
|
14
|
+
if config.load_plugin_insights?(:karafka)
|
15
|
+
::Karafka.monitor.subscribe("consumer.consumed") do |event|
|
16
|
+
context = {
|
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
|
+
partition: event.payload[:caller].messages.metadata.partition
|
23
|
+
}
|
24
|
+
|
25
|
+
Honeybadger.event('consumer.consumed.karafka', context)
|
26
|
+
end
|
12
27
|
end
|
13
28
|
end
|
14
29
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'honeybadger/plugin'
|
3
|
+
require 'honeybadger/instrumentation'
|
4
|
+
require 'resolv'
|
5
|
+
|
6
|
+
module Honeybadger
|
7
|
+
module Plugins
|
8
|
+
module Net
|
9
|
+
module HTTP
|
10
|
+
def request(request_data, body = nil, &block)
|
11
|
+
return super unless started?
|
12
|
+
return super if hb?
|
13
|
+
|
14
|
+
Honeybadger.instrumentation.monotonic_timer { super }.tap do |duration, response_data|
|
15
|
+
context = {
|
16
|
+
duration: duration,
|
17
|
+
method: request_data.method,
|
18
|
+
status: response_data.code.to_i
|
19
|
+
}.merge(parsed_uri_data(request_data))
|
20
|
+
|
21
|
+
Honeybadger.event('request.net_http', context)
|
22
|
+
end[1] # return the response data only
|
23
|
+
end
|
24
|
+
|
25
|
+
def hb?
|
26
|
+
address.to_s[/#{Honeybadger.config[:'connection.host'].to_s}/]
|
27
|
+
end
|
28
|
+
|
29
|
+
def parsed_uri_data(request_data)
|
30
|
+
uri = request_data.uri || build_uri(request_data)
|
31
|
+
{}.tap do |uri_data|
|
32
|
+
uri_data[:host] = uri.host
|
33
|
+
uri_data[:url] = uri.to_s if Honeybadger.config[:'net_http.insights.full_url']
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_uri(request_data)
|
38
|
+
hostname = (address[/#{Resolv::IPv6::Regex}/]) ? "[#{address}]" : address
|
39
|
+
URI.parse("#{use_ssl? ? 'https' : 'http'}://#{hostname}#{request_data.path}")
|
40
|
+
end
|
41
|
+
|
42
|
+
Plugin.register :net_http do
|
43
|
+
requirement { config.load_plugin_insights?(:net_http) }
|
44
|
+
|
45
|
+
execution do
|
46
|
+
::Net::HTTP.send(:prepend, Honeybadger::Plugins::Net::HTTP)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'honeybadger/plugin'
|
2
|
+
require 'honeybadger/notification_subscriber'
|
2
3
|
|
3
4
|
module Honeybadger
|
4
5
|
module Plugins
|
@@ -68,6 +69,20 @@ module Honeybadger
|
|
68
69
|
end
|
69
70
|
end
|
70
71
|
end
|
72
|
+
|
73
|
+
Plugin.register :rails do
|
74
|
+
requirement { config.load_plugin_insights?(:rails_metrics) && defined?(::Rails.application) && ::Rails.application }
|
75
|
+
|
76
|
+
execution do
|
77
|
+
::ActiveSupport::Notifications.subscribe(/(process_action|send_file|redirect_to|halted_callback|unpermitted_parameters)\.action_controller/, Honeybadger::ActionControllerSubscriber.new)
|
78
|
+
::ActiveSupport::Notifications.subscribe(/(write_fragment|read_fragment|expire_fragment|exist_fragment\?)\.action_controller/, Honeybadger::ActionControllerCacheSubscriber.new)
|
79
|
+
::ActiveSupport::Notifications.subscribe(/cache_(read|read_multi|generate|fetch_hit|write|write_multi|increment|decrement|delete|delete_multi|cleanup|prune|exist\?)\.active_support/, Honeybadger::ActiveSupportCacheSubscriber.new)
|
80
|
+
::ActiveSupport::Notifications.subscribe(/^render_(template|partial|collection)\.action_view/, Honeybadger::ActionViewSubscriber.new)
|
81
|
+
::ActiveSupport::Notifications.subscribe("sql.active_record", Honeybadger::ActiveRecordSubscriber.new)
|
82
|
+
::ActiveSupport::Notifications.subscribe("process.action_mailer", Honeybadger::ActionMailerSubscriber.new)
|
83
|
+
::ActiveSupport::Notifications.subscribe(/(service_upload|service_download)\.active_storage/, Honeybadger::ActiveStorageSubscriber.new)
|
84
|
+
end
|
85
|
+
end
|
71
86
|
end
|
72
87
|
end
|
73
88
|
end
|