honeybadger 4.3.1 → 4.4.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 +6 -0
- data/lib/honeybadger/agent.rb +55 -4
- data/lib/honeybadger/breadcrumbs.rb +8 -0
- data/lib/honeybadger/breadcrumbs/active_support.rb +108 -0
- data/lib/honeybadger/breadcrumbs/breadcrumb.rb +53 -0
- data/lib/honeybadger/breadcrumbs/collector.rb +82 -0
- data/lib/honeybadger/breadcrumbs/logging.rb +50 -0
- data/lib/honeybadger/breadcrumbs/ring_buffer.rb +44 -0
- data/lib/honeybadger/config/defaults.rb +16 -0
- data/lib/honeybadger/config/env.rb +6 -1
- data/lib/honeybadger/init/rake.rb +1 -1
- data/lib/honeybadger/logging.rb +8 -3
- data/lib/honeybadger/notice.rb +12 -0
- data/lib/honeybadger/plugins/breadcrumbs.rb +109 -0
- data/lib/honeybadger/plugins/delayed_job/plugin.rb +1 -1
- data/lib/honeybadger/plugins/resque.rb +1 -1
- data/lib/honeybadger/plugins/shoryuken.rb +1 -1
- data/lib/honeybadger/plugins/sidekiq.rb +1 -1
- data/lib/honeybadger/rack/error_notifier.rb +22 -3
- data/lib/honeybadger/rack/user_feedback.rb +6 -2
- data/lib/honeybadger/rack/user_informer.rb +5 -2
- data/lib/honeybadger/singleton.rb +3 -0
- data/lib/honeybadger/util/sql.rb +25 -0
- data/lib/honeybadger/version.rb +1 -1
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5bd1d88346f34070874c2929f1f322ed318157fd7772d2d89848685d3e23adcb
|
4
|
+
data.tar.gz: dada0e8a7039c5750a7406e5c5c8452ae942c369ebac17e816ba15a977b1ea33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad7229d00efd55f9a1f02b32d510bb7f97f022df4de9713e750063539ef67db1a482841e51b079c1b7b6659e161bb2235d41273095c3e776b5b8cb536a6af755
|
7
|
+
data.tar.gz: f6ee33891464589594aa9c3b4cdf3489943b9a4accf99e199723b395d999a0b45b242f9e510d7f3466c6133aca47b8abff8927ef9ac022b58fc00f285421cb22
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,12 @@ adheres to [Semantic Versioning](http://semver.org/).
|
|
5
5
|
|
6
6
|
## [Unreleased]
|
7
7
|
|
8
|
+
## [4.4.0] - 2019-07-24
|
9
|
+
### Added
|
10
|
+
- Added the ability to store and send Breadcrumbs along with the notice.
|
11
|
+
Breadcrumbs are disabled by default in this version so they must be enabled
|
12
|
+
via the config (option `breadcrumbs.enabled`) to work.
|
13
|
+
|
8
14
|
## [4.3.1] - 2019-05-30
|
9
15
|
### Fixed
|
10
16
|
- Add Rails 6 RC1 Support
|
data/lib/honeybadger/agent.rb
CHANGED
@@ -7,6 +7,7 @@ require 'honeybadger/notice'
|
|
7
7
|
require 'honeybadger/plugin'
|
8
8
|
require 'honeybadger/logging'
|
9
9
|
require 'honeybadger/worker'
|
10
|
+
require 'honeybadger/breadcrumbs'
|
10
11
|
|
11
12
|
module Honeybadger
|
12
13
|
# The Honeybadger agent contains all the methods for interacting with the
|
@@ -62,7 +63,10 @@ module Honeybadger
|
|
62
63
|
end
|
63
64
|
|
64
65
|
@context = opts.delete(:context)
|
65
|
-
|
66
|
+
if opts.delete(:local_context)
|
67
|
+
@context ||= ContextManager.new
|
68
|
+
@breadcrumbs = Breadcrumbs::Collector.new(config)
|
69
|
+
end
|
66
70
|
|
67
71
|
@config ||= Config.new(opts)
|
68
72
|
|
@@ -120,8 +124,15 @@ module Honeybadger
|
|
120
124
|
|
121
125
|
validate_notify_opts!(opts)
|
122
126
|
|
127
|
+
add_breadcrumb(
|
128
|
+
"Honeybadger Notice",
|
129
|
+
metadata: opts,
|
130
|
+
category: "notice"
|
131
|
+
) if config[:'breadcrumbs.enabled']
|
132
|
+
|
123
133
|
opts[:rack_env] ||= context_manager.get_rack_env
|
124
134
|
opts[:global_context] ||= context_manager.get_context
|
135
|
+
opts[:breadcrumbs] ||= breadcrumbs.dup
|
125
136
|
|
126
137
|
notice = Notice.new(config, opts)
|
127
138
|
|
@@ -214,10 +225,10 @@ module Honeybadger
|
|
214
225
|
self
|
215
226
|
end
|
216
227
|
|
217
|
-
#
|
218
|
-
|
219
|
-
def clear! # :nodoc:
|
228
|
+
# Clear all transaction scoped data.
|
229
|
+
def clear!
|
220
230
|
context_manager.clear!
|
231
|
+
breadcrumbs.clear!
|
221
232
|
end
|
222
233
|
|
223
234
|
# Get global context for the current request.
|
@@ -231,6 +242,46 @@ module Honeybadger
|
|
231
242
|
context_manager.get_context
|
232
243
|
end
|
233
244
|
|
245
|
+
# @api private
|
246
|
+
# Direct access to the Breadcrumbs::Collector instance
|
247
|
+
def breadcrumbs
|
248
|
+
return @breadcrumbs if @breadcrumbs
|
249
|
+
|
250
|
+
Thread.current[:__hb_breadcrumbs] ||= Breadcrumbs::Collector.new(config)
|
251
|
+
end
|
252
|
+
|
253
|
+
# Appends a breadcrumb to the trace. Use this when you want to add some
|
254
|
+
# custom data to your breadcrumb trace in effort to help debugging. If a
|
255
|
+
# notice is reported to Honeybadger, all breadcrumbs within the execution
|
256
|
+
# path will be appended to the notice. You will be able to view the
|
257
|
+
# breadcrumb trace in the Honeybadger interface to see what events led up
|
258
|
+
# to the notice.
|
259
|
+
#
|
260
|
+
# @example
|
261
|
+
# Honeybadger.add_breadcrumb("Email Sent", metadata: { user: user.id, message: message })
|
262
|
+
#
|
263
|
+
# @param message [String] The message you want to send with the breadcrumb
|
264
|
+
# @param params [Hash] extra options for breadcrumb building
|
265
|
+
# @option params [Hash] :metadata Any metadata that you want to pass along
|
266
|
+
# with the breadcrumb. We only accept a hash with simple primatives as
|
267
|
+
# values (Strings, Numbers, Booleans & Symbols) (optional)
|
268
|
+
# @option params [String] :category You can provide a custom category. This
|
269
|
+
# affects how the breadcrumb is displayed, so we recommend that you pick a
|
270
|
+
# known category. (optional)
|
271
|
+
#
|
272
|
+
# @return self
|
273
|
+
def add_breadcrumb(message, metadata: {}, category: "custom")
|
274
|
+
params = Util::Sanitizer.new(max_depth: 2).sanitize({
|
275
|
+
category: category,
|
276
|
+
message: message,
|
277
|
+
metadata: metadata
|
278
|
+
})
|
279
|
+
|
280
|
+
breadcrumbs.add!(Breadcrumbs::Breadcrumb.new(params))
|
281
|
+
|
282
|
+
self
|
283
|
+
end
|
284
|
+
|
234
285
|
# Flushes all data from workers before returning. This is most useful in
|
235
286
|
# tests when using the test backend, where normally the asynchronous nature
|
236
287
|
# of this library could create race conditions.
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'honeybadger/util/sql'
|
2
|
+
|
3
|
+
module Honeybadger
|
4
|
+
module Breadcrumbs
|
5
|
+
class ActiveSupport
|
6
|
+
def self.default_notifications
|
7
|
+
{
|
8
|
+
# ActiveRecord Actions
|
9
|
+
#
|
10
|
+
"sql.active_record" => {
|
11
|
+
message: lambda do |data|
|
12
|
+
# Disregard empty string names
|
13
|
+
name = data[:name] if data[:name] && !data[:name].strip.empty?
|
14
|
+
|
15
|
+
["Active Record", name].compact.join(" - ")
|
16
|
+
end,
|
17
|
+
category: "query",
|
18
|
+
select_keys: [:sql, :name, :connection_id, :cached],
|
19
|
+
transform: lambda do |data|
|
20
|
+
if data[:sql]
|
21
|
+
adapter = ::ActiveRecord::Base.connection_config[:adapter]
|
22
|
+
data[:sql] = Util::SQL.obfuscate(data[:sql], adapter)
|
23
|
+
end
|
24
|
+
data
|
25
|
+
end,
|
26
|
+
exclude_when: lambda do |data|
|
27
|
+
# Ignore schema, begin, and commit transaction queries
|
28
|
+
data[:name] == "SCHEMA" || (data[:sql] && (data[:sql] =~ /^(begin|commit)( transaction)?$/i))
|
29
|
+
end
|
30
|
+
},
|
31
|
+
|
32
|
+
# ActionCable Actions
|
33
|
+
#
|
34
|
+
"perform_action.action_cable" => {
|
35
|
+
message: "Action Cable Perform Action",
|
36
|
+
select_keys: [:channel_class, :action],
|
37
|
+
category: "render"
|
38
|
+
},
|
39
|
+
|
40
|
+
# ActiveJob Actions
|
41
|
+
#
|
42
|
+
"enqueue.active_job" => {
|
43
|
+
message: "Active Job Enqueue",
|
44
|
+
select_keys: [],
|
45
|
+
category: "job"
|
46
|
+
},
|
47
|
+
"perform_start.active_job" => {
|
48
|
+
message: "Active Job Perform Start",
|
49
|
+
select_keys: [],
|
50
|
+
category: "job",
|
51
|
+
},
|
52
|
+
|
53
|
+
# ActiveSupport Actions
|
54
|
+
#
|
55
|
+
"cache_read.active_support" => {
|
56
|
+
message: "Active Support Cache Read",
|
57
|
+
category: "query"
|
58
|
+
},
|
59
|
+
"cache_fetch_hit.active_support" => {
|
60
|
+
message: "Active Support Cache Fetch Hit",
|
61
|
+
category: "query"
|
62
|
+
},
|
63
|
+
|
64
|
+
# Controller Actions
|
65
|
+
#
|
66
|
+
"halted_callback.action_controller" => {
|
67
|
+
message: "Action Controller Callback Halted",
|
68
|
+
category: "request",
|
69
|
+
},
|
70
|
+
"process_action.action_controller" => {
|
71
|
+
message: "Action Controller Action Process",
|
72
|
+
select_keys: [:controller, :action, :format, :method, :path, :status, :view_runtime, :db_runtime],
|
73
|
+
category: "request",
|
74
|
+
},
|
75
|
+
"start_processing.action_controller" => {
|
76
|
+
message: "Action Controller Start Process",
|
77
|
+
select_keys: [:controller, :action, :format, :method, :path],
|
78
|
+
category: "request",
|
79
|
+
},
|
80
|
+
"redirect_to.action_controller" => {
|
81
|
+
message: "Action Controller Redirect",
|
82
|
+
category: "request",
|
83
|
+
},
|
84
|
+
|
85
|
+
# View Actions
|
86
|
+
#
|
87
|
+
"render_template.action_view" => {
|
88
|
+
message: "Action View Template Render",
|
89
|
+
category: "render",
|
90
|
+
},
|
91
|
+
"render_partial.action_view" => {
|
92
|
+
message: "Action View Partial Render",
|
93
|
+
category: "render",
|
94
|
+
},
|
95
|
+
|
96
|
+
# Mailer actions
|
97
|
+
#
|
98
|
+
"deliver.action_mailer" => {
|
99
|
+
message: "Action Mailer Deliver",
|
100
|
+
select_keys: [:mailer, :message_id, :from, :date],
|
101
|
+
category: "render"
|
102
|
+
}
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module Honeybadger
|
4
|
+
module Breadcrumbs
|
5
|
+
class Breadcrumb
|
6
|
+
# Raw breadcrumb data structure
|
7
|
+
#
|
8
|
+
attr_reader :category, :timestamp
|
9
|
+
attr_accessor :message, :metadata, :active
|
10
|
+
|
11
|
+
include Comparable
|
12
|
+
|
13
|
+
def initialize(category: "custom", message: nil, metadata: {})
|
14
|
+
@active = true
|
15
|
+
@timestamp = Time.now.utc
|
16
|
+
|
17
|
+
@category = category
|
18
|
+
@message = message
|
19
|
+
@metadata = metadata.is_a?(Hash) ? metadata : {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_h
|
23
|
+
{
|
24
|
+
category: category,
|
25
|
+
message: message,
|
26
|
+
metadata: metadata,
|
27
|
+
timestamp: timestamp.iso8601(3)
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def <=>(other)
|
32
|
+
to_h <=> other.to_h
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# Is the Breadcrumb active or not. Inactive Breadcrumbs not be included
|
37
|
+
# with any outgoing payloads.
|
38
|
+
#
|
39
|
+
# @return [Boolean]
|
40
|
+
def active?
|
41
|
+
@active
|
42
|
+
end
|
43
|
+
|
44
|
+
# Sets the breadcrumb to inactive
|
45
|
+
#
|
46
|
+
# @return self
|
47
|
+
def ignore!
|
48
|
+
@active = false
|
49
|
+
self
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Honeybadger
|
4
|
+
module Breadcrumbs
|
5
|
+
class Collector
|
6
|
+
include Enumerable
|
7
|
+
extend Forwardable
|
8
|
+
# The Collector manages breadcrumbs and provides an interface for accessing
|
9
|
+
# and affecting breadcrumbs
|
10
|
+
#
|
11
|
+
# Most actions are delegated to the current buffer implementation. A
|
12
|
+
# Buffer must implement all delegated methods to work with the Collector.
|
13
|
+
|
14
|
+
# Flush all breadcrumbs, delegates to buffer
|
15
|
+
def_delegator :@buffer, :clear!
|
16
|
+
|
17
|
+
# Iterate over all Breadcrumbs and satify Enumerable, delegates to buffer
|
18
|
+
# @yield [Object] sequentially gives breadcrumbs to the block
|
19
|
+
def_delegator :@buffer, :each
|
20
|
+
|
21
|
+
# Raw Array of Breadcrumbs, delegates to buffer
|
22
|
+
# @return [Array] Raw set of breadcrumbs
|
23
|
+
def_delegator :@buffer, :to_a
|
24
|
+
|
25
|
+
# Last item added to the buffer
|
26
|
+
# @return [Breadcrumb]
|
27
|
+
def_delegator :@buffer, :previous
|
28
|
+
|
29
|
+
def initialize(config, buffer = RingBuffer.new)
|
30
|
+
@config = config
|
31
|
+
@buffer = buffer
|
32
|
+
end
|
33
|
+
|
34
|
+
# Add Breadcrumb to stack
|
35
|
+
#
|
36
|
+
# @return [self] Filtered breadcrumbs
|
37
|
+
def add!(breadcrumb)
|
38
|
+
return unless @config[:'breadcrumbs.enabled']
|
39
|
+
@buffer.add!(breadcrumb)
|
40
|
+
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
alias_method :<<, :add!
|
45
|
+
|
46
|
+
# @api private
|
47
|
+
# Removes the prevous breadcrumb from the buffer if the supplied
|
48
|
+
# block returns a falsy value
|
49
|
+
#
|
50
|
+
def drop_previous_breadcrumb_if
|
51
|
+
@buffer.drop if (previous && block_given? && yield(previous))
|
52
|
+
end
|
53
|
+
|
54
|
+
# All active breadcrumbs you want to remove a breadcrumb from the trail,
|
55
|
+
# then you can selectively ignore breadcrumbs while building a notice.
|
56
|
+
#
|
57
|
+
# @return [Array] Active breadcrumbs
|
58
|
+
def trail
|
59
|
+
select(&:active?)
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_h
|
63
|
+
{
|
64
|
+
enabled: @config[:'breadcrumbs.enabled'],
|
65
|
+
trail: trail.map(&:to_h)
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# @api private
|
72
|
+
# Since the collector is shared with the worker thread, there is a chance
|
73
|
+
# it can be cleared before we have prepared the request. We provide the
|
74
|
+
# ability to duplicate a collector which should also duplicate the buffer
|
75
|
+
# instance, as that holds the breadcrumbs.
|
76
|
+
def initialize_dup(source)
|
77
|
+
@buffer = source.instance_variable_get(:@buffer).dup
|
78
|
+
super
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Honeybadger
|
2
|
+
module Breadcrumbs
|
3
|
+
# @api private
|
4
|
+
#
|
5
|
+
module LogWrapper
|
6
|
+
def add(severity, message = nil, progname = nil)
|
7
|
+
message, progname = [progname, nil] if message.nil?
|
8
|
+
message = message && message.strip
|
9
|
+
unless should_ignore_log?(message, progname)
|
10
|
+
Honeybadger.add_breadcrumb(message, category: :log, metadata: {
|
11
|
+
severity: format_severity(severity),
|
12
|
+
progname: progname
|
13
|
+
})
|
14
|
+
end
|
15
|
+
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def should_ignore_log?(message, progname)
|
22
|
+
message.nil? ||
|
23
|
+
message == "" ||
|
24
|
+
Thread.current[:__hb_within_log_subscriber] ||
|
25
|
+
progname == "honeybadger"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# @api private
|
30
|
+
#
|
31
|
+
# This module is designed to be prepended into the
|
32
|
+
# ActiveSupport::LogSubscriber for the sole purpose of silencing breadcrumb
|
33
|
+
# log events. Since we already have specific breadcrumb events for each
|
34
|
+
# class that provides LogSubscriber events, we want to filter out those
|
35
|
+
# logs as they just become noise.
|
36
|
+
module LogSubscriberInjector
|
37
|
+
%w(info debug warn error fatal unknown).each do |level|
|
38
|
+
define_method(level) do |*args|
|
39
|
+
begin
|
40
|
+
Thread.current[:__hb_within_log_subscriber] = true
|
41
|
+
super(*args)
|
42
|
+
ensure
|
43
|
+
Thread.current[:__hb_within_log_subscriber] = false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Honeybadger
|
2
|
+
module Breadcrumbs
|
3
|
+
class RingBuffer
|
4
|
+
# Simple ring buffer implementation that keeps item count constrained using
|
5
|
+
# a rolling window. Items from the front of the buffer are dropped as more
|
6
|
+
# are pushed on the end of the stack.
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
attr_reader :buffer
|
10
|
+
|
11
|
+
def initialize(buffer_size = 40)
|
12
|
+
@buffer_size = buffer_size
|
13
|
+
clear!
|
14
|
+
end
|
15
|
+
|
16
|
+
def add!(item)
|
17
|
+
@buffer << item
|
18
|
+
@ct += 1
|
19
|
+
@buffer.shift(1) if @ct > @buffer_size
|
20
|
+
end
|
21
|
+
|
22
|
+
def clear!
|
23
|
+
@buffer = []
|
24
|
+
@ct = 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_a
|
28
|
+
@buffer
|
29
|
+
end
|
30
|
+
|
31
|
+
def each(&blk)
|
32
|
+
@buffer.each(&blk)
|
33
|
+
end
|
34
|
+
|
35
|
+
def previous
|
36
|
+
@buffer.last
|
37
|
+
end
|
38
|
+
|
39
|
+
def drop
|
40
|
+
@buffer.pop
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'socket'
|
2
|
+
require 'honeybadger/breadcrumbs/active_support'
|
2
3
|
|
3
4
|
module Honeybadger
|
4
5
|
class Config
|
@@ -287,6 +288,21 @@ module Honeybadger
|
|
287
288
|
description: 'Send exceptions when retrying job.',
|
288
289
|
default: true,
|
289
290
|
type: Boolean
|
291
|
+
},
|
292
|
+
:'breadcrumbs.enabled' => {
|
293
|
+
description: 'Enable/Disable breadcrumb functionality.',
|
294
|
+
default: false,
|
295
|
+
type: Boolean
|
296
|
+
},
|
297
|
+
:'breadcrumbs.active_support_notifications' => {
|
298
|
+
description: 'Configuration for automatic Active Support Instrumentation events.',
|
299
|
+
default: Breadcrumbs::ActiveSupport.default_notifications,
|
300
|
+
type: Hash
|
301
|
+
},
|
302
|
+
:'breadcrumbs.logging.enabled' => {
|
303
|
+
description: 'Enable/Disable automatic breadcrumbs from log messages.',
|
304
|
+
default: true,
|
305
|
+
type: Boolean
|
290
306
|
}
|
291
307
|
}.freeze
|
292
308
|
|
@@ -1,9 +1,12 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Honeybadger
|
2
4
|
class Config
|
3
5
|
module Env
|
4
6
|
CONFIG_KEY = /\AHONEYBADGER_(.+)\Z/.freeze
|
5
7
|
CONFIG_MAPPING = Hash[DEFAULTS.keys.map {|k| [k.to_s.upcase.gsub(KEY_REPLACEMENT, '_'), k] }].freeze
|
6
8
|
ARRAY_VALUES = Regexp.new('\s*,\s*').freeze
|
9
|
+
IGNORED_TYPES = Set[Hash]
|
7
10
|
|
8
11
|
def self.new(env = ENV)
|
9
12
|
hash = {}
|
@@ -11,7 +14,9 @@ module Honeybadger
|
|
11
14
|
env.each_pair do |k,v|
|
12
15
|
next unless k.match(CONFIG_KEY)
|
13
16
|
next unless config_key = CONFIG_MAPPING[$1]
|
14
|
-
|
17
|
+
type = OPTIONS[config_key][:type]
|
18
|
+
next if IGNORED_TYPES.include?(type)
|
19
|
+
hash[config_key] = cast_value(v, type)
|
15
20
|
end
|
16
21
|
|
17
22
|
hash
|
data/lib/honeybadger/logging.rb
CHANGED
@@ -7,6 +7,7 @@ module Honeybadger
|
|
7
7
|
# @api private
|
8
8
|
module Logging
|
9
9
|
PREFIX = '** [Honeybadger] '.freeze
|
10
|
+
LOGGER_PROG = "honeybadger".freeze
|
10
11
|
|
11
12
|
# Logging helper methods. Requires a Honeybadger::Config @config instance
|
12
13
|
# variable to exist and/or #logger to be defined. Each method is
|
@@ -93,12 +94,16 @@ module Honeybadger
|
|
93
94
|
@logger = logger
|
94
95
|
end
|
95
96
|
|
96
|
-
|
97
|
+
def add(severity, msg, progname=LOGGER_PROG)
|
98
|
+
@logger.add(severity, msg, progname)
|
99
|
+
end
|
100
|
+
|
101
|
+
def_delegators :@logger, :level, :debug?, :info?, :warn?, :error?
|
97
102
|
end
|
98
103
|
|
99
104
|
class FormattedLogger < StandardLogger
|
100
|
-
def add(severity, msg)
|
101
|
-
super(severity, format_message(msg))
|
105
|
+
def add(severity, msg, progname=LOGGER_PROG)
|
106
|
+
super(severity, format_message(msg), progname)
|
102
107
|
end
|
103
108
|
|
104
109
|
private
|
data/lib/honeybadger/notice.rb
CHANGED
@@ -115,6 +115,9 @@ module Honeybadger
|
|
115
115
|
# Deprecated: Excerpt from source file.
|
116
116
|
attr_reader :source
|
117
117
|
|
118
|
+
# @return [Breadcrumbs::Collector] The collection of captured breadcrumbs
|
119
|
+
attr_accessor :breadcrumbs
|
120
|
+
|
118
121
|
# @api private
|
119
122
|
# Cache project path substitutions for backtrace lines.
|
120
123
|
PROJECT_ROOT_CACHE = {}
|
@@ -184,6 +187,8 @@ module Honeybadger
|
|
184
187
|
|
185
188
|
self.session = opts[:session][:data] if opts[:session] && opts[:session][:data]
|
186
189
|
|
190
|
+
self.breadcrumbs = opts[:breadcrumbs] || Breadcrumbs::Collector.new(config)
|
191
|
+
|
187
192
|
# Fingerprint must be calculated last since callback operates on `self`.
|
188
193
|
self.fingerprint = fingerprint_from_opts(opts)
|
189
194
|
end
|
@@ -200,6 +205,7 @@ module Honeybadger
|
|
200
205
|
{
|
201
206
|
api_key: s(api_key),
|
202
207
|
notifier: NOTIFIER,
|
208
|
+
breadcrumbs: sanitized_breadcrumbs,
|
203
209
|
error: {
|
204
210
|
token: id,
|
205
211
|
class: s(error_class),
|
@@ -354,6 +360,12 @@ module Honeybadger
|
|
354
360
|
Context(object)
|
355
361
|
end
|
356
362
|
|
363
|
+
# Sanitize at the depth of 4 since we are sanitizing the breadcrumb root
|
364
|
+
# hash data structure.
|
365
|
+
def sanitized_breadcrumbs
|
366
|
+
Util::Sanitizer.new(max_depth: 4).sanitize(breadcrumbs.to_h)
|
367
|
+
end
|
368
|
+
|
357
369
|
def construct_context_hash(opts, exception)
|
358
370
|
context = {}
|
359
371
|
context.merge!(Context(opts[:global_context]))
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'honeybadger/plugin'
|
2
|
+
require 'honeybadger/breadcrumbs/logging'
|
3
|
+
|
4
|
+
module Honeybadger
|
5
|
+
module Plugins
|
6
|
+
# @api private
|
7
|
+
#
|
8
|
+
# This plugin pounces on the dynamic nature of Ruby / Rails to inject into
|
9
|
+
# the runtime and provide automatic breadcrumb events.
|
10
|
+
#
|
11
|
+
# === Log events
|
12
|
+
#
|
13
|
+
# All log messages within the execution path will automatically be appened
|
14
|
+
# to the breadcrumb trace. You can disable all log events in the
|
15
|
+
# Honeybadger config:
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
#
|
19
|
+
# Honeybadger.configure do |config|
|
20
|
+
# config.breadcrumbs.logging.enabled = false
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# === ActiveSupport Breadcrumbs
|
24
|
+
#
|
25
|
+
# We hook into Rails's ActiveSupport Instrumentation system to provide
|
26
|
+
# automatic breadcrumb event generation. You can customize these events by
|
27
|
+
# passing a Hash into the honeybadger configuration. The simplest method is
|
28
|
+
# to alter the current defaults:
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# notifications = Honeybadger::Breadcrumbs::ActiveSupport.default_notifications
|
32
|
+
# notifications.delete("sql.active_record")
|
33
|
+
# notifications["enqueue.active_job"][:exclude_when] = lambda do |data|
|
34
|
+
# data[:job].topic == "salmon_activity"
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# Honeybadger.configure do |config|
|
38
|
+
# config.breadcrumbs.active_support_notifications = notifications
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# See RailsBreadcrumbs.send_breadcrumb_notification for specifics on the
|
42
|
+
# options for customization
|
43
|
+
Plugin.register :breadcrumbs do
|
44
|
+
requirement { config[:'breadcrumbs.enabled'] }
|
45
|
+
|
46
|
+
execution do
|
47
|
+
# Rails specific breadcrumb events
|
48
|
+
#
|
49
|
+
if defined?(::Rails.application) && ::Rails.application
|
50
|
+
config[:'breadcrumbs.active_support_notifications'].each do |name, config|
|
51
|
+
RailsBreadcrumbs.subscribe_to_notification(name, config)
|
52
|
+
end
|
53
|
+
ActiveSupport::LogSubscriber.prepend(Honeybadger::Breadcrumbs::LogSubscriberInjector) if config[:'breadcrumbs.logging.enabled']
|
54
|
+
end
|
55
|
+
|
56
|
+
::Logger.prepend(Honeybadger::Breadcrumbs::LogWrapper) if config[:'breadcrumbs.logging.enabled']
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class RailsBreadcrumbs
|
61
|
+
# @api private
|
62
|
+
# Used internally for sending out Rails Instrumentation breadcrumbs.
|
63
|
+
#
|
64
|
+
# @param [String] name The ActiveSupport instrumentation key
|
65
|
+
# @param [Number] duration The time spent in the instrumentation event
|
66
|
+
# @param [Hash] notification_config The instrumentation event configuration
|
67
|
+
# @param [Hash] data Custom metadata from the instrumentation event
|
68
|
+
#
|
69
|
+
# @option notification_config [String | Proc] :message A message that describes the event. You can dynamically build the message by passing a proc that accepts the event metadata.
|
70
|
+
# @option notification_config [Symbol] :category A key to group specific types of events
|
71
|
+
# @option notification_config [Array] :select_keys A set of keys that filters what data we select from the instrumentation data (optional)
|
72
|
+
# @option notification_config [Proc] :exclude_when A proc that accepts the data payload. A truthy return value will exclude this event from the payload (optional)
|
73
|
+
# @option notification_config [Proc] :transform A proc that accepts the data payload. The return value will replace the current data hash (optional)
|
74
|
+
#
|
75
|
+
def self.send_breadcrumb_notification(name, duration, notification_config, data = {})
|
76
|
+
return if notification_config[:exclude_when] && notification_config[:exclude_when].call(data)
|
77
|
+
|
78
|
+
message =
|
79
|
+
case (m = notification_config[:message])
|
80
|
+
when Proc
|
81
|
+
m.call(data)
|
82
|
+
when String
|
83
|
+
m
|
84
|
+
else
|
85
|
+
name
|
86
|
+
end
|
87
|
+
|
88
|
+
data = data.slice(*notification_config[:select_keys]) if notification_config[:select_keys]
|
89
|
+
data = notification_config[:transform].call(data) if notification_config[:transform]
|
90
|
+
data = data.is_a?(Hash) ? data : {}
|
91
|
+
|
92
|
+
data[:duration] = duration
|
93
|
+
|
94
|
+
Honeybadger.add_breadcrumb(
|
95
|
+
message,
|
96
|
+
category: notification_config[:category] || :custom,
|
97
|
+
metadata: data
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
# @api private
|
102
|
+
def self.subscribe_to_notification(name, notification_config)
|
103
|
+
ActiveSupport::Notifications.subscribe(name) do |_, started, finished, _, data|
|
104
|
+
send_breadcrumb_notification(name, finished - started, notification_config, data)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -23,7 +23,7 @@ module Honeybadger
|
|
23
23
|
|
24
24
|
def initialize(app, agent = nil)
|
25
25
|
@app = app
|
26
|
-
@agent = agent.kind_of?(Agent)
|
26
|
+
@agent = agent.kind_of?(Agent) && agent
|
27
27
|
end
|
28
28
|
|
29
29
|
def call(env)
|
@@ -44,15 +44,18 @@ module Honeybadger
|
|
44
44
|
response
|
45
45
|
end
|
46
46
|
ensure
|
47
|
-
agent.
|
47
|
+
agent.clear!
|
48
48
|
end
|
49
49
|
|
50
50
|
private
|
51
51
|
|
52
|
-
attr_reader :agent
|
53
52
|
def_delegator :agent, :config
|
54
53
|
def_delegator :config, :logger
|
55
54
|
|
55
|
+
def agent
|
56
|
+
@agent || Honeybadger::Agent.instance
|
57
|
+
end
|
58
|
+
|
56
59
|
def ignored_user_agent?(env)
|
57
60
|
true if config[:'exceptions.ignored_user_agents'].
|
58
61
|
flatten.
|
@@ -61,6 +64,22 @@ module Honeybadger
|
|
61
64
|
|
62
65
|
def notify_honeybadger(exception, env)
|
63
66
|
return if ignored_user_agent?(env)
|
67
|
+
|
68
|
+
if config[:'breadcrumbs.enabled']
|
69
|
+
# Drop the last breadcrumb only if the message contains the error class name
|
70
|
+
agent.breadcrumbs.drop_previous_breadcrumb_if do |bc|
|
71
|
+
bc.category == "log" && bc.message.include?(exception.class.to_s)
|
72
|
+
end
|
73
|
+
|
74
|
+
agent.add_breadcrumb(
|
75
|
+
exception.class,
|
76
|
+
metadata: {
|
77
|
+
exception_message: exception.message
|
78
|
+
},
|
79
|
+
category: "error"
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
64
83
|
agent.notify(exception)
|
65
84
|
end
|
66
85
|
|
@@ -23,7 +23,7 @@ module Honeybadger
|
|
23
23
|
|
24
24
|
def initialize(app, agent = nil)
|
25
25
|
@app = app
|
26
|
-
@agent = agent.kind_of?(Agent)
|
26
|
+
@agent = agent.kind_of?(Agent) && agent
|
27
27
|
end
|
28
28
|
|
29
29
|
def call(env)
|
@@ -76,9 +76,13 @@ module Honeybadger
|
|
76
76
|
|
77
77
|
private
|
78
78
|
|
79
|
-
attr_reader :agent
|
80
79
|
def_delegator :agent, :config
|
81
80
|
def_delegator :config, :logger
|
81
|
+
|
82
|
+
def agent
|
83
|
+
@agent || Honeybadger::Agent.instance
|
84
|
+
end
|
85
|
+
|
82
86
|
end
|
83
87
|
end
|
84
88
|
end
|
@@ -9,7 +9,7 @@ module Honeybadger
|
|
9
9
|
|
10
10
|
def initialize(app, agent = nil)
|
11
11
|
@app = app
|
12
|
-
@agent = agent.kind_of?(Agent)
|
12
|
+
@agent = agent.kind_of?(Agent) && agent
|
13
13
|
end
|
14
14
|
|
15
15
|
def replacement(with)
|
@@ -34,9 +34,12 @@ module Honeybadger
|
|
34
34
|
|
35
35
|
private
|
36
36
|
|
37
|
-
attr_reader :agent
|
38
37
|
def_delegator :agent, :config
|
39
38
|
def_delegator :config, :logger
|
39
|
+
|
40
|
+
def agent
|
41
|
+
@agent || Honeybadger::Agent.instance
|
42
|
+
end
|
40
43
|
end
|
41
44
|
end
|
42
45
|
end
|
@@ -34,6 +34,9 @@ module Honeybadger
|
|
34
34
|
def_delegator :'Honeybadger::Agent.instance', :exception_filter
|
35
35
|
def_delegator :'Honeybadger::Agent.instance', :exception_fingerprint
|
36
36
|
def_delegator :'Honeybadger::Agent.instance', :backtrace_filter
|
37
|
+
def_delegator :'Honeybadger::Agent.instance', :add_breadcrumb
|
38
|
+
def_delegator :'Honeybadger::Agent.instance', :breadcrumbs
|
39
|
+
def_delegator :'Honeybadger::Agent.instance', :clear!
|
37
40
|
|
38
41
|
# @!macro [attach] def_delegator
|
39
42
|
# @!method $2(...)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Honeybadger
|
2
|
+
module Util
|
3
|
+
class SQL
|
4
|
+
EscapedQuotes = /(\\"|\\')/.freeze
|
5
|
+
SQuotedData = /'(?:[^']|'')*'/.freeze
|
6
|
+
DQuotedData = /"(?:[^"]|"")*"/.freeze
|
7
|
+
NumericData = /\b\d+\b/.freeze
|
8
|
+
Newline = /\n/.freeze
|
9
|
+
Replacement = "?".freeze
|
10
|
+
EmptyReplacement = "".freeze
|
11
|
+
DoubleQuoters = /(postgres|sqlite|postgis)/.freeze
|
12
|
+
|
13
|
+
def self.obfuscate(sql, adapter)
|
14
|
+
sql.dup.tap do |s|
|
15
|
+
s.gsub!(EscapedQuotes, EmptyReplacement)
|
16
|
+
s.gsub!(SQuotedData, Replacement)
|
17
|
+
s.gsub!(DQuotedData, Replacement) if adapter =~ DoubleQuoters
|
18
|
+
s.gsub!(NumericData, Replacement)
|
19
|
+
s.gsub!(Newline, EmptyReplacement)
|
20
|
+
s.squeeze!(' ')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
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: 4.
|
4
|
+
version: 4.4.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: 2019-
|
11
|
+
date: 2019-07-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Make managing application errors a more pleasant experience.
|
14
14
|
email:
|
@@ -32,6 +32,12 @@ files:
|
|
32
32
|
- lib/honeybadger/backend/server.rb
|
33
33
|
- lib/honeybadger/backend/test.rb
|
34
34
|
- lib/honeybadger/backtrace.rb
|
35
|
+
- lib/honeybadger/breadcrumbs.rb
|
36
|
+
- lib/honeybadger/breadcrumbs/active_support.rb
|
37
|
+
- lib/honeybadger/breadcrumbs/breadcrumb.rb
|
38
|
+
- lib/honeybadger/breadcrumbs/collector.rb
|
39
|
+
- lib/honeybadger/breadcrumbs/logging.rb
|
40
|
+
- lib/honeybadger/breadcrumbs/ring_buffer.rb
|
35
41
|
- lib/honeybadger/cli.rb
|
36
42
|
- lib/honeybadger/cli/deploy.rb
|
37
43
|
- lib/honeybadger/cli/exec.rb
|
@@ -56,6 +62,7 @@ files:
|
|
56
62
|
- lib/honeybadger/logging.rb
|
57
63
|
- lib/honeybadger/notice.rb
|
58
64
|
- lib/honeybadger/plugin.rb
|
65
|
+
- lib/honeybadger/plugins/breadcrumbs.rb
|
59
66
|
- lib/honeybadger/plugins/delayed_job.rb
|
60
67
|
- lib/honeybadger/plugins/delayed_job/plugin.rb
|
61
68
|
- lib/honeybadger/plugins/local_variables.rb
|
@@ -79,6 +86,7 @@ files:
|
|
79
86
|
- lib/honeybadger/util/request_payload.rb
|
80
87
|
- lib/honeybadger/util/revision.rb
|
81
88
|
- lib/honeybadger/util/sanitizer.rb
|
89
|
+
- lib/honeybadger/util/sql.rb
|
82
90
|
- lib/honeybadger/util/stats.rb
|
83
91
|
- lib/honeybadger/version.rb
|
84
92
|
- lib/honeybadger/worker.rb
|