honeybadger 5.10.2 → 5.11.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 +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
@@ -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
|