honeybadger 2.0.12 → 2.1.0.beta.1
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 +73 -0
- data/lib/honeybadger/agent/metrics_collection.rb +6 -8
- data/lib/honeybadger/agent/worker.rb +28 -8
- data/lib/honeybadger/backend/debug.rb +1 -0
- data/lib/honeybadger/cli/main.rb +2 -0
- data/lib/honeybadger/config.rb +12 -6
- data/lib/honeybadger/config/defaults.rb +124 -51
- data/lib/honeybadger/config/env.rb +13 -18
- data/lib/honeybadger/config/yaml.rb +2 -0
- data/lib/honeybadger/init/rails.rb +28 -10
- data/lib/honeybadger/init/sinatra.rb +16 -6
- data/lib/honeybadger/notice.rb +52 -97
- data/lib/honeybadger/plugins/delayed_job/plugin.rb +21 -8
- data/lib/honeybadger/plugins/local_variables.rb +9 -0
- data/lib/honeybadger/plugins/net_http.rb +4 -1
- data/lib/honeybadger/plugins/resque.rb +63 -0
- data/lib/honeybadger/plugins/sidekiq.rb +7 -4
- data/lib/honeybadger/trace.rb +46 -14
- data/lib/honeybadger/util/request_payload.rb +36 -0
- data/lib/honeybadger/util/sanitizer.rb +11 -1
- data/lib/honeybadger/util/stats.rb +22 -3
- data/lib/honeybadger/version.rb +1 -1
- data/vendor/capistrano-honeybadger/lib/capistrano/tasks/deploy.cap +1 -1
- data/vendor/capistrano-honeybadger/lib/honeybadger/capistrano/legacy.rb +1 -1
- metadata +6 -4
@@ -8,31 +8,26 @@ module Honeybadger
|
|
8
8
|
def initialize(env = ENV)
|
9
9
|
env.each_pair do |k,v|
|
10
10
|
next unless k.match(CONFIG_KEY)
|
11
|
-
next
|
12
|
-
self[
|
11
|
+
next unless config_key = CONFIG_MAPPING[$1]
|
12
|
+
self[config_key] = cast_value(v, OPTIONS[config_key][:type])
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
private
|
17
17
|
|
18
|
-
def cast_value(value)
|
19
|
-
|
20
|
-
return value.split(ARRAY_VALUES).map(&method(:cast_value))
|
21
|
-
end
|
18
|
+
def cast_value(value, type = String)
|
19
|
+
v = value.to_s
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
value.to_i
|
32
|
-
when /\A\d+\.\d+\z/
|
33
|
-
value.to_f
|
21
|
+
if type == Boolean
|
22
|
+
!!(v =~ /\A(true|t|1)\z/i)
|
23
|
+
elsif type == Array
|
24
|
+
v.split(ARRAY_VALUES).map(&method(:cast_value))
|
25
|
+
elsif type == Integer
|
26
|
+
v.to_i
|
27
|
+
elsif type == Float
|
28
|
+
v.to_f
|
34
29
|
else
|
35
|
-
|
30
|
+
v
|
36
31
|
end
|
37
32
|
end
|
38
33
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'rails'
|
2
2
|
require 'yaml'
|
3
|
+
require 'honeybadger/util/sanitizer'
|
4
|
+
require 'honeybadger/util/request_payload'
|
3
5
|
|
4
6
|
module Honeybadger
|
5
7
|
module Init
|
@@ -30,7 +32,7 @@ module Honeybadger
|
|
30
32
|
Trace.current.add_query(event) if Trace.current and event.name != 'SCHEMA'
|
31
33
|
end
|
32
34
|
|
33
|
-
ActiveSupport::Notifications.subscribe(/^render_(template|action|collection)\.action_view/) do |*args|
|
35
|
+
ActiveSupport::Notifications.subscribe(/^render_(template|partial|action|collection)\.action_view/) do |*args|
|
34
36
|
event = ActiveSupport::Notifications::Event.new(*args)
|
35
37
|
Trace.current.add(event) if Trace.current
|
36
38
|
end
|
@@ -43,7 +45,10 @@ module Honeybadger
|
|
43
45
|
ActiveSupport::Notifications.subscribe('process_action.action_controller') do |*args|
|
44
46
|
event = ActiveSupport::Notifications::Event.new(*args)
|
45
47
|
if Trace.current && event.payload[:controller] && event.payload[:action]
|
46
|
-
|
48
|
+
payload = { source: 'web' }
|
49
|
+
payload[:path] = Util::Sanitizer.new(filters: config.params_filters).filter_url(event.payload[:path]) if event.payload[:path]
|
50
|
+
payload[:request] = request_data(event, config)
|
51
|
+
Trace.current.complete(event, payload)
|
47
52
|
end
|
48
53
|
end
|
49
54
|
end
|
@@ -53,14 +58,6 @@ module Honeybadger
|
|
53
58
|
event = ActiveSupport::Notifications::Event.new(*args)
|
54
59
|
status = event.payload[:exception] ? 500 : event.payload[:status]
|
55
60
|
Agent.timing("app.request.#{status}", event.duration)
|
56
|
-
|
57
|
-
controller = event.payload[:controller]
|
58
|
-
action = event.payload[:action]
|
59
|
-
if controller && action
|
60
|
-
Agent.timing("app.controller.#{controller}.#{action}.total", event.duration)
|
61
|
-
Agent.timing("app.controller.#{controller}.#{action}.view", event.payload[:view_runtime]) if event.payload[:view_runtime]
|
62
|
-
Agent.timing("app.controller.#{controller}.#{action}.db", event.payload[:db_runtime]) if event.payload[:db_runtime]
|
63
|
-
end
|
64
61
|
end
|
65
62
|
end
|
66
63
|
end
|
@@ -77,6 +74,27 @@ module Honeybadger
|
|
77
74
|
:framework => :rails
|
78
75
|
}
|
79
76
|
end
|
77
|
+
|
78
|
+
def request_data(event, config)
|
79
|
+
h = {
|
80
|
+
url: event.payload[:path],
|
81
|
+
component: event.payload[:controller],
|
82
|
+
action: event.payload[:action],
|
83
|
+
params: event.payload[:params]
|
84
|
+
}
|
85
|
+
h.merge!(config.request_hash)
|
86
|
+
h.delete_if {|k,v| config.excluded_request_keys.include?(k) }
|
87
|
+
h[:sanitizer] = Util::Sanitizer.new(filters: config.params_filters)
|
88
|
+
Util::RequestPayload.build(h).update({
|
89
|
+
context: context_data
|
90
|
+
})
|
91
|
+
end
|
92
|
+
|
93
|
+
def context_data
|
94
|
+
if Thread.current[:__honeybadger_context]
|
95
|
+
Util::Sanitizer.new.sanitize(Thread.current[:__honeybadger_context])
|
96
|
+
end
|
97
|
+
end
|
80
98
|
end
|
81
99
|
end
|
82
100
|
end
|
@@ -4,12 +4,7 @@ module Honeybadger
|
|
4
4
|
::Sinatra::Base.class_eval do
|
5
5
|
class << self
|
6
6
|
def build_with_honeybadger(*args, &block)
|
7
|
-
|
8
|
-
if Honeybadger.start(config)
|
9
|
-
use(Honeybadger::Rack::ErrorNotifier, config) if config.feature?(:notices) && config[:'exceptions.enabled']
|
10
|
-
use(Honeybadger::Rack::MetricsReporter, config) if config.feature?(:metrics) && config[:'metrics.enabled']
|
11
|
-
end
|
12
|
-
|
7
|
+
install_honeybadger
|
13
8
|
build_without_honeybadger(*args, &block)
|
14
9
|
end
|
15
10
|
alias :build_without_honeybadger :build
|
@@ -20,6 +15,21 @@ module Honeybadger
|
|
20
15
|
api_key: defined?(honeybadger_api_key) ? honeybadger_api_key : nil
|
21
16
|
}
|
22
17
|
end
|
18
|
+
|
19
|
+
def install_honeybadger
|
20
|
+
config = Honeybadger::Config.new(honeybadger_config(self))
|
21
|
+
|
22
|
+
return unless config[:'sinatra.enabled']
|
23
|
+
return unless Honeybadger.start(config)
|
24
|
+
|
25
|
+
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
|
+
end
|
28
|
+
|
29
|
+
def install_honeybadger_middleware(klass, config)
|
30
|
+
return if middleware.any? {|m| m[0] == klass }
|
31
|
+
use(klass, config)
|
32
|
+
end
|
23
33
|
end
|
24
34
|
end
|
25
35
|
end
|
data/lib/honeybadger/notice.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'securerandom'
|
3
3
|
require 'forwardable'
|
4
|
-
require 'ostruct'
|
5
4
|
|
6
5
|
require 'honeybadger/version'
|
7
6
|
require 'honeybadger/backtrace'
|
8
7
|
require 'honeybadger/util/stats'
|
9
8
|
require 'honeybadger/util/sanitizer'
|
10
|
-
require 'honeybadger/
|
9
|
+
require 'honeybadger/util/request_payload'
|
11
10
|
|
12
11
|
module Honeybadger
|
13
12
|
NOTIFIER = {
|
@@ -32,16 +31,6 @@ module Honeybadger
|
|
32
31
|
# Internal: Matches lines beginning with ./
|
33
32
|
RELATIVE_ROOT = Regexp.new('^\.\/').freeze
|
34
33
|
|
35
|
-
# Internal: default values to use for request data.
|
36
|
-
REQUEST_DEFAULTS = {
|
37
|
-
url: nil,
|
38
|
-
component: nil,
|
39
|
-
action: nil,
|
40
|
-
params: {}.freeze,
|
41
|
-
session: {}.freeze,
|
42
|
-
cgi_data: {}.freeze
|
43
|
-
}.freeze
|
44
|
-
|
45
34
|
MAX_EXCEPTION_CAUSES = 5
|
46
35
|
|
47
36
|
class Notice
|
@@ -79,24 +68,25 @@ module Honeybadger
|
|
79
68
|
attr_reader :source
|
80
69
|
|
81
70
|
# Public: CGI variables such as HTTP_METHOD.
|
82
|
-
|
71
|
+
def cgi_data; @request[:cgi_data]; end
|
83
72
|
|
84
73
|
# Public: A hash of parameters from the query string or post body.
|
85
|
-
|
74
|
+
def params; @request[:params]; end
|
86
75
|
alias_method :parameters, :params
|
87
76
|
|
88
77
|
# Public: The component (if any) which was used in this request. (usually the controller)
|
89
|
-
|
78
|
+
def component; @request[:component]; end
|
90
79
|
alias_method :controller, :component
|
91
80
|
|
92
81
|
# Public: The action (if any) that was called in this request.
|
93
|
-
|
82
|
+
def action; @request[:action]; end
|
94
83
|
|
95
84
|
# Public: A hash of session data from the request.
|
96
85
|
def_delegator :@request, :session
|
86
|
+
def session; @request[:session]; end
|
97
87
|
|
98
88
|
# Public: The URL at which the error occurred (if any).
|
99
|
-
|
89
|
+
def url; @request[:url]; end
|
100
90
|
|
101
91
|
# Public: Local variables are extracted from first frame of backtrace.
|
102
92
|
attr_reader :local_variables
|
@@ -138,24 +128,21 @@ module Honeybadger
|
|
138
128
|
@config = config
|
139
129
|
|
140
130
|
@sanitizer = Util::Sanitizer.new
|
141
|
-
@filtered_sanitizer = Util::Sanitizer.new(filters: config.params_filters)
|
142
131
|
|
143
|
-
@exception = opts[:exception]
|
132
|
+
@exception = unwrap_exception(opts[:exception])
|
144
133
|
@error_class = exception_attribute(:error_class) {|exception| exception.class.name }
|
145
|
-
@error_message =
|
146
|
-
|
147
|
-
"#{exception.class.name}: #{exception.message}"
|
148
|
-
end
|
134
|
+
@error_message = exception_attribute(:error_message, 'Notification') do |exception|
|
135
|
+
"#{exception.class.name}: #{exception.message}"
|
149
136
|
end
|
150
137
|
@backtrace = parse_backtrace(exception_attribute(:backtrace, caller))
|
151
138
|
@source = extract_source_from_backtrace(@backtrace, config, opts)
|
152
139
|
@fingerprint = construct_fingerprint(opts)
|
153
140
|
|
154
|
-
@request =
|
141
|
+
@request = construct_request_hash(config, opts)
|
155
142
|
|
156
143
|
@context = construct_context_hash(opts)
|
157
144
|
|
158
|
-
@causes = unwrap_causes(
|
145
|
+
@causes = unwrap_causes(@exception)
|
159
146
|
|
160
147
|
@tags = construct_tags(opts[:tags])
|
161
148
|
@tags = construct_tags(context[:tags]) | @tags if context
|
@@ -186,16 +173,10 @@ module Honeybadger
|
|
186
173
|
tags: s(tags),
|
187
174
|
causes: s(causes)
|
188
175
|
},
|
189
|
-
request: {
|
190
|
-
url: sanitized_url,
|
191
|
-
component: s(component),
|
192
|
-
action: s(action),
|
193
|
-
params: f(params),
|
194
|
-
session: f(session),
|
195
|
-
cgi_data: f(cgi_data),
|
176
|
+
request: @request.update({
|
196
177
|
context: s(context),
|
197
178
|
local_variables: s(local_variables)
|
198
|
-
},
|
179
|
+
}),
|
199
180
|
server: {
|
200
181
|
project_root: s(config[:root]),
|
201
182
|
environment_name: s(config[:env]),
|
@@ -239,7 +220,7 @@ module Honeybadger
|
|
239
220
|
|
240
221
|
private
|
241
222
|
|
242
|
-
attr_reader :config, :opts, :context, :stats, :api_key, :now, :pid, :causes, :sanitizer
|
223
|
+
attr_reader :config, :opts, :context, :stats, :api_key, :now, :pid, :causes, :sanitizer
|
243
224
|
|
244
225
|
def ignore_by_origin?
|
245
226
|
opts[:origin] == :rake && !config[:'exceptions.rescue_rake']
|
@@ -262,7 +243,7 @@ module Honeybadger
|
|
262
243
|
#
|
263
244
|
# Returns attribute value from args or exception, otherwise default
|
264
245
|
def exception_attribute(attribute, default = nil, &block)
|
265
|
-
opts[attribute] || (
|
246
|
+
opts[attribute] || (exception && from_exception(attribute, &block)) || default
|
266
247
|
end
|
267
248
|
|
268
249
|
# Gets a property named +attribute+ from an exception.
|
@@ -274,12 +255,12 @@ module Honeybadger
|
|
274
255
|
# If no block is given, a method with the same name as +attribute+ will be
|
275
256
|
# invoked for the value.
|
276
257
|
def from_exception(attribute)
|
277
|
-
return unless
|
258
|
+
return unless exception
|
278
259
|
|
279
260
|
if block_given?
|
280
|
-
yield(
|
261
|
+
yield(exception)
|
281
262
|
else
|
282
|
-
|
263
|
+
exception.send(attribute)
|
283
264
|
end
|
284
265
|
end
|
285
266
|
|
@@ -302,50 +283,24 @@ module Honeybadger
|
|
302
283
|
ignored_class ? @ignore_by_class.call(ignored_class) : config[:'exceptions.ignore'].any?(&@ignore_by_class)
|
303
284
|
end
|
304
285
|
|
305
|
-
# Limit size of string to bytes
|
306
|
-
#
|
307
|
-
# input - The String to be trimmed.
|
308
|
-
# bytes - The Integer bytes to trim.
|
309
|
-
# block - An optional block used in place of input.
|
310
|
-
#
|
311
|
-
# Examples
|
312
|
-
#
|
313
|
-
# trimmed = trim_size("Honeybadger doesn't care", 3)
|
314
|
-
#
|
315
|
-
# trimmed = trim_size(3) do
|
316
|
-
# "Honeybadger doesn't care"
|
317
|
-
# end
|
318
|
-
#
|
319
|
-
# Returns trimmed String
|
320
|
-
def trim_size(*args, &block)
|
321
|
-
input, bytes = args.first, args.last
|
322
|
-
input = yield if block_given?
|
323
|
-
input = input.dup
|
324
|
-
input = input[0...bytes] if input.respond_to?(:size) && input.size > bytes
|
325
|
-
input
|
326
|
-
end
|
327
|
-
|
328
286
|
def construct_backtrace_filters(opts)
|
329
287
|
[
|
330
288
|
opts[:callbacks] ? opts[:callbacks].backtrace_filter : nil
|
331
289
|
].compact | BACKTRACE_FILTERS
|
332
290
|
end
|
333
291
|
|
334
|
-
|
292
|
+
# Internal: Construct the request object with data from various sources.
|
293
|
+
#
|
294
|
+
# Returns Request.
|
295
|
+
def construct_request_hash(config, opts)
|
335
296
|
request = {}
|
336
|
-
request.merge!(
|
337
|
-
|
297
|
+
request.merge!(config.request_hash)
|
298
|
+
request.merge!(opts)
|
338
299
|
request[:component] = opts[:controller] if opts.has_key?(:controller)
|
339
300
|
request[:params] = opts[:parameters] if opts.has_key?(:parameters)
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
request[key] = default if !request[key] || excluded_keys.include?(key)
|
344
|
-
end
|
345
|
-
|
346
|
-
request[:session] = request[:session][:data] if request[:session][:data]
|
347
|
-
|
348
|
-
request
|
301
|
+
request.delete_if {|k,v| config.excluded_request_keys.include?(k) }
|
302
|
+
request[:sanitizer] = Util::Sanitizer.new(filters: config.params_filters)
|
303
|
+
Util::RequestPayload.build(request)
|
349
304
|
end
|
350
305
|
|
351
306
|
def construct_context_hash(opts)
|
@@ -356,21 +311,19 @@ module Honeybadger
|
|
356
311
|
end
|
357
312
|
|
358
313
|
def extract_source_from_backtrace(backtrace, config, opts)
|
359
|
-
if backtrace.lines.empty?
|
360
|
-
|
314
|
+
return nil if backtrace.lines.empty?
|
315
|
+
|
316
|
+
# ActionView::Template::Error has its own source_extract method.
|
317
|
+
# If present, use that instead.
|
318
|
+
if exception.respond_to?(:source_extract) && exception.source_extract
|
319
|
+
Hash[exception.source_extract.split("\n").map do |line|
|
320
|
+
parts = line.split(': ')
|
321
|
+
[parts[0].strip, parts[1] || '']
|
322
|
+
end]
|
323
|
+
elsif backtrace.application_lines.any?
|
324
|
+
backtrace.application_lines.first.source(config[:'exceptions.source_radius'])
|
361
325
|
else
|
362
|
-
|
363
|
-
# If present, use that instead.
|
364
|
-
if opts[:exception].respond_to?(:source_extract) && opts[:exception].source_extract
|
365
|
-
Hash[exception.source_extract.split("\n").map do |line|
|
366
|
-
parts = line.split(': ')
|
367
|
-
[parts[0].strip, parts[1] || '']
|
368
|
-
end]
|
369
|
-
elsif backtrace.application_lines.any?
|
370
|
-
backtrace.application_lines.first.source(config[:'exceptions.source_radius'])
|
371
|
-
else
|
372
|
-
backtrace.lines.first.source(config[:'exceptions.source_radius'])
|
373
|
-
end
|
326
|
+
backtrace.lines.first.source(config[:'exceptions.source_radius'])
|
374
327
|
end
|
375
328
|
end
|
376
329
|
|
@@ -408,15 +361,6 @@ module Honeybadger
|
|
408
361
|
sanitizer.sanitize(data)
|
409
362
|
end
|
410
363
|
|
411
|
-
def f(data)
|
412
|
-
filtered_sanitizer.sanitize(data)
|
413
|
-
end
|
414
|
-
|
415
|
-
def sanitized_url
|
416
|
-
return nil unless url
|
417
|
-
filtered_sanitizer.filter_url(s(url))
|
418
|
-
end
|
419
|
-
|
420
364
|
# Internal: Fetch local variables from first frame of backtrace.
|
421
365
|
#
|
422
366
|
# exception - The Exception containing the bindings stack.
|
@@ -458,6 +402,17 @@ module Honeybadger
|
|
458
402
|
)
|
459
403
|
end
|
460
404
|
|
405
|
+
# Internal: Unwrap the exception so that original exception is ignored or
|
406
|
+
# reported.
|
407
|
+
#
|
408
|
+
# exception - The exception which was rescued.
|
409
|
+
#
|
410
|
+
# Returns the Exception to report.
|
411
|
+
def unwrap_exception(exception)
|
412
|
+
return exception unless config[:'exceptions.unwrap']
|
413
|
+
exception_cause(exception) || exception
|
414
|
+
end
|
415
|
+
|
461
416
|
# Internal: Fetch cause from exception.
|
462
417
|
#
|
463
418
|
# exception - Exception to fetch cause from.
|
@@ -484,7 +439,7 @@ module Honeybadger
|
|
484
439
|
while (e = exception_cause(e)) && i < MAX_EXCEPTION_CAUSES
|
485
440
|
c << {
|
486
441
|
class: e.class.name,
|
487
|
-
message:
|
442
|
+
message: e.message,
|
488
443
|
backtrace: parse_backtrace(e.backtrace || caller)
|
489
444
|
}
|
490
445
|
i += 1
|
@@ -8,21 +8,34 @@ module Honeybadger
|
|
8
8
|
callbacks do |lifecycle|
|
9
9
|
lifecycle.around(:invoke_job) do |job, &block|
|
10
10
|
begin
|
11
|
+
begin #buildin suport for Delayed::PerformableMethod
|
12
|
+
component = job.payload_object.object.is_a?(Class) ? job.payload_object.object.name : job.payload_object.object.class.name
|
13
|
+
action = job.payload_object.method_name.to_s
|
14
|
+
rescue #fallback to support all other classes
|
15
|
+
component = job.payload_object.class.name
|
16
|
+
action = 'perform'
|
17
|
+
end
|
18
|
+
|
19
|
+
::Honeybadger.context(
|
20
|
+
:component => component,
|
21
|
+
:action => action,
|
22
|
+
:job_id => job.id,
|
23
|
+
:handler => job.handler,
|
24
|
+
:last_error => job.last_error,
|
25
|
+
:attempts => job.attempts,
|
26
|
+
:queue => job.queue
|
27
|
+
)
|
28
|
+
|
11
29
|
::Honeybadger::Trace.instrument("#{job.payload_object.class}#perform", {source: 'delayed_job', jid: job.id, class: job.payload_object.class.name}) do
|
12
30
|
block.call(job)
|
13
31
|
end
|
14
32
|
rescue Exception => error
|
15
33
|
::Honeybadger.notify_or_ignore(
|
34
|
+
:component => component,
|
35
|
+
:action => action,
|
16
36
|
:error_class => error.class.name,
|
17
37
|
:error_message => "#{ error.class.name }: #{ error.message }",
|
18
|
-
:backtrace => error.backtrace
|
19
|
-
:context => {
|
20
|
-
:job_id => job.id,
|
21
|
-
:handler => job.handler,
|
22
|
-
:last_error => job.last_error,
|
23
|
-
:attempts => job.attempts,
|
24
|
-
:queue => job.queue
|
25
|
-
}
|
38
|
+
:backtrace => error.backtrace
|
26
39
|
) if job.attempts.to_i >= ::Honeybadger::Agent.config[:'delayed_job.attempt_threshold'].to_i
|
27
40
|
raise error
|
28
41
|
ensure
|