honeybadger 4.3.1 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +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
|