honeybadger 2.6.1 → 2.7.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 +9 -0
- data/README.md +2 -10
- data/lib/honeybadger/agent/worker.rb +3 -1
- data/lib/honeybadger/agent.rb +2 -152
- data/lib/honeybadger/backend/base.rb +1 -1
- data/lib/honeybadger/backend/server.rb +0 -2
- data/lib/honeybadger/backtrace.rb +9 -7
- data/lib/honeybadger/config/defaults.rb +1 -21
- data/lib/honeybadger/config.rb +1 -9
- data/lib/honeybadger/const.rb +0 -2
- data/lib/honeybadger/init/rails.rb +0 -60
- data/lib/honeybadger/init/sinatra.rb +2 -2
- data/lib/honeybadger/notice.rb +3 -14
- data/lib/honeybadger/plugins/delayed_job/plugin.rb +1 -3
- data/lib/honeybadger/plugins/resque.rb +1 -3
- data/lib/honeybadger/plugins/shoryuken.rb +7 -10
- data/lib/honeybadger/plugins/sidekiq.rb +1 -4
- data/lib/honeybadger/rack/metrics_reporter.rb +2 -27
- data/lib/honeybadger/rack/request_hash.rb +1 -1
- data/lib/honeybadger/util/request_payload.rb +1 -1
- data/lib/honeybadger/version.rb +1 -1
- metadata +2 -8
- data/lib/honeybadger/agent/metered_queue.rb +0 -80
- data/lib/honeybadger/agent/metrics_collection.rb +0 -59
- data/lib/honeybadger/agent/metrics_collector.rb +0 -106
- data/lib/honeybadger/agent/trace_collection.rb +0 -32
- data/lib/honeybadger/plugins/net_http.rb +0 -38
- data/lib/honeybadger/trace.rb +0 -229
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bfe22be20c43fd975d126fc91fb7cb898c670757
|
4
|
+
data.tar.gz: 23b9996827b81249114e8c87b016d00f01f08762
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d5319144574a8f37e9256aa3786162a185da7abdaf2805844e9b100a4952659a53edce559b3e7c03b51a211e16cc2817338a512f35f159c19ec0a875192a3da
|
7
|
+
data.tar.gz: b15017e7866fb9c3acc9d09543644d088b5914a1f671fa85bdbfaf7665b20a2755f1fee272614370536efe5e2bb98d6911584d934347a054a11f3120964fba27
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,15 @@ adheres to [Semantic Versioning](http://semver.org/).
|
|
5
5
|
|
6
6
|
## [Unreleased][unreleased]
|
7
7
|
|
8
|
+
## [2.7.0] - 2016-10-20
|
9
|
+
### Added
|
10
|
+
- Support Sinatra 2.0.
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
- Sunset performance metrics. See
|
14
|
+
http://blog.honeybadger.io/sunsetting-performance-metrics/
|
15
|
+
- Backtraces are now limited to a maximum of 1000 lines.
|
16
|
+
|
8
17
|
## [2.6.1] - 2016-08-24
|
9
18
|
### Added
|
10
19
|
- shoryuken plugin. -@ivanvc
|
data/README.md
CHANGED
@@ -132,7 +132,6 @@ Honeybadger.start(honeybadger_config)
|
|
132
132
|
|
133
133
|
# And use Honeybadger's rack middleware
|
134
134
|
use Honeybadger::Rack::ErrorNotifier, honeybadger_config
|
135
|
-
use Honeybadger::Rack::MetricsReporter, honeybadger_config
|
136
135
|
|
137
136
|
run app
|
138
137
|
```
|
@@ -222,8 +221,8 @@ You can use any of the options below in your config file, or in the environment.
|
|
222
221
|
|`root` | String | The project's absolute root path.<br/>_Default: `Dir.pwd`_|
|
223
222
|
|`hostname` | String | The hostname of the current box.<br/>_Default: `Socket.gethostname`_|
|
224
223
|
|`backend` | String | An alternate backend to use for reporting data.<br/>_Default: `nil`_|
|
225
|
-
|`debug` | Boolean |
|
226
|
-
|`send_data_at_exit` | Boolean | Finish sending enqueued exceptions
|
224
|
+
|`debug` | Boolean | Enables verbose debug logging.<br/>_Default: `false`_|
|
225
|
+
|`send_data_at_exit` | Boolean | Finish sending enqueued exceptions before allowing program to exit.<br/>_Default: `true`_|
|
227
226
|
|`disabled` | Boolean | Prevents Honeybadger from starting entirely.<br/>_Default: `false`_|
|
228
227
|
| `config_path` | String | The path of the honeybadger config file. Can only be set via the `$HONEYBADGER_CONFIG_PATH` environment variable |
|
229
228
|
|`development_environments` | Array | Environments which will not report data by default (use report_data to enable/disable explicitly).<br/>_Default: `["development", "test", "cucumber"]`_|
|
@@ -269,13 +268,6 @@ You can use any of the options below in your config file, or in the environment.
|
|
269
268
|
|`exceptions.local_variables` | Boolean | Enable sending local variables. Requires the [binding_of_caller gem](https://rubygems.org/gems/binding_of_caller).<br/>_Default: `false`_|
|
270
269
|
|`exceptions.unwrap` | Boolean | Reports #original_exception or #cause one level up from rescued exception when available.<br/>_Default: `false`_|
|
271
270
|
| | ||
|
272
|
-
|__METRIC REPORTING__ | ||
|
273
|
-
|`metrics.enabled` | Boolean | Enable sending metrics, such as requests per minute.<br/>_Default: `true`_|
|
274
|
-
|`metrics.gc_profiler` | Boolean | Enable sending GC metrics (GC::Profiler must be enabled)<br/>_Default: `false`_|
|
275
|
-
| | ||
|
276
|
-
|__TRACE REPORTING__ | ||
|
277
|
-
|`traces.enabled` | Boolean | Enable sending performance traces for slow actions.<br/>_Default: `true`_|
|
278
|
-
|`traces.threshold` | Integer | The threshold in seconds to send traces.<br/>_Default: `2000`_|
|
279
271
|
|__SIDEKIQ__ | ||
|
280
272
|
|`sidekiq.attempt_threshold` | Integer | The number of attempts before notifications will be sent.<br/>_Default: `0`_|
|
281
273
|
|`sidekiq.use_component` | Boolean | Automatically set the component to the class of the job. Helps with grouping.<br/>_Default: `false`_|
|
@@ -12,7 +12,7 @@ module Honeybadger
|
|
12
12
|
|
13
13
|
include Honeybadger::Logging::Helper
|
14
14
|
|
15
|
-
# Internal:
|
15
|
+
# Internal: Sub-class thread so we have a named thread (useful for debugging in Thread.list).
|
16
16
|
class Thread < ::Thread; end
|
17
17
|
|
18
18
|
# Internal: A queue which enforces a maximum size.
|
@@ -52,6 +52,8 @@ module Honeybadger
|
|
52
52
|
#
|
53
53
|
# Returns true.
|
54
54
|
def shutdown
|
55
|
+
d { sprintf('shutting down worker feature=%s', feature) }
|
56
|
+
|
55
57
|
mutex.synchronize do
|
56
58
|
@shutdown = true
|
57
59
|
@pid = nil
|
data/lib/honeybadger/agent.rb
CHANGED
@@ -13,14 +13,8 @@ module Honeybadger
|
|
13
13
|
|
14
14
|
include Logging::Helper
|
15
15
|
|
16
|
-
# Internal: Sub-class thread so we have a named thread (useful for debugging in Thread.list).
|
17
|
-
class Thread < ::Thread; end
|
18
|
-
|
19
16
|
autoload :Worker, 'honeybadger/agent/worker'
|
20
17
|
autoload :NullWorker, 'honeybadger/agent/worker'
|
21
|
-
autoload :Batch, 'honeybadger/agent/batch'
|
22
|
-
autoload :MetricsCollector, 'honeybadger/agent/metrics_collector'
|
23
|
-
autoload :TraceCollection, 'honeybadger/agent/trace_collection'
|
24
18
|
|
25
19
|
class << self
|
26
20
|
extend Forwardable
|
@@ -83,19 +77,7 @@ module Honeybadger
|
|
83
77
|
end
|
84
78
|
|
85
79
|
def self.fork(*args)
|
86
|
-
|
87
|
-
end
|
88
|
-
|
89
|
-
def self.trace(*args)
|
90
|
-
self.instance ? self.instance.trace(*args) : false
|
91
|
-
end
|
92
|
-
|
93
|
-
def self.timing(*args)
|
94
|
-
self.instance ? self.instance.timing(*args) : false
|
95
|
-
end
|
96
|
-
|
97
|
-
def self.increment(*args)
|
98
|
-
self.instance ? self.instance.increment(*args) : false
|
80
|
+
# noop
|
99
81
|
end
|
100
82
|
|
101
83
|
def self.flush(&block)
|
@@ -132,21 +114,17 @@ module Honeybadger
|
|
132
114
|
end
|
133
115
|
end
|
134
116
|
|
135
|
-
attr_reader :
|
117
|
+
attr_reader :workers
|
136
118
|
|
137
119
|
def initialize(config)
|
138
120
|
@config = config
|
139
|
-
@delay = config.debug? ? 10 : 60
|
140
121
|
@mutex = Mutex.new
|
141
|
-
@pid = Process.pid
|
142
122
|
|
143
123
|
unless config.backend.kind_of?(Backend::Server)
|
144
124
|
warn('Initializing development backend: data will not be reported.')
|
145
125
|
end
|
146
126
|
|
147
127
|
init_workers
|
148
|
-
init_traces
|
149
|
-
init_metrics
|
150
128
|
|
151
129
|
at_exit do
|
152
130
|
# Fix for https://bugs.ruby-lang.org/issues/5218
|
@@ -162,38 +140,7 @@ module Honeybadger
|
|
162
140
|
end
|
163
141
|
end
|
164
142
|
|
165
|
-
# Internal: Spawn the agent thread. This method is idempotent.
|
166
|
-
#
|
167
|
-
# Returns false if the Agent is stopped, otherwise true.
|
168
|
-
def start
|
169
|
-
mutex.synchronize do
|
170
|
-
return false unless pid
|
171
|
-
return true if thread && thread.alive?
|
172
|
-
|
173
|
-
debug { 'starting agent' }
|
174
|
-
|
175
|
-
@pid = Process.pid
|
176
|
-
@thread = Thread.new { run }
|
177
|
-
end
|
178
|
-
|
179
|
-
true
|
180
|
-
end
|
181
|
-
|
182
143
|
def stop(force = false)
|
183
|
-
debug { 'stopping agent' }
|
184
|
-
|
185
|
-
mutex.synchronize do
|
186
|
-
@pid = nil
|
187
|
-
end
|
188
|
-
|
189
|
-
# Kill the collector
|
190
|
-
Thread.kill(thread) if thread
|
191
|
-
|
192
|
-
unless force
|
193
|
-
flush_traces
|
194
|
-
flush_metrics
|
195
|
-
end
|
196
|
-
|
197
144
|
workers.each_pair do |key, worker|
|
198
145
|
worker.send(force ? :shutdown! : :shutdown)
|
199
146
|
end
|
@@ -201,10 +148,6 @@ module Honeybadger
|
|
201
148
|
true
|
202
149
|
end
|
203
150
|
|
204
|
-
def fork
|
205
|
-
# noop
|
206
|
-
end
|
207
|
-
|
208
151
|
def notice(opts)
|
209
152
|
opts.merge!(callbacks: self.class.callbacks)
|
210
153
|
notice = Notice.new(config, opts)
|
@@ -219,44 +162,6 @@ module Honeybadger
|
|
219
162
|
end
|
220
163
|
end
|
221
164
|
|
222
|
-
def trace(trace)
|
223
|
-
return false unless config.traces?
|
224
|
-
|
225
|
-
start
|
226
|
-
|
227
|
-
if trace.duration > config[:'traces.threshold']
|
228
|
-
debug { sprintf('agent adding trace duration=%s feature=traces id=%s', trace.duration.round(2), trace.id) }
|
229
|
-
mutex.synchronize { traces.push(trace) }
|
230
|
-
flush_traces if traces.flush?
|
231
|
-
true
|
232
|
-
else
|
233
|
-
debug { sprintf('agent discarding trace duration=%s feature=traces id=%s', trace.duration.round(2), trace.id) }
|
234
|
-
false
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
def timing(*args, &block)
|
239
|
-
return false unless config.metrics?
|
240
|
-
|
241
|
-
start
|
242
|
-
|
243
|
-
mutex.synchronize { metrics.timing(*args, &block) }
|
244
|
-
flush_metrics if metrics.flush?
|
245
|
-
|
246
|
-
true
|
247
|
-
end
|
248
|
-
|
249
|
-
def increment(*args, &block)
|
250
|
-
return false unless config.metrics?
|
251
|
-
|
252
|
-
start
|
253
|
-
|
254
|
-
mutex.synchronize { metrics.increment(*args, &block) }
|
255
|
-
flush_metrics if metrics.flush?
|
256
|
-
|
257
|
-
true
|
258
|
-
end
|
259
|
-
|
260
165
|
# Internal: Flush the workers. See Honeybadger#flush.
|
261
166
|
#
|
262
167
|
# block - an option block which is executed before flushing data.
|
@@ -266,8 +171,6 @@ module Honeybadger
|
|
266
171
|
return true unless block_given?
|
267
172
|
yield
|
268
173
|
ensure
|
269
|
-
flush_metrics
|
270
|
-
flush_traces
|
271
174
|
workers.values.each(&:flush)
|
272
175
|
end
|
273
176
|
|
@@ -286,62 +189,9 @@ module Honeybadger
|
|
286
189
|
true
|
287
190
|
end
|
288
191
|
|
289
|
-
def run
|
290
|
-
loop { work }
|
291
|
-
rescue Exception => e
|
292
|
-
error {
|
293
|
-
msg = "error in agent thread (shutting down) class=%s message=%s\n\t%s"
|
294
|
-
sprintf(msg, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
|
295
|
-
}
|
296
|
-
ensure
|
297
|
-
d { sprintf('stopping agent') }
|
298
|
-
end
|
299
|
-
|
300
|
-
def work
|
301
|
-
flush_metrics if metrics.flush?
|
302
|
-
flush_traces if traces.flush?
|
303
|
-
sleep(delay)
|
304
|
-
rescue StandardError => e
|
305
|
-
error {
|
306
|
-
msg = "error in agent thread class=%s message=%s\n\t%s"
|
307
|
-
sprintf(msg, e.class, e.message.dump, Array(e.backtrace).join("\n\t"))
|
308
|
-
}
|
309
|
-
sleep(delay)
|
310
|
-
end
|
311
|
-
|
312
192
|
def init_workers
|
313
193
|
@workers = Hash.new(NullWorker.new)
|
314
194
|
workers[:notices] = Worker.new(config, :notices)
|
315
|
-
workers[:traces] = Worker.new(config, :traces)
|
316
|
-
workers[:metrics] = Worker.new(config, :metrics)
|
317
|
-
end
|
318
|
-
|
319
|
-
def init_traces
|
320
|
-
@traces = Batch.new(config, :traces, max: 20, interval: config.debug? ? 10 : 60, collection: TraceCollection.new)
|
321
|
-
end
|
322
|
-
|
323
|
-
def init_metrics
|
324
|
-
@metrics = MetricsCollector.new(config, config.debug? ? 10 : 60)
|
325
|
-
end
|
326
|
-
|
327
|
-
def flush_metrics
|
328
|
-
mutex.synchronize do
|
329
|
-
if (count = metrics.size) > 0
|
330
|
-
debug { sprintf('agent flushing metrics feature=metrics count=%d', count) }
|
331
|
-
end
|
332
|
-
metrics.chunk(100, &method(:push).to_proc.curry[:metrics])
|
333
|
-
init_metrics
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
def flush_traces
|
338
|
-
mutex.synchronize do
|
339
|
-
if (count = traces.size) > 0
|
340
|
-
debug { sprintf('agent flushing traces feature=traces count=%d', count) }
|
341
|
-
end
|
342
|
-
push(:traces, traces) unless traces.empty?
|
343
|
-
init_traces
|
344
|
-
end
|
345
195
|
end
|
346
196
|
|
347
197
|
def notify_at_exit(ex)
|
@@ -60,7 +60,7 @@ module Honeybadger
|
|
60
60
|
# Internal: Process payload for feature.
|
61
61
|
#
|
62
62
|
# feature - A Symbol feature name (corresponds to HTTP endpoint). Current
|
63
|
-
# options are: :notices, :
|
63
|
+
# options are: :notices, :deploys, :ping
|
64
64
|
# payload - Any Object responding to #to_json.
|
65
65
|
#
|
66
66
|
# Examples:
|
@@ -39,20 +39,22 @@ module Honeybadger
|
|
39
39
|
if filtered_line
|
40
40
|
_, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
|
41
41
|
_, *filtered_args = filtered_line.match(INPUT_FORMAT).to_a
|
42
|
-
new(file, number, method, *filtered_args)
|
42
|
+
new(file, number, method, *filtered_args, opts.fetch(:source_radius, 2))
|
43
43
|
else
|
44
44
|
nil
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
48
|
def initialize(file, number, method, filtered_file = file,
|
49
|
-
filtered_number = number, filtered_method = method
|
49
|
+
filtered_number = number, filtered_method = method,
|
50
|
+
source_radius = 2)
|
50
51
|
self.filtered_file = filtered_file
|
51
52
|
self.filtered_number = filtered_number
|
52
53
|
self.filtered_method = filtered_method
|
53
54
|
self.file = file
|
54
55
|
self.number = number
|
55
56
|
self.method = method
|
57
|
+
self.source_radius = source_radius
|
56
58
|
end
|
57
59
|
|
58
60
|
# Reconstructs the line in a readable fashion.
|
@@ -73,16 +75,16 @@ module Honeybadger
|
|
73
75
|
(filtered_file =~ /^\[PROJECT_ROOT\]/i) && !(filtered_file =~ /^\[PROJECT_ROOT\]\/vendor/i)
|
74
76
|
end
|
75
77
|
|
76
|
-
|
77
|
-
|
78
|
-
def source(radius = 2)
|
79
|
-
@source ||= get_source(file, number, radius)
|
78
|
+
def source
|
79
|
+
@source ||= get_source(file, number, source_radius)
|
80
80
|
end
|
81
81
|
|
82
82
|
private
|
83
83
|
|
84
84
|
attr_writer :file, :number, :method, :filtered_file, :filtered_number, :filtered_method
|
85
85
|
|
86
|
+
attr_accessor :source_radius
|
87
|
+
|
86
88
|
# Open source file and read line(s).
|
87
89
|
#
|
88
90
|
# Returns an array of line(s) from source file.
|
@@ -126,7 +128,7 @@ module Honeybadger
|
|
126
128
|
#
|
127
129
|
# Returns array containing backtrace lines.
|
128
130
|
def to_ary
|
129
|
-
lines.map { |l| { :number => l.filtered_number, :file => l.filtered_file, :method => l.filtered_method } }
|
131
|
+
lines.take(1000).map { |l| { :number => l.filtered_number, :file => l.filtered_file, :method => l.filtered_method, :source => l.source } }
|
130
132
|
end
|
131
133
|
alias :to_a :to_ary
|
132
134
|
|
@@ -57,7 +57,7 @@ module Honeybadger
|
|
57
57
|
type: String
|
58
58
|
},
|
59
59
|
debug: {
|
60
|
-
description: '
|
60
|
+
description: 'Enables debug logging.',
|
61
61
|
default: false,
|
62
62
|
type: Boolean
|
63
63
|
},
|
@@ -256,26 +256,6 @@ module Honeybadger
|
|
256
256
|
default: false,
|
257
257
|
type: Boolean
|
258
258
|
},
|
259
|
-
:'metrics.enabled' => {
|
260
|
-
description: 'Enable sending metrics.',
|
261
|
-
default: true,
|
262
|
-
type: Boolean
|
263
|
-
},
|
264
|
-
:'metrics.gc_profiler' => {
|
265
|
-
description: 'Enable sending GC metrics (GC::Profiler must be enabled)',
|
266
|
-
default: false,
|
267
|
-
type: Boolean
|
268
|
-
},
|
269
|
-
:'traces.enabled' => {
|
270
|
-
description: 'Enable sending traces.',
|
271
|
-
default: true,
|
272
|
-
type: Boolean
|
273
|
-
},
|
274
|
-
:'traces.threshold' => {
|
275
|
-
description: 'The threshold in seconds to send traces.',
|
276
|
-
default: 2000,
|
277
|
-
type: Integer
|
278
|
-
},
|
279
259
|
:'delayed_job.attempt_threshold' => {
|
280
260
|
description: 'The number of attempts before notifications will be sent.',
|
281
261
|
default: 0,
|
data/lib/honeybadger/config.rb
CHANGED
@@ -30,7 +30,7 @@ module Honeybadger
|
|
30
30
|
|
31
31
|
NOT_BLANK = Regexp.new('\S').freeze
|
32
32
|
|
33
|
-
FEATURES = [:notices, :local_variables
|
33
|
+
FEATURES = [:notices, :local_variables].freeze
|
34
34
|
|
35
35
|
MERGE_DEFAULT = [:'exceptions.ignore'].freeze
|
36
36
|
|
@@ -88,14 +88,6 @@ module Honeybadger
|
|
88
88
|
!!features[feature.to_sym]
|
89
89
|
end
|
90
90
|
|
91
|
-
def traces?
|
92
|
-
feature?(:traces) && !!self[:'traces.enabled']
|
93
|
-
end
|
94
|
-
|
95
|
-
def metrics?
|
96
|
-
feature?(:metrics) && !!self[:'metrics.enabled']
|
97
|
-
end
|
98
|
-
|
99
91
|
def logger
|
100
92
|
@logger || Logging::BootLogger.instance
|
101
93
|
end
|
data/lib/honeybadger/const.rb
CHANGED
@@ -7,12 +7,10 @@ module Honeybadger
|
|
7
7
|
autoload :Config, 'honeybadger/config'
|
8
8
|
autoload :Logging, 'honeybadger/logging'
|
9
9
|
autoload :Notice, 'honeybadger/notice'
|
10
|
-
autoload :Trace, 'honeybadger/trace'
|
11
10
|
autoload :Plugin, 'honeybadger/plugin'
|
12
11
|
|
13
12
|
module Rack
|
14
13
|
autoload :ErrorNotifier, 'honeybadger/rack/error_notifier'
|
15
|
-
autoload :MetricsReporter, 'honeybadger/rack/metrics_reporter'
|
16
14
|
autoload :UserFeedback, 'honeybadger/rack/user_feedback'
|
17
15
|
autoload :UserInformer, 'honeybadger/rack/user_informer'
|
18
16
|
autoload :RequestHash, 'honeybadger/rack/request_hash'
|
@@ -25,45 +25,6 @@ module Honeybadger
|
|
25
25
|
middleware.insert_before(Honeybadger::Rack::ErrorNotifier, Honeybadger::Rack::UserFeedback, config) if config[:'feedback.enabled']
|
26
26
|
end
|
27
27
|
end
|
28
|
-
|
29
|
-
if config.traces?
|
30
|
-
ActiveSupport::Notifications.subscribe('start_processing.action_controller') do |name, started, finished, id, data|
|
31
|
-
Trace.create(id)
|
32
|
-
end
|
33
|
-
|
34
|
-
ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
|
35
|
-
event = ActiveSupport::Notifications::Event.new(*args)
|
36
|
-
Trace.current.add_query(event) if Trace.current and event.name != 'SCHEMA'
|
37
|
-
end
|
38
|
-
|
39
|
-
ActiveSupport::Notifications.subscribe(/^render_(template|partial|action|collection)\.action_view/) do |*args|
|
40
|
-
event = ActiveSupport::Notifications::Event.new(*args)
|
41
|
-
Trace.current.add(event) if Trace.current
|
42
|
-
end
|
43
|
-
|
44
|
-
ActiveSupport::Notifications.subscribe('net_http.request') do |*args|
|
45
|
-
event = ActiveSupport::Notifications::Event.new(*args)
|
46
|
-
Trace.current.add(event) if Trace.current
|
47
|
-
end
|
48
|
-
|
49
|
-
ActiveSupport::Notifications.subscribe('process_action.action_controller') do |*args|
|
50
|
-
event = ActiveSupport::Notifications::Event.new(*args)
|
51
|
-
if Trace.current && event.payload[:controller] && event.payload[:action]
|
52
|
-
payload = { source: 'web' }
|
53
|
-
payload[:path] = Util::Sanitizer.new(filters: config.params_filters).filter_url(event.payload[:path]) if event.payload[:path]
|
54
|
-
payload[:request] = request_data(event, config)
|
55
|
-
Trace.current.complete(event, payload)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
if config.metrics?
|
61
|
-
ActiveSupport::Notifications.subscribe('process_action.action_controller') do |*args|
|
62
|
-
event = ActiveSupport::Notifications::Event.new(*args)
|
63
|
-
status = event.payload[:exception] ? 500 : event.payload[:status]
|
64
|
-
Agent.timing("app.request.#{status}", event.duration)
|
65
|
-
end
|
66
|
-
end
|
67
28
|
end
|
68
29
|
end
|
69
30
|
|
@@ -78,27 +39,6 @@ module Honeybadger
|
|
78
39
|
:framework => :rails
|
79
40
|
}
|
80
41
|
end
|
81
|
-
|
82
|
-
def request_data(event, config)
|
83
|
-
h = {
|
84
|
-
url: event.payload[:path],
|
85
|
-
component: event.payload[:controller],
|
86
|
-
action: event.payload[:action],
|
87
|
-
params: event.payload[:params]
|
88
|
-
}
|
89
|
-
h.merge!(config.request_hash)
|
90
|
-
h.delete_if {|k,v| config.excluded_request_keys.include?(k) }
|
91
|
-
h[:sanitizer] = Util::Sanitizer.new(filters: config.params_filters)
|
92
|
-
Util::RequestPayload.build(h).update({
|
93
|
-
context: context_data
|
94
|
-
})
|
95
|
-
end
|
96
|
-
|
97
|
-
def context_data
|
98
|
-
if Thread.current[:__honeybadger_context]
|
99
|
-
Util::Sanitizer.new.sanitize(Thread.current[:__honeybadger_context])
|
100
|
-
end
|
101
|
-
end
|
102
42
|
end
|
103
43
|
end
|
104
44
|
end
|
@@ -12,7 +12,8 @@ module Honeybadger
|
|
12
12
|
|
13
13
|
def honeybadger_config(app)
|
14
14
|
{
|
15
|
-
api_key: defined?(honeybadger_api_key) ? honeybadger_api_key : nil
|
15
|
+
api_key: defined?(honeybadger_api_key) ? honeybadger_api_key : nil,
|
16
|
+
env: ENV['APP_ENV']
|
16
17
|
}
|
17
18
|
end
|
18
19
|
|
@@ -23,7 +24,6 @@ module Honeybadger
|
|
23
24
|
return unless Honeybadger.start(config)
|
24
25
|
|
25
26
|
install_honeybadger_middleware(Honeybadger::Rack::ErrorNotifier, config) if config.feature?(:notices) && config[:'exceptions.enabled']
|
26
|
-
install_honeybadger_middleware(Honeybadger::Rack::MetricsReporter, config) if config.feature?(:metrics) && config[:'metrics.enabled']
|
27
27
|
end
|
28
28
|
|
29
29
|
def install_honeybadger_middleware(klass, config)
|
data/lib/honeybadger/notice.rb
CHANGED
@@ -64,7 +64,7 @@ module Honeybadger
|
|
64
64
|
# Public: The message from the exception, or a general description of the error.
|
65
65
|
attr_reader :error_message
|
66
66
|
|
67
|
-
#
|
67
|
+
# Deprecated: Excerpt from source file.
|
68
68
|
attr_reader :source
|
69
69
|
|
70
70
|
# Public: CGI variables such as HTTP_METHOD.
|
@@ -139,7 +139,6 @@ module Honeybadger
|
|
139
139
|
"#{exception.class.name}: #{exception.message}"
|
140
140
|
end
|
141
141
|
@backtrace = parse_backtrace(exception_attribute(:backtrace, caller))
|
142
|
-
@source = extract_source_from_backtrace(@backtrace, config, opts)
|
143
142
|
@fingerprint = construct_fingerprint(opts)
|
144
143
|
|
145
144
|
@request = construct_request_hash(config, opts)
|
@@ -175,7 +174,6 @@ module Honeybadger
|
|
175
174
|
class: s(error_class),
|
176
175
|
message: s(error_message),
|
177
176
|
backtrace: s(backtrace.to_a),
|
178
|
-
source: s(source),
|
179
177
|
fingerprint: s(fingerprint),
|
180
178
|
tags: s(tags),
|
181
179
|
causes: s(causes)
|
@@ -316,16 +314,6 @@ module Honeybadger
|
|
316
314
|
context.empty? ? nil : context
|
317
315
|
end
|
318
316
|
|
319
|
-
def extract_source_from_backtrace(backtrace, config, opts)
|
320
|
-
return nil if backtrace.lines.empty?
|
321
|
-
|
322
|
-
if backtrace.application_lines.any?
|
323
|
-
backtrace.application_lines.first.source(config[:'exceptions.source_radius'])
|
324
|
-
else
|
325
|
-
backtrace.lines.first.source(config[:'exceptions.source_radius'])
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
317
|
def fingerprint_from_opts(opts)
|
330
318
|
callback = opts[:fingerprint]
|
331
319
|
callback ||= opts[:callbacks] && opts[:callbacks].exception_fingerprint
|
@@ -398,7 +386,8 @@ module Honeybadger
|
|
398
386
|
Backtrace.parse(
|
399
387
|
backtrace,
|
400
388
|
filters: construct_backtrace_filters(opts),
|
401
|
-
config: config
|
389
|
+
config: config,
|
390
|
+
source_radius: config[:'exceptions.source_radius']
|
402
391
|
)
|
403
392
|
end
|
404
393
|
|
@@ -34,9 +34,7 @@ module Honeybadger
|
|
34
34
|
:queue => job.queue
|
35
35
|
)
|
36
36
|
|
37
|
-
|
38
|
-
block.call(job)
|
39
|
-
end
|
37
|
+
block.call(job)
|
40
38
|
rescue Exception => error
|
41
39
|
::Honeybadger.notify_or_ignore(
|
42
40
|
:component => component,
|
@@ -8,9 +8,7 @@ module Honeybadger
|
|
8
8
|
def around_perform_with_honeybadger(*args)
|
9
9
|
Honeybadger.flush do
|
10
10
|
begin
|
11
|
-
|
12
|
-
yield
|
13
|
-
end
|
11
|
+
yield
|
14
12
|
rescue Exception => e
|
15
13
|
Honeybadger.notify(e, parameters: { job_arguments: args }) if send_exception?(e, args)
|
16
14
|
raise e
|
@@ -11,18 +11,15 @@ module Honeybadger
|
|
11
11
|
return
|
12
12
|
end
|
13
13
|
|
14
|
-
klass = worker.class.name
|
15
14
|
Honeybadger.flush do
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
Honeybadger.notify(e, parameters: body)
|
23
|
-
end
|
24
|
-
raise e
|
15
|
+
begin
|
16
|
+
yield
|
17
|
+
rescue => e
|
18
|
+
receive_count = sqs_msg.attributes['ApproximateReceiveCount'.freeze]
|
19
|
+
if receive_count && ::Honeybadger::Agent.config[:'shoryuken.attempt_threshold'].to_i <= receive_count.to_i
|
20
|
+
Honeybadger.notify(e, parameters: body)
|
25
21
|
end
|
22
|
+
raise e
|
26
23
|
end
|
27
24
|
end
|
28
25
|
ensure
|
@@ -7,10 +7,7 @@ module Honeybadger
|
|
7
7
|
class Middleware
|
8
8
|
def call(worker, msg, queue)
|
9
9
|
Honeybadger.context.clear!
|
10
|
-
|
11
|
-
Honeybadger::Trace.instrument("#{klass}#perform", { :source => 'sidekiq'.freeze, :jid => msg['jid'.freeze], :class => klass }) do
|
12
|
-
yield
|
13
|
-
end
|
10
|
+
yield
|
14
11
|
end
|
15
12
|
end
|
16
13
|
|
@@ -3,38 +3,13 @@ require 'honeybadger'
|
|
3
3
|
module Honeybadger
|
4
4
|
module Rack
|
5
5
|
class MetricsReporter
|
6
|
-
GC_TIME_METRIC = 'app.gc.time'.freeze
|
7
|
-
GC_COLLECTIONS_METRIC = 'app.gc.collections'.freeze
|
8
|
-
|
9
6
|
def initialize(app, config)
|
10
7
|
@app = app
|
11
|
-
|
8
|
+
config.logger.warn('DEPRECATION WARNING: `Honeybadger::Rack::MetricsReporter` no longer has any effect and will be removed.')
|
12
9
|
end
|
13
10
|
|
14
11
|
def call(env)
|
15
|
-
|
16
|
-
track_gc? and GC::Profiler.clear
|
17
|
-
status, headers, body = app.call(env)
|
18
|
-
duration = (Time.now - start) * 1000
|
19
|
-
report_metrics(status, duration)
|
20
|
-
[status, headers, body]
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
attr_reader :app, :config
|
26
|
-
|
27
|
-
def track_gc?
|
28
|
-
config[:'metrics.gc_profiler']
|
29
|
-
end
|
30
|
-
|
31
|
-
def report_metrics(status, duration)
|
32
|
-
Agent.timing("app.request.#{status}", duration)
|
33
|
-
|
34
|
-
if track_gc? && GC::Profiler.total_time > 0
|
35
|
-
Agent.timing(GC_TIME_METRIC, GC::Profiler.total_time * 1000)
|
36
|
-
Agent.increment(GC_COLLECTIONS_METRIC, GC::Profiler.result[/\d+/].to_i)
|
37
|
-
end
|
12
|
+
@app.call(env)
|
38
13
|
end
|
39
14
|
end
|
40
15
|
end
|
@@ -4,7 +4,7 @@ module Honeybadger
|
|
4
4
|
# /v1/notices API specification.
|
5
5
|
class RequestHash < ::Hash
|
6
6
|
# Internal
|
7
|
-
CGI_BLACKLIST = ['QUERY_STRING', 'RAW_POST_DATA'].freeze
|
7
|
+
CGI_BLACKLIST = ['QUERY_STRING', 'RAW_POST_DATA', 'ORIGINAL_FULLPATH', 'REQUEST_URI'].freeze
|
8
8
|
CGI_KEY_REGEXP = /\A[A-Z_]+\Z/
|
9
9
|
|
10
10
|
def initialize(request)
|
@@ -2,7 +2,7 @@ require 'honeybadger/util/sanitizer'
|
|
2
2
|
|
3
3
|
module Honeybadger
|
4
4
|
module Util
|
5
|
-
# Internal: Constructs/sanitizes request data for notices
|
5
|
+
# Internal: Constructs/sanitizes request data for notices
|
6
6
|
module RequestPayload
|
7
7
|
# Internal: Default values to use for request data.
|
8
8
|
DEFAULTS = {
|
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: 2.
|
4
|
+
version: 2.7.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: 2016-
|
11
|
+
date: 2016-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Make managing application errors a more pleasant experience.
|
14
14
|
email:
|
@@ -26,11 +26,7 @@ files:
|
|
26
26
|
- lib/honeybadger.rb
|
27
27
|
- lib/honeybadger/agent.rb
|
28
28
|
- lib/honeybadger/agent/batch.rb
|
29
|
-
- lib/honeybadger/agent/metered_queue.rb
|
30
|
-
- lib/honeybadger/agent/metrics_collection.rb
|
31
|
-
- lib/honeybadger/agent/metrics_collector.rb
|
32
29
|
- lib/honeybadger/agent/null_worker.rb
|
33
|
-
- lib/honeybadger/agent/trace_collection.rb
|
34
30
|
- lib/honeybadger/agent/worker.rb
|
35
31
|
- lib/honeybadger/backend.rb
|
36
32
|
- lib/honeybadger/backend/base.rb
|
@@ -58,7 +54,6 @@ files:
|
|
58
54
|
- lib/honeybadger/plugins/delayed_job.rb
|
59
55
|
- lib/honeybadger/plugins/delayed_job/plugin.rb
|
60
56
|
- lib/honeybadger/plugins/local_variables.rb
|
61
|
-
- lib/honeybadger/plugins/net_http.rb
|
62
57
|
- lib/honeybadger/plugins/passenger.rb
|
63
58
|
- lib/honeybadger/plugins/rails.rb
|
64
59
|
- lib/honeybadger/plugins/resque.rb
|
@@ -75,7 +70,6 @@ files:
|
|
75
70
|
- lib/honeybadger/rack/user_informer.rb
|
76
71
|
- lib/honeybadger/tasks.rb
|
77
72
|
- lib/honeybadger/templates/feedback_form.erb
|
78
|
-
- lib/honeybadger/trace.rb
|
79
73
|
- lib/honeybadger/util/http.rb
|
80
74
|
- lib/honeybadger/util/request_payload.rb
|
81
75
|
- lib/honeybadger/util/sanitizer.rb
|
@@ -1,80 +0,0 @@
|
|
1
|
-
module Honeybadger
|
2
|
-
class Agent
|
3
|
-
# Internal: A thread-safe first-in-first-out queue. Values are pushed onto
|
4
|
-
# the queue and released at a defined interval.
|
5
|
-
class MeteredQueue
|
6
|
-
def initialize(interval = 1, max = 1000, now = now())
|
7
|
-
@interval = interval
|
8
|
-
@max = max
|
9
|
-
@values = Array.new
|
10
|
-
@throttles = Array.new
|
11
|
-
@future = calculate_future(now, interval)
|
12
|
-
@mutex = Mutex.new
|
13
|
-
end
|
14
|
-
|
15
|
-
def size
|
16
|
-
mutex.synchronize { values.size }
|
17
|
-
end
|
18
|
-
|
19
|
-
def push(value)
|
20
|
-
unless values.size == max
|
21
|
-
mutex.synchronize { values.push(value) }
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def pop
|
26
|
-
if now.to_i >= future
|
27
|
-
mutex.synchronize do
|
28
|
-
@future = calculate_future
|
29
|
-
values.shift
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def pop!
|
35
|
-
mutex.synchronize { values.shift }
|
36
|
-
end
|
37
|
-
|
38
|
-
# Applies a new throttle to this queue and adjusts the future.
|
39
|
-
#
|
40
|
-
# Returns nothing
|
41
|
-
def throttle(throttle)
|
42
|
-
mutex.synchronize do
|
43
|
-
old_interval = throttled_interval
|
44
|
-
throttles << throttle
|
45
|
-
@future += (throttled_interval - old_interval)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# Removes the last throttle from this queue and adjusts the future.
|
50
|
-
#
|
51
|
-
# Returns Float throttle
|
52
|
-
def unthrottle
|
53
|
-
mutex.synchronize do
|
54
|
-
old_interval = throttled_interval
|
55
|
-
throttles.pop.tap do |throttle|
|
56
|
-
if throttle
|
57
|
-
@future -= (old_interval - throttled_interval)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
attr_reader :interval, :max, :values, :throttles, :future, :mutex
|
66
|
-
|
67
|
-
def now
|
68
|
-
Time.now
|
69
|
-
end
|
70
|
-
|
71
|
-
def calculate_future(now = now(), interval = interval())
|
72
|
-
now.to_i + throttled_interval(interval)
|
73
|
-
end
|
74
|
-
|
75
|
-
def throttled_interval(interval = interval())
|
76
|
-
throttles.reduce(interval) {|a,e| a * e }
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
module Honeybadger
|
2
|
-
class Agent
|
3
|
-
# This code comes from batsd
|
4
|
-
class MetricsCollection < Array
|
5
|
-
PERCENTILE_METHOD_SIGNATURE = /\Apercentile_(.+)\z/.freeze
|
6
|
-
|
7
|
-
# Calculates the sum of values in the array
|
8
|
-
def sum
|
9
|
-
inject( nil ) { |sum,x| sum ? sum+x : x };
|
10
|
-
end
|
11
|
-
|
12
|
-
# Calculates the arithmetic mean of values in the array
|
13
|
-
def mean
|
14
|
-
self.sum.to_f / self.length
|
15
|
-
end
|
16
|
-
|
17
|
-
# Calculates the median of values in the array
|
18
|
-
def median
|
19
|
-
self.sort[self.length/2]
|
20
|
-
end
|
21
|
-
|
22
|
-
# Calculates the value of the upper percentile of values
|
23
|
-
# in the array. If only a single value is provided in the array, that is
|
24
|
-
# returned
|
25
|
-
def percentile(threshold)
|
26
|
-
return self.first unless count > 1
|
27
|
-
|
28
|
-
self.sort!
|
29
|
-
# strip off the top 100-threshold
|
30
|
-
threshold_index = (((100 - threshold).to_f / 100) * count).round
|
31
|
-
self[0..-threshold_index].last
|
32
|
-
end
|
33
|
-
|
34
|
-
# Calculates the mean squared error of values in the array
|
35
|
-
def mean_squared
|
36
|
-
m = mean
|
37
|
-
self.class.new(map{|v| (v-m)**2}).sum
|
38
|
-
end
|
39
|
-
|
40
|
-
# Calculates the standard deviatiation of values in the array
|
41
|
-
def standard_dev
|
42
|
-
(mean_squared/(count-1))**0.5
|
43
|
-
end
|
44
|
-
|
45
|
-
# Allow [1,2,3].percentile_90, [1,2,3].percentile(75), etc.
|
46
|
-
def method_missing(method, *args, &block)
|
47
|
-
if method.to_s =~ PERCENTILE_METHOD_SIGNATURE
|
48
|
-
percentile($1.to_i)
|
49
|
-
else
|
50
|
-
super
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def respond_to_missing?(method, include_private = false)
|
55
|
-
method.to_s =~ PERCENTILE_METHOD_SIGNATURE or super
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
@@ -1,106 +0,0 @@
|
|
1
|
-
require 'securerandom'
|
2
|
-
require 'forwardable'
|
3
|
-
|
4
|
-
module Honeybadger
|
5
|
-
class Agent
|
6
|
-
autoload :MetricsCollection, 'honeybadger/agent/metrics_collection'
|
7
|
-
|
8
|
-
class MetricsCollector
|
9
|
-
extend Forwardable
|
10
|
-
|
11
|
-
class Chunk
|
12
|
-
extend Forwardable
|
13
|
-
|
14
|
-
attr_reader :id
|
15
|
-
|
16
|
-
def initialize(id, metrics)
|
17
|
-
@id = SecureRandom.uuid
|
18
|
-
@metrics = metrics
|
19
|
-
end
|
20
|
-
|
21
|
-
def_delegators :@metrics, :to_json, :size
|
22
|
-
end
|
23
|
-
|
24
|
-
def initialize(config, interval = 60, now = now())
|
25
|
-
@id = SecureRandom.uuid
|
26
|
-
@config = config
|
27
|
-
@interval = interval
|
28
|
-
@future = now + interval
|
29
|
-
@mutex = Mutex.new
|
30
|
-
@metrics = { :timing => {}, :counter => {} }
|
31
|
-
end
|
32
|
-
|
33
|
-
attr_reader :id
|
34
|
-
|
35
|
-
def_delegators :@metrics, :[]
|
36
|
-
|
37
|
-
def timing(name, value)
|
38
|
-
add_metric(name, value, :timing)
|
39
|
-
end
|
40
|
-
|
41
|
-
def increment(name, value)
|
42
|
-
add_metric(name, value, :counter)
|
43
|
-
end
|
44
|
-
|
45
|
-
def flush?
|
46
|
-
now >= future
|
47
|
-
end
|
48
|
-
|
49
|
-
def size
|
50
|
-
mutex.synchronize do
|
51
|
-
metrics.reduce(0) do |count, hash|
|
52
|
-
count + hash[1].size
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def to_a
|
58
|
-
mutex.synchronize do
|
59
|
-
[].tap do |m|
|
60
|
-
metrics[:counter].each do |metric, values|
|
61
|
-
m << "#{metric} #{values.sum}"
|
62
|
-
end
|
63
|
-
metrics[:timing].each do |metric, values|
|
64
|
-
m << "#{metric}:mean #{values.mean}"
|
65
|
-
m << "#{metric}:median #{values.median}"
|
66
|
-
m << "#{metric}:percentile_90 #{values.percentile(90)}"
|
67
|
-
m << "#{metric}:min #{values.min}"
|
68
|
-
m << "#{metric}:max #{values.max}"
|
69
|
-
m << "#{metric}:stddev #{values.standard_dev}" if values.count > 1
|
70
|
-
m << "#{metric} #{values.count}"
|
71
|
-
end
|
72
|
-
m.compact!
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def chunk(size)
|
78
|
-
to_a.each_slice(size) do |metrics|
|
79
|
-
yield(Chunk.new(id, as_json(metrics)))
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def as_json(metrics = to_a)
|
84
|
-
{metrics: metrics, :environment => config[:env], :hostname => config[:hostname]}
|
85
|
-
end
|
86
|
-
|
87
|
-
def to_json(*args)
|
88
|
-
as_json.to_json(*args)
|
89
|
-
end
|
90
|
-
|
91
|
-
private
|
92
|
-
|
93
|
-
attr_reader :config, :future, :metrics, :mutex
|
94
|
-
|
95
|
-
def now
|
96
|
-
Time.now.to_i
|
97
|
-
end
|
98
|
-
|
99
|
-
def add_metric(name, value, kind)
|
100
|
-
mutex.synchronize do
|
101
|
-
(metrics[kind][name] ||= MetricsCollection.new) << value
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
|
3
|
-
# Internal: A collection for de-duping traces. Not currently thread-safe (so
|
4
|
-
# make sure access is synchronized.)
|
5
|
-
module Honeybadger
|
6
|
-
class Agent
|
7
|
-
class TraceCollection
|
8
|
-
extend Forwardable
|
9
|
-
include Enumerable
|
10
|
-
|
11
|
-
def initialize
|
12
|
-
@traces = {}
|
13
|
-
end
|
14
|
-
|
15
|
-
def_delegators :to_a, :each, :empty?, :size
|
16
|
-
|
17
|
-
def push(trace)
|
18
|
-
if !traces.key?(trace.key) || traces[trace.key].duration < trace.duration
|
19
|
-
traces[trace.key] = trace
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def to_a
|
24
|
-
traces.values
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
attr_reader :traces
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
require 'honeybadger/plugin'
|
2
|
-
require 'honeybadger/trace'
|
3
|
-
|
4
|
-
module Honeybadger
|
5
|
-
module Plugins
|
6
|
-
module NetHttp
|
7
|
-
module Instrumentation
|
8
|
-
def self.included(base)
|
9
|
-
base.send(:alias_method, :request_without_honeybadger, :request)
|
10
|
-
base.send(:alias_method, :request, :request_with_honeybadger)
|
11
|
-
end
|
12
|
-
|
13
|
-
def request_with_honeybadger(*args, &block)
|
14
|
-
request = args[0]
|
15
|
-
uri = request.path.to_s.match(%r{https?://}) ? URI(request.path) : URI("http#{use_ssl? ? 's' : ''}://#{address}:#{port}#{request.path}")
|
16
|
-
|
17
|
-
if uri.host.to_s.match("honeybadger.io")
|
18
|
-
return request_without_honeybadger(*args, &block)
|
19
|
-
end
|
20
|
-
|
21
|
-
ActiveSupport::Notifications.instrument("net_http.request", { :uri => uri, :method => request.method }) do
|
22
|
-
# Disable tracing during #request so that additional calls (i.e.
|
23
|
-
# when connection wasn't started) don't result in double counting.
|
24
|
-
Trace.ignore_events { request_without_honeybadger(*args, &block) }
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
Plugin.register do
|
30
|
-
requirement { defined?(::ActiveSupport::Notifications) }
|
31
|
-
requirement { defined?(::Net::HTTP) }
|
32
|
-
requirement { config[:'traces.enabled'] }
|
33
|
-
|
34
|
-
execution { ::Net::HTTP.send(:include, Instrumentation) }
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
data/lib/honeybadger/trace.rb
DELETED
@@ -1,229 +0,0 @@
|
|
1
|
-
require 'securerandom'
|
2
|
-
|
3
|
-
require 'honeybadger/agent'
|
4
|
-
require 'honeybadger/util/sanitizer'
|
5
|
-
|
6
|
-
module Honeybadger
|
7
|
-
class Trace
|
8
|
-
attr_reader :id, :duration, :key
|
9
|
-
|
10
|
-
def self.current
|
11
|
-
Thread.current[:__hb_trace]
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.create(id)
|
15
|
-
Thread.current[:__hb_trace] = new(id)
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.instrument(key, payload = {}, &block)
|
19
|
-
current = self.current
|
20
|
-
self.create(SecureRandom.uuid).instrument(key, payload, &block)
|
21
|
-
ensure
|
22
|
-
Thread.current[:__hb_trace] = current
|
23
|
-
end
|
24
|
-
|
25
|
-
# Internal: Disables event tracing for executed code block.
|
26
|
-
#
|
27
|
-
# block - The code which should not be traced.
|
28
|
-
#
|
29
|
-
# Returns the return value from the block.
|
30
|
-
def self.ignore_events
|
31
|
-
return yield if ignoring_events?
|
32
|
-
|
33
|
-
begin
|
34
|
-
Thread.current[:__hb_ignore_trace_events] = true
|
35
|
-
yield
|
36
|
-
ensure
|
37
|
-
Thread.current[:__hb_ignore_trace_events] = false
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# Internal: Is event tracing currently disabled?
|
42
|
-
def self.ignoring_events?
|
43
|
-
!!Thread.current[:__hb_ignore_trace_events]
|
44
|
-
end
|
45
|
-
|
46
|
-
def initialize(id)
|
47
|
-
@id = id
|
48
|
-
@events = []
|
49
|
-
@meta = {}
|
50
|
-
@fast_queries = {}
|
51
|
-
@duration = 0
|
52
|
-
end
|
53
|
-
|
54
|
-
def add(event)
|
55
|
-
return if ignoring_events?
|
56
|
-
ce = clean_event(event)
|
57
|
-
@events << ce.to_a if ce.render?
|
58
|
-
end
|
59
|
-
|
60
|
-
def add_query(event)
|
61
|
-
return if ignoring_events?
|
62
|
-
return add(event) unless event.duration < 6
|
63
|
-
|
64
|
-
ce = clean_event(event)
|
65
|
-
return unless ce.render?
|
66
|
-
query = ce.to_s
|
67
|
-
if @fast_queries[query]
|
68
|
-
@fast_queries[query][:duration] += ce.event.duration
|
69
|
-
@fast_queries[query][:count] += 1
|
70
|
-
else
|
71
|
-
@fast_queries[query] = { :duration => ce.event.duration, :count => 1 }
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def complete(event, payload = {})
|
76
|
-
@meta = clean_event(event).to_h.merge(payload)
|
77
|
-
@duration = event.duration
|
78
|
-
@key = "#{event.payload[:controller]}##{event.payload[:action]}"
|
79
|
-
Thread.current[:__hb_trace] = nil
|
80
|
-
Agent.trace(self)
|
81
|
-
end
|
82
|
-
|
83
|
-
def instrument(key, payload)
|
84
|
-
@key = key
|
85
|
-
@meta = payload
|
86
|
-
started = Time.now
|
87
|
-
yield
|
88
|
-
rescue Exception => e
|
89
|
-
@meta[:exception] = [e.class.name, e.message]
|
90
|
-
raise e
|
91
|
-
ensure
|
92
|
-
@meta.merge!(:duration => @duration = 1000.0 * (Time.now - started))
|
93
|
-
Agent.trace(self)
|
94
|
-
end
|
95
|
-
|
96
|
-
def to_h
|
97
|
-
@meta.merge({ :events => @events, :key => @key, :fast_queries => @fast_queries.map {|k,v| [ k, v[:duration], v[:count] ] } })
|
98
|
-
end
|
99
|
-
|
100
|
-
# Private helpers: use at your own risk.
|
101
|
-
|
102
|
-
attr_reader :meta
|
103
|
-
|
104
|
-
protected
|
105
|
-
|
106
|
-
def ignoring_events?
|
107
|
-
self.class.ignoring_events?
|
108
|
-
end
|
109
|
-
|
110
|
-
def clean_event(event)
|
111
|
-
TraceCleaner.create(event)
|
112
|
-
end
|
113
|
-
|
114
|
-
end
|
115
|
-
|
116
|
-
module TraceCleaner
|
117
|
-
|
118
|
-
def self.create(event)
|
119
|
-
Classes[event.name].new(event)
|
120
|
-
end
|
121
|
-
|
122
|
-
class Base
|
123
|
-
attr_reader :event
|
124
|
-
|
125
|
-
def initialize(event)
|
126
|
-
@event = event
|
127
|
-
end
|
128
|
-
|
129
|
-
def render?
|
130
|
-
true
|
131
|
-
end
|
132
|
-
|
133
|
-
def payload
|
134
|
-
event.payload
|
135
|
-
end
|
136
|
-
|
137
|
-
def to_s
|
138
|
-
payload[:path] || payload[:key] || payload.inspect
|
139
|
-
end
|
140
|
-
|
141
|
-
def to_h
|
142
|
-
{ :name => event.name, :desc => to_s, :duration => event.duration }
|
143
|
-
end
|
144
|
-
|
145
|
-
def to_a
|
146
|
-
[ event.name, event.duration, to_s ]
|
147
|
-
end
|
148
|
-
|
149
|
-
end
|
150
|
-
|
151
|
-
class NetHttpRequest < Base
|
152
|
-
Replacement = "..."
|
153
|
-
def to_s
|
154
|
-
uri = payload[:uri]
|
155
|
-
uri.user = Replacement if uri.user
|
156
|
-
uri.password = Replacement if uri.password
|
157
|
-
uri.query = Replacement if uri.query
|
158
|
-
"#{payload[:method]} #{uri}"
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
class ActiveRecord < Base
|
163
|
-
Schema = "SCHEMA".freeze
|
164
|
-
SchemaMigrations = /schema_migrations/.freeze
|
165
|
-
EscapedQuotes = /(\\"|\\')/.freeze
|
166
|
-
SQuotedData = /'(?:[^']|'')*'/.freeze
|
167
|
-
DQuotedData = /"(?:[^"]|"")*"/.freeze
|
168
|
-
NumericData = /\b\d+\b/.freeze
|
169
|
-
Newline = /\n/.freeze
|
170
|
-
Replacement = "?".freeze
|
171
|
-
EmptyReplacement = "".freeze
|
172
|
-
DoubleQuoters = /(postgres|sqlite|postgis)/.freeze
|
173
|
-
|
174
|
-
def initialize(event)
|
175
|
-
super
|
176
|
-
@sql = Util::Sanitizer.sanitize_string(event.payload[:sql])
|
177
|
-
end
|
178
|
-
|
179
|
-
def render?
|
180
|
-
event.payload[:name] != Schema && !sql.match(SchemaMigrations)
|
181
|
-
end
|
182
|
-
|
183
|
-
def to_s
|
184
|
-
s = sql.dup
|
185
|
-
s.gsub!(EscapedQuotes, EmptyReplacement)
|
186
|
-
s.gsub!(SQuotedData, Replacement)
|
187
|
-
s.gsub!(DQuotedData, Replacement) unless ::ActiveRecord::Base.connection_pool.spec.config[:adapter] =~ DoubleQuoters
|
188
|
-
s.gsub!(NumericData, Replacement)
|
189
|
-
s.gsub!(Newline, EmptyReplacement)
|
190
|
-
s.squeeze!(' ')
|
191
|
-
s
|
192
|
-
end
|
193
|
-
|
194
|
-
private
|
195
|
-
attr_reader :sql
|
196
|
-
end
|
197
|
-
|
198
|
-
class ActionView < Base
|
199
|
-
EmptyReplacement = "".freeze
|
200
|
-
|
201
|
-
def to_s
|
202
|
-
event.payload[:identifier].to_s.gsub(::Rails.root.to_s + '/', EmptyReplacement)
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
class ActionController < Base
|
207
|
-
def payload
|
208
|
-
event.payload.reject {|k, v| k == :params || k == :headers }
|
209
|
-
end
|
210
|
-
|
211
|
-
def to_s
|
212
|
-
payload.inspect
|
213
|
-
end
|
214
|
-
|
215
|
-
def to_h
|
216
|
-
payload.merge({ :duration => event.duration })
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
Classes = Hash.new(Base).merge({
|
221
|
-
'sql.active_record' => ActiveRecord,
|
222
|
-
'render_template.action_view' => ActionView,
|
223
|
-
'render_partial.action_view' => ActionView,
|
224
|
-
'render_collection.action_view' => ActionView,
|
225
|
-
'process_action.action_controller' => ActionController,
|
226
|
-
'net_http.request' => NetHttpRequest
|
227
|
-
})
|
228
|
-
end
|
229
|
-
end
|