exception_handling 2.17.0.pre.tstarck.1 → 3.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +0 -3
- data/.ruby-version +1 -1
- data/Gemfile +16 -12
- data/Gemfile.lock +138 -153
- data/README.md +21 -90
- data/Rakefile +11 -8
- data/exception_handling.gemspec +10 -14
- data/lib/exception_handling/exception_info.rb +11 -15
- data/lib/exception_handling/honeybadger_callbacks.rb +59 -0
- data/lib/exception_handling/log_stub_error.rb +1 -2
- data/lib/exception_handling/methods.rb +53 -6
- data/lib/exception_handling/testing.rb +10 -20
- data/lib/exception_handling/version.rb +1 -1
- data/lib/exception_handling.rb +34 -135
- data/semaphore_ci/setup.sh +3 -0
- data/{spec → test}/helpers/exception_helpers.rb +2 -2
- data/{spec/spec_helper.rb → test/test_helper.rb} +45 -75
- data/test/unit/exception_handling/exception_catalog_test.rb +85 -0
- data/test/unit/exception_handling/exception_description_test.rb +82 -0
- data/{spec/unit/exception_handling/exception_info_spec.rb → test/unit/exception_handling/exception_info_test.rb} +114 -170
- data/test/unit/exception_handling/honeybadger_callbacks_test.rb +122 -0
- data/{spec/unit/exception_handling/log_error_stub_spec.rb → test/unit/exception_handling/log_error_stub_test.rb} +22 -38
- data/{spec/unit/exception_handling/mailer_spec.rb → test/unit/exception_handling/mailer_test.rb} +18 -17
- data/test/unit/exception_handling/methods_test.rb +84 -0
- data/test/unit/exception_handling/sensu_test.rb +52 -0
- data/test/unit/exception_handling_test.rb +1109 -0
- metadata +59 -99
- data/.github/CODEOWNERS +0 -1
- data/.github/workflows/pipeline.yml +0 -36
- data/.rspec +0 -3
- data/.tool-versions +0 -1
- data/Appraisals +0 -19
- data/CHANGELOG.md +0 -149
- data/gemfiles/rails_5.gemfile +0 -18
- data/gemfiles/rails_6.gemfile +0 -18
- data/gemfiles/rails_7.gemfile +0 -18
- data/lib/exception_handling/escalate_callback.rb +0 -19
- data/lib/exception_handling/logging_methods.rb +0 -27
- data/spec/rake_test_warning_false.rb +0 -20
- data/spec/unit/exception_handling/escalate_callback_spec.rb +0 -81
- data/spec/unit/exception_handling/exception_catalog_spec.rb +0 -85
- data/spec/unit/exception_handling/exception_description_spec.rb +0 -82
- data/spec/unit/exception_handling/logging_methods_spec.rb +0 -38
- data/spec/unit/exception_handling/methods_spec.rb +0 -105
- data/spec/unit/exception_handling/sensu_spec.rb +0 -51
- data/spec/unit/exception_handling_spec.rb +0 -1465
- /data/{spec → test}/helpers/controller_helpers.rb +0 -0
@@ -3,7 +3,7 @@
|
|
3
3
|
module ExceptionHandling
|
4
4
|
class ExceptionInfo
|
5
5
|
|
6
|
-
|
6
|
+
ENVIRONMENT_WHITELIST = [
|
7
7
|
/^HTTP_/,
|
8
8
|
/^QUERY_/,
|
9
9
|
/^REQUEST_/,
|
@@ -46,20 +46,16 @@ module ExceptionHandling
|
|
46
46
|
EOS
|
47
47
|
|
48
48
|
SECTIONS = [:request, :session, :environment, :backtrace, :event_response].freeze
|
49
|
-
HONEYBADGER_CONTEXT_SECTIONS = [:timestamp, :error_class, :exception_context, :server, :scm_revision, :notes,
|
50
|
-
:user_details, :request, :session, :environment, :backtrace, :event_response, :log_context].freeze
|
49
|
+
HONEYBADGER_CONTEXT_SECTIONS = [:timestamp, :error_class, :exception_context, :server, :scm_revision, :notes, :user_details, :request, :session, :environment, :backtrace, :event_response].freeze
|
51
50
|
|
52
|
-
attr_reader :exception, :controller, :exception_context, :timestamp
|
51
|
+
attr_reader :exception, :controller, :exception_context, :timestamp
|
53
52
|
|
54
|
-
def initialize(exception, exception_context, timestamp, controller
|
53
|
+
def initialize(exception, exception_context, timestamp, controller = nil, data_callback = nil)
|
55
54
|
@exception = exception
|
56
55
|
@exception_context = exception_context
|
57
56
|
@timestamp = timestamp
|
58
57
|
@controller = controller || controller_from_context(exception_context)
|
59
58
|
@data_callback = data_callback
|
60
|
-
# merge into the surrounding context just like ContextualLogger does when logging
|
61
|
-
@merged_log_context = ExceptionHandling.logger.current_context_for_thread.deep_merge(log_context || {})
|
62
|
-
@honeybadger_tags = Array(@merged_log_context[:honeybadger_tags] || [])
|
63
59
|
end
|
64
60
|
|
65
61
|
def data
|
@@ -83,10 +79,9 @@ module ExceptionHandling
|
|
83
79
|
end
|
84
80
|
|
85
81
|
def controller_name
|
86
|
-
@controller_name ||=
|
87
|
-
|
88
|
-
|
89
|
-
).to_s
|
82
|
+
@controller_name ||= if @controller
|
83
|
+
@controller.request.parameters.with_indifferent_access[:controller]
|
84
|
+
end.to_s
|
90
85
|
end
|
91
86
|
|
92
87
|
private
|
@@ -181,7 +176,7 @@ module ExceptionHandling
|
|
181
176
|
|
182
177
|
def clean_environment(env)
|
183
178
|
Hash[ env.map do |k, v|
|
184
|
-
[k, v] if !"#{k}: #{v}".in?(ENVIRONMENT_OMIT) &&
|
179
|
+
[k, v] if !"#{k}: #{v}".in?(ENVIRONMENT_OMIT) && ENVIRONMENT_WHITELIST.any? { |regex| k =~ regex }
|
185
180
|
end.compact ]
|
186
181
|
end
|
187
182
|
|
@@ -272,13 +267,14 @@ module ExceptionHandling
|
|
272
267
|
data = enhanced_data.dup
|
273
268
|
data[:server] = ExceptionHandling.server_name
|
274
269
|
data[:exception_context] = deep_clean_hash(@exception_context) if @exception_context.present?
|
275
|
-
data[:log_context] = @merged_log_context
|
276
270
|
unstringify_sections(data)
|
277
|
-
HONEYBADGER_CONTEXT_SECTIONS.
|
271
|
+
context_data = HONEYBADGER_CONTEXT_SECTIONS.reduce({}) do |context, section|
|
278
272
|
if data[section].present?
|
279
273
|
context[section] = data[section]
|
280
274
|
end
|
275
|
+
context
|
281
276
|
end
|
277
|
+
context_data
|
282
278
|
end
|
283
279
|
end
|
284
280
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ExceptionHandling
|
4
|
+
module HoneybadgerCallbacks
|
5
|
+
class << self
|
6
|
+
def register_callbacks
|
7
|
+
if ExceptionHandling.honeybadger_defined?
|
8
|
+
Honeybadger.local_variable_filter(&method(:local_variable_filter))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def inspect_object(object, filter_keys)
|
15
|
+
inspection_output = object.inspect
|
16
|
+
|
17
|
+
if contains_filter_key?(filter_keys, inspection_output)
|
18
|
+
filtered_object(object)
|
19
|
+
else
|
20
|
+
inspection_output
|
21
|
+
end
|
22
|
+
rescue => ex
|
23
|
+
details = if object.respond_to?(:to_pk)
|
24
|
+
" @pk=#{object.to_pk}"
|
25
|
+
elsif object.respond_to?(:id)
|
26
|
+
" @id=#{object.id}"
|
27
|
+
end
|
28
|
+
|
29
|
+
"#<#{object.class.name}#{details} [error '#{ex.class.name}: #{ex.message}' while calling #inspect]>"
|
30
|
+
end
|
31
|
+
|
32
|
+
def local_variable_filter(_symbol, object, filter_keys)
|
33
|
+
case object
|
34
|
+
# Honeybadger will filter these data types for us
|
35
|
+
when String, Hash, Array, Set, Numeric, TrueClass, FalseClass, NilClass
|
36
|
+
object
|
37
|
+
else # handle other Ruby objects, intended for POROs
|
38
|
+
inspect_object(object, filter_keys)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def contains_filter_key?(filter_keys, string)
|
43
|
+
filter_keys._?.any? { |key| string.include?(key) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def filtered_object(object)
|
47
|
+
# make the output look similar to inspect
|
48
|
+
# use [FILTERED], just like honeybadger does
|
49
|
+
if object.respond_to?(:to_pk)
|
50
|
+
"#<#{object.class.name} @pk=#{object.to_pk}, [FILTERED]>"
|
51
|
+
elsif object.respond_to?(:id)
|
52
|
+
"#<#{object.class.name} @id=#{object.id}, [FILTERED]>"
|
53
|
+
else
|
54
|
+
"#<#{object.class.name} [FILTERED]>"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -1,15 +1,65 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'active_support/concern'
|
4
|
-
require_relative 'logging_methods'
|
5
4
|
|
6
5
|
module ExceptionHandling
|
7
6
|
module Methods # included on models and controllers
|
8
7
|
extend ActiveSupport::Concern
|
9
|
-
include ExceptionHandling::LoggingMethods
|
10
8
|
|
11
9
|
protected
|
12
10
|
|
11
|
+
def log_error(exception_or_string, exception_context = '')
|
12
|
+
controller = self if respond_to?(:request) && respond_to?(:session)
|
13
|
+
ExceptionHandling.log_error(exception_or_string, exception_context, controller)
|
14
|
+
end
|
15
|
+
|
16
|
+
def log_error_rack(exception_or_string, exception_context = '', rack_filter = '')
|
17
|
+
ExceptionHandling.log_error_rack(exception_or_string, exception_context, rack_filter)
|
18
|
+
end
|
19
|
+
|
20
|
+
def log_warning(message)
|
21
|
+
ExceptionHandling.log_warning(message)
|
22
|
+
end
|
23
|
+
|
24
|
+
def log_info(message)
|
25
|
+
ExceptionHandling.logger.info(message)
|
26
|
+
end
|
27
|
+
|
28
|
+
def log_debug(message)
|
29
|
+
ExceptionHandling.logger.debug(message)
|
30
|
+
end
|
31
|
+
|
32
|
+
def ensure_safe(exception_context = "")
|
33
|
+
yield
|
34
|
+
rescue => ex
|
35
|
+
log_error ex, exception_context
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def escalate_error(exception_or_string, email_subject)
|
40
|
+
ExceptionHandling.escalate_error(exception_or_string, email_subject)
|
41
|
+
end
|
42
|
+
|
43
|
+
def escalate_warning(message, email_subject)
|
44
|
+
ExceptionHandling.escalate_warning(message, email_subject)
|
45
|
+
end
|
46
|
+
|
47
|
+
def ensure_escalation(*args)
|
48
|
+
ExceptionHandling.ensure_escalation(*args) do
|
49
|
+
yield
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def alert_warning(*args)
|
54
|
+
ExceptionHandling.alert_warning(*args)
|
55
|
+
end
|
56
|
+
|
57
|
+
def ensure_alert(*args)
|
58
|
+
ExceptionHandling.ensure_alert(*args) do
|
59
|
+
yield
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
13
63
|
def long_controller_action_timeout
|
14
64
|
if defined?(Rails) && Rails.respond_to?(:env) && Rails.env == 'test'
|
15
65
|
300
|
@@ -38,10 +88,7 @@ module ExceptionHandling
|
|
38
88
|
end
|
39
89
|
|
40
90
|
included do
|
41
|
-
|
42
|
-
if respond_to? :around_filter
|
43
|
-
around_filter :set_current_controller
|
44
|
-
end
|
91
|
+
around_filter :set_current_controller if respond_to? :around_filter
|
45
92
|
end
|
46
93
|
|
47
94
|
class_methods do
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
module ExceptionHandling
|
6
6
|
module Testing
|
7
|
-
class
|
7
|
+
class ControllerStub
|
8
8
|
|
9
9
|
class Request
|
10
10
|
attr_accessor :parameters, :protocol, :host, :request_uri, :env, :session_options
|
@@ -25,7 +25,7 @@ module ExceptionHandling
|
|
25
25
|
attr_accessor :around_filter_method
|
26
26
|
|
27
27
|
def around_filter(method)
|
28
|
-
|
28
|
+
ControllerStub.around_filter_method = method
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
@@ -44,6 +44,14 @@ module ExceptionHandling
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
def simulate_around_filter(&block)
|
48
|
+
set_current_controller(&block)
|
49
|
+
end
|
50
|
+
|
51
|
+
def controller_name
|
52
|
+
"ControllerStub"
|
53
|
+
end
|
54
|
+
|
47
55
|
def action_name
|
48
56
|
"test_action"
|
49
57
|
end
|
@@ -51,27 +59,9 @@ module ExceptionHandling
|
|
51
59
|
def complete_request_uri
|
52
60
|
"#{@request.protocol}#{@request.host}#{@request.request_uri}"
|
53
61
|
end
|
54
|
-
end
|
55
|
-
|
56
|
-
class LoggingMethodsControllerStub < ControllerStubBase
|
57
|
-
include ExceptionHandling::LoggingMethods
|
58
|
-
|
59
|
-
def controller_name
|
60
|
-
"LoggingMethodsControllerStub"
|
61
|
-
end
|
62
|
-
end
|
63
62
|
|
64
|
-
class MethodsControllerStub < ControllerStubBase
|
65
63
|
include ExceptionHandling::Methods
|
66
64
|
set_long_controller_action_timeout 2
|
67
|
-
|
68
|
-
def simulate_around_filter(&block)
|
69
|
-
set_current_controller(&block)
|
70
|
-
end
|
71
|
-
|
72
|
-
def controller_name
|
73
|
-
"MethodsControllerStub"
|
74
|
-
end
|
75
65
|
end
|
76
66
|
end
|
77
67
|
end
|
data/lib/exception_handling.rb
CHANGED
@@ -1,21 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'digest'
|
4
3
|
require 'timeout'
|
5
4
|
require 'active_support'
|
6
|
-
require 'active_support/core_ext'
|
5
|
+
require 'active_support/core_ext/hash'
|
7
6
|
require 'contextual_logger'
|
8
7
|
|
9
8
|
require 'invoca/utils'
|
10
9
|
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
10
|
+
require "exception_handling/mailer"
|
11
|
+
require "exception_handling/sensu"
|
12
|
+
require "exception_handling/methods"
|
13
|
+
require "exception_handling/log_stub_error"
|
14
|
+
require "exception_handling/exception_description"
|
15
|
+
require "exception_handling/exception_catalog"
|
16
|
+
require "exception_handling/exception_info"
|
17
|
+
require "exception_handling/honeybadger_callbacks.rb"
|
19
18
|
|
20
19
|
_ = ActiveSupport::HashWithIndifferentAccess
|
21
20
|
|
@@ -30,8 +29,6 @@ module ExceptionHandling # never included
|
|
30
29
|
AUTHENTICATION_HEADERS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION', 'REDIRECT_X_HTTP_AUTHORIZATION'].freeze
|
31
30
|
HONEYBADGER_STATUSES = [:success, :failure, :skipped].freeze
|
32
31
|
|
33
|
-
Deprecation3_0 = ActiveSupport::Deprecation.new('3.0', 'exception_handling')
|
34
|
-
|
35
32
|
class << self
|
36
33
|
|
37
34
|
#
|
@@ -53,28 +50,15 @@ module ExceptionHandling # never included
|
|
53
50
|
@exception_recipients or raise ArgumentError, "You must assign a value to #{name}.exception_recipients"
|
54
51
|
end
|
55
52
|
|
56
|
-
def configured?
|
57
|
-
!@logger.nil?
|
58
|
-
end
|
59
|
-
|
60
53
|
def logger
|
61
54
|
@logger or raise ArgumentError, "You must assign a value to #{name}.logger"
|
62
55
|
end
|
63
56
|
|
64
57
|
def logger=(logger)
|
65
|
-
@logger =
|
66
|
-
logger
|
67
|
-
else
|
68
|
-
Deprecation3_0.deprecation_warning('implicit extend with ContextualLogger::LoggerMixin', 'extend your logger instance or include into your logger class first')
|
69
|
-
logger.extend(ContextualLogger::LoggerMixin)
|
70
|
-
end
|
71
|
-
EscalateCallback.register_if_configured!
|
58
|
+
@logger = logger.is_a?(ContextualLogger) ? logger : ContextualLogger.new(logger)
|
72
59
|
end
|
73
60
|
|
74
|
-
def default_metric_name(exception_data, exception, treat_like_warning
|
75
|
-
include_prefix and Deprecation3_0.deprecation_warning("the 'expection_handling.' prefix in ExceptionHandling::default_metric_name",
|
76
|
-
"do not rely on metric names including the 'exception_handling.' prefix.")
|
77
|
-
|
61
|
+
def default_metric_name(exception_data, exception, treat_like_warning)
|
78
62
|
metric_name = if exception_data['metric_name']
|
79
63
|
exception_data['metric_name']
|
80
64
|
elsif exception.is_a?(ExceptionHandling::Warning)
|
@@ -86,7 +70,7 @@ module ExceptionHandling # never included
|
|
86
70
|
"exception"
|
87
71
|
end
|
88
72
|
|
89
|
-
"
|
73
|
+
"exception_handling.#{metric_name}"
|
90
74
|
end
|
91
75
|
|
92
76
|
def default_honeybadger_metric_name(honeybadger_status)
|
@@ -110,12 +94,10 @@ module ExceptionHandling # never included
|
|
110
94
|
attr_accessor :sensu_host
|
111
95
|
attr_accessor :sensu_port
|
112
96
|
attr_accessor :sensu_prefix
|
113
|
-
attr_reader :honeybadger_log_context_tags
|
114
97
|
|
115
98
|
attr_reader :filter_list_filename
|
116
99
|
attr_reader :eventmachine_safe
|
117
100
|
attr_reader :eventmachine_synchrony
|
118
|
-
attr_reader :honeybadger_auto_tagger
|
119
101
|
|
120
102
|
@filter_list_filename = "./config/exception_filters.yml"
|
121
103
|
@email_environment = ""
|
@@ -156,30 +138,6 @@ module ExceptionHandling # never included
|
|
156
138
|
@exception_catalog ||= ExceptionCatalog.new(@filter_list_filename)
|
157
139
|
end
|
158
140
|
|
159
|
-
# rubocop:disable Style/TrivialAccessors
|
160
|
-
# @param value [Proc|nil] Proc that accepts 1 parameter that will be the exception object or nil to disable the auto-tagger.
|
161
|
-
# The proc is always expected to return an array of strings. The array can be empty.
|
162
|
-
def honeybadger_auto_tagger=(value)
|
163
|
-
@honeybadger_auto_tagger = value
|
164
|
-
end
|
165
|
-
# rubocop:enable Style/TrivialAccessors
|
166
|
-
|
167
|
-
# @param tag_name [String]
|
168
|
-
# @param path [Array]
|
169
|
-
def add_honeybadger_tag_from_log_context(tag_name, path:)
|
170
|
-
tag_name.is_a?(String) or raise ArgumentError, "tag_name must be a String, #{tag_name.inspect}"
|
171
|
-
path.is_a?(Array) or raise ArgumentError, "path must be an Array, #{path.inspect}"
|
172
|
-
@honeybadger_log_context_tags ||= {}
|
173
|
-
if @honeybadger_log_context_tags.key?(tag_name)
|
174
|
-
log_warning("Overwriting existing tag path for '#{tag_name}' from #{@honeybadger_log_context_tags[tag_name]} to #{path}")
|
175
|
-
end
|
176
|
-
@honeybadger_log_context_tags[tag_name] = path
|
177
|
-
end
|
178
|
-
|
179
|
-
def clear_honeybadger_tags_from_log_context
|
180
|
-
@honeybadger_log_context_tags = nil
|
181
|
-
end
|
182
|
-
|
183
141
|
#
|
184
142
|
# internal settings (don't set directly)
|
185
143
|
#
|
@@ -219,18 +177,13 @@ module ExceptionHandling # never included
|
|
219
177
|
# Called directly by our code, usually from rescue blocks.
|
220
178
|
# Writes to log file and may send to honeybadger
|
221
179
|
#
|
222
|
-
# TODO: the **log_context means we can never have context named treat_like_warning. In general, keyword args will be conflated with log_context.
|
223
|
-
# Ideally we'd separate to log_context from the other keywords so they don't interfere in any way. Or have no keyword args.
|
224
|
-
#
|
225
180
|
# Functional Test Operation:
|
226
181
|
# Calls into handle_stub_log_error and returns. no log file. no honeybadger
|
227
182
|
#
|
228
|
-
def log_error(exception_or_string, exception_context = '',
|
183
|
+
def log_error(exception_or_string, exception_context = '', treat_like_warning: false, **log_context, &data_callback)
|
229
184
|
ex = make_exception(exception_or_string)
|
230
185
|
timestamp = set_log_error_timestamp
|
231
|
-
exception_info = ExceptionInfo.new(ex, exception_context, timestamp,
|
232
|
-
controller: controller || current_controller, data_callback: data_callback,
|
233
|
-
log_context: log_context)
|
186
|
+
exception_info = ExceptionInfo.new(ex, exception_context, timestamp, current_controller, data_callback)
|
234
187
|
|
235
188
|
if stub_handler
|
236
189
|
stub_handler.handle_stub_log_error(exception_info.data)
|
@@ -257,13 +210,7 @@ module ExceptionHandling # never included
|
|
257
210
|
#
|
258
211
|
def write_exception_to_log(ex, exception_context, timestamp, log_context = {})
|
259
212
|
ActiveSupport::Deprecation.silence do
|
260
|
-
|
261
|
-
|
262
|
-
if ex.is_a?(Warning)
|
263
|
-
ExceptionHandling.logger.warn("\nExceptionHandlingWarning (Warning:#{timestamp}) #{log_message}", **log_context)
|
264
|
-
else
|
265
|
-
ExceptionHandling.logger.fatal("\nExceptionHandlingError (Error:#{timestamp}) #{log_message}", **log_context)
|
266
|
-
end
|
213
|
+
ExceptionHandling.logger.fatal("\nExceptionHandlingError (Error:#{timestamp}) #{ex.class} #{exception_context} (#{encode_utf8(ex.message.to_s)}):\n " + clean_backtrace(ex).join("\n ") + "\n\n", log_context)
|
267
214
|
end
|
268
215
|
end
|
269
216
|
|
@@ -296,16 +243,11 @@ module ExceptionHandling # never included
|
|
296
243
|
def send_exception_to_honeybadger(exception_info)
|
297
244
|
exception = exception_info.exception
|
298
245
|
exception_description = exception_info.exception_description
|
299
|
-
|
300
|
-
# Note: Both commas and spaces are treated as delimiters for the :tags string. Space-delimiters are not officially documented.
|
301
|
-
# https://github.com/honeybadger-io/honeybadger-ruby/pull/422
|
302
|
-
tags = tags_for_honeybadger(exception_info).join(' ')
|
303
246
|
response = Honeybadger.notify(error_class: exception_description ? exception_description.filter_name : exception.class.name,
|
304
247
|
error_message: exception.message.to_s,
|
305
248
|
exception: exception,
|
306
249
|
context: exception_info.honeybadger_context_data,
|
307
|
-
controller: exception_info.controller_name
|
308
|
-
tags: tags)
|
250
|
+
controller: exception_info.controller_name)
|
309
251
|
response ? :success : :failure
|
310
252
|
rescue Exception => ex
|
311
253
|
warn("ExceptionHandling.send_exception_to_honeybadger rescued exception while logging #{exception_info.exception_context}:\n#{exception.class}: #{exception.message}:\n#{ex.class}: #{ex.message}\n#{ex.backtrace.join("\n")}")
|
@@ -323,41 +265,38 @@ module ExceptionHandling # never included
|
|
323
265
|
#
|
324
266
|
# Expects passed in hash to only include keys which be directly set on the Honeybadger config
|
325
267
|
#
|
326
|
-
def enable_honeybadger(
|
268
|
+
def enable_honeybadger(config = {})
|
327
269
|
Bundler.require(:honeybadger)
|
270
|
+
HoneybadgerCallbacks.register_callbacks
|
328
271
|
Honeybadger.configure do |config_klass|
|
329
272
|
config.each do |k, v|
|
330
|
-
|
331
|
-
config_klass.send(k, v)
|
332
|
-
else
|
333
|
-
config_klass.send(:"#{k}=", v)
|
334
|
-
end
|
273
|
+
config_klass.send(:"#{k}=", v)
|
335
274
|
end
|
336
275
|
end
|
337
276
|
end
|
338
277
|
|
339
|
-
def log_warning(message,
|
278
|
+
def log_warning(message, log_context = {})
|
340
279
|
warning = Warning.new(message)
|
341
280
|
warning.set_backtrace([])
|
342
281
|
log_error(warning, **log_context)
|
343
282
|
end
|
344
283
|
|
345
|
-
def log_info(message,
|
346
|
-
ExceptionHandling.logger.info(message,
|
284
|
+
def log_info(message, log_context = {})
|
285
|
+
ExceptionHandling.logger.info(message, log_context)
|
347
286
|
end
|
348
287
|
|
349
|
-
def log_debug(message,
|
350
|
-
ExceptionHandling.logger.debug(message,
|
288
|
+
def log_debug(message, log_context = {})
|
289
|
+
ExceptionHandling.logger.debug(message, log_context)
|
351
290
|
end
|
352
291
|
|
353
|
-
def ensure_safe(exception_context = "",
|
292
|
+
def ensure_safe(exception_context = "", log_context = {})
|
354
293
|
yield
|
355
294
|
rescue => ex
|
356
295
|
log_error(ex, exception_context, **log_context)
|
357
296
|
nil
|
358
297
|
end
|
359
298
|
|
360
|
-
def ensure_completely_safe(exception_context = "",
|
299
|
+
def ensure_completely_safe(exception_context = "", log_context = {})
|
361
300
|
yield
|
362
301
|
rescue SystemExit, SystemStackError, NoMemoryError, SecurityError, SignalException
|
363
302
|
raise
|
@@ -372,29 +311,26 @@ module ExceptionHandling # never included
|
|
372
311
|
escalate(email_subject, ex, last_exception_timestamp, production_support_recipients)
|
373
312
|
end
|
374
313
|
|
375
|
-
def escalate_error(exception_or_string, email_subject, custom_recipients = nil,
|
314
|
+
def escalate_error(exception_or_string, email_subject, custom_recipients = nil, log_context = {})
|
376
315
|
ex = make_exception(exception_or_string)
|
377
316
|
log_error(ex, **log_context)
|
378
317
|
escalate(email_subject, ex, last_exception_timestamp, custom_recipients)
|
379
318
|
end
|
380
319
|
|
381
|
-
def escalate_warning(message, email_subject, custom_recipients = nil,
|
320
|
+
def escalate_warning(message, email_subject, custom_recipients = nil, log_context = {})
|
382
321
|
ex = Warning.new(message)
|
383
322
|
log_error(ex, **log_context)
|
384
323
|
escalate(email_subject, ex, last_exception_timestamp, custom_recipients)
|
385
324
|
end
|
386
325
|
|
387
|
-
def ensure_escalation(email_subject, custom_recipients = nil,
|
326
|
+
def ensure_escalation(email_subject, custom_recipients = nil, log_context = {})
|
388
327
|
yield
|
389
328
|
rescue => ex
|
390
|
-
escalate_error(ex, email_subject, custom_recipients,
|
329
|
+
escalate_error(ex, email_subject, custom_recipients, log_context)
|
391
330
|
nil
|
392
331
|
end
|
393
332
|
|
394
|
-
|
395
|
-
deprecator: ActiveSupport::Deprecation.new('3.0', 'ExceptionHandling')
|
396
|
-
|
397
|
-
def alert_warning(exception_or_string, alert_name, exception_context, **log_context)
|
333
|
+
def alert_warning(exception_or_string, alert_name, exception_context, log_context)
|
398
334
|
ex = make_exception(exception_or_string)
|
399
335
|
log_error(ex, exception_context, **log_context)
|
400
336
|
begin
|
@@ -404,10 +340,10 @@ module ExceptionHandling # never included
|
|
404
340
|
end
|
405
341
|
end
|
406
342
|
|
407
|
-
def ensure_alert(alert_name, exception_context,
|
343
|
+
def ensure_alert(alert_name, exception_context, log_context = {})
|
408
344
|
yield
|
409
345
|
rescue => ex
|
410
|
-
alert_warning(ex, alert_name, exception_context,
|
346
|
+
alert_warning(ex, alert_name, exception_context, log_context)
|
411
347
|
nil
|
412
348
|
end
|
413
349
|
|
@@ -424,7 +360,7 @@ module ExceptionHandling # never included
|
|
424
360
|
result
|
425
361
|
end
|
426
362
|
|
427
|
-
def log_periodically(exception_key, interval, message,
|
363
|
+
def log_periodically(exception_key, interval, message, log_context = {})
|
428
364
|
self.periodic_exception_intervals ||= {}
|
429
365
|
last_logged = self.periodic_exception_intervals[exception_key]
|
430
366
|
if !last_logged || ((last_logged + interval) < Time.now)
|
@@ -461,41 +397,6 @@ module ExceptionHandling # never included
|
|
461
397
|
|
462
398
|
private
|
463
399
|
|
464
|
-
# @param exception_info [ExceptionInfo]
|
465
|
-
#
|
466
|
-
# @return [Array<String>]
|
467
|
-
def tags_for_honeybadger(exception_info)
|
468
|
-
(
|
469
|
-
honeybadger_auto_tags(exception_info.exception) +
|
470
|
-
exception_info.honeybadger_tags +
|
471
|
-
honeybadger_tags_from_log_context(exception_info.honeybadger_context_data)
|
472
|
-
).uniq
|
473
|
-
end
|
474
|
-
|
475
|
-
# @param exception [Exception]
|
476
|
-
#
|
477
|
-
# @return [Array<String>]
|
478
|
-
def honeybadger_auto_tags(exception)
|
479
|
-
@honeybadger_auto_tagger&.call(exception) || []
|
480
|
-
rescue => ex
|
481
|
-
traces = ex.backtrace.join("\n")
|
482
|
-
message = "Unable to execute honeybadger_auto_tags callback. #{ExceptionHandling.encode_utf8(ex.message.to_s)} #{traces}\n"
|
483
|
-
ExceptionHandling.log_info(message)
|
484
|
-
[]
|
485
|
-
end
|
486
|
-
|
487
|
-
def honeybadger_tags_from_log_context(honeybadger_context_data)
|
488
|
-
if @honeybadger_log_context_tags
|
489
|
-
@honeybadger_log_context_tags.map do |tag_name, tag_path|
|
490
|
-
if (value_from_log_context = honeybadger_context_data.dig(:log_context, *tag_path))
|
491
|
-
"#{tag_name}:#{value_from_log_context}"
|
492
|
-
end
|
493
|
-
end.compact
|
494
|
-
else
|
495
|
-
[]
|
496
|
-
end
|
497
|
-
end
|
498
|
-
|
499
400
|
def execute_custom_log_error_callback(exception_data, exception, treat_like_warning, external_notification_results)
|
500
401
|
if ExceptionHandling.post_log_error_hook
|
501
402
|
honeybadger_status = external_notification_results[:honeybadger_status] || :skipped
|
@@ -562,6 +463,4 @@ module ExceptionHandling # never included
|
|
562
463
|
end
|
563
464
|
end
|
564
465
|
end
|
565
|
-
|
566
|
-
EscalateCallback.register_if_configured!
|
567
466
|
end
|
@@ -7,7 +7,7 @@ module ExceptionHelpers
|
|
7
7
|
|
8
8
|
def exception_with_nil_message
|
9
9
|
exception_with_nil_message = RuntimeError.new(nil)
|
10
|
-
|
10
|
+
stub(exception_with_nil_message).message { nil }
|
11
11
|
exception_with_nil_message
|
12
12
|
end
|
13
13
|
|
@@ -15,6 +15,6 @@ module ExceptionHelpers
|
|
15
15
|
|
16
16
|
def capture_notifications
|
17
17
|
@sent_notifications = []
|
18
|
-
|
18
|
+
stub(ExceptionHandling).send_exception_to_honeybadger(anything) { |exception_info| @sent_notifications << exception_info }
|
19
19
|
end
|
20
20
|
end
|