honeybadger 2.6.1 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|