exception_handling 2.5.0 → 2.7.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/.gitignore +1 -1
- data/.jenkins/Jenkinsfile +24 -8
- data/.rspec +3 -0
- data/CHANGELOG.md +18 -0
- data/Gemfile +4 -4
- data/Gemfile.lock +67 -58
- data/Rakefile +7 -6
- data/gemfiles/rails_4.gemfile +4 -4
- data/gemfiles/rails_5.gemfile +4 -4
- data/gemfiles/rails_6.gemfile +4 -4
- data/lib/exception_handling.rb +9 -3
- data/lib/exception_handling/exception_info.rb +3 -6
- data/lib/exception_handling/log_stub_error.rb +2 -1
- data/lib/exception_handling/logging_methods.rb +27 -0
- data/lib/exception_handling/methods.rb +6 -53
- data/lib/exception_handling/testing.rb +20 -10
- data/lib/exception_handling/version.rb +1 -1
- data/{test → spec}/helpers/controller_helpers.rb +0 -0
- data/{test → spec}/helpers/exception_helpers.rb +2 -2
- data/{test → spec}/rake_test_warning_false.rb +0 -0
- data/{test/test_helper.rb → spec/spec_helper.rb} +57 -42
- data/spec/unit/exception_handling/exception_catalog_spec.rb +85 -0
- data/spec/unit/exception_handling/exception_description_spec.rb +82 -0
- data/{test/unit/exception_handling/exception_info_test.rb → spec/unit/exception_handling/exception_info_spec.rb} +118 -99
- data/{test/unit/exception_handling/honeybadger_callbacks_test.rb → spec/unit/exception_handling/honeybadger_callbacks_spec.rb} +20 -20
- data/{test/unit/exception_handling/log_error_stub_test.rb → spec/unit/exception_handling/log_error_stub_spec.rb} +38 -22
- data/spec/unit/exception_handling/logging_methods_spec.rb +38 -0
- data/{test/unit/exception_handling/mailer_test.rb → spec/unit/exception_handling/mailer_spec.rb} +17 -17
- data/spec/unit/exception_handling/methods_spec.rb +105 -0
- data/spec/unit/exception_handling/sensu_spec.rb +51 -0
- data/{test/unit/exception_handling_test.rb → spec/unit/exception_handling_spec.rb} +349 -319
- metadata +35 -31
- data/test/unit/exception_handling/exception_catalog_test.rb +0 -85
- data/test/unit/exception_handling/exception_description_test.rb +0 -82
- data/test/unit/exception_handling/methods_test.rb +0 -84
- data/test/unit/exception_handling/sensu_test.rb +0 -52
@@ -57,9 +57,8 @@ module ExceptionHandling
|
|
57
57
|
@timestamp = timestamp
|
58
58
|
@controller = controller || controller_from_context(exception_context)
|
59
59
|
@data_callback = data_callback
|
60
|
-
|
61
|
-
|
62
|
-
end
|
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 || {})
|
63
62
|
end
|
64
63
|
|
65
64
|
def data
|
@@ -273,13 +272,11 @@ module ExceptionHandling
|
|
273
272
|
data[:exception_context] = deep_clean_hash(@exception_context) if @exception_context.present?
|
274
273
|
data[:log_context] = @merged_log_context
|
275
274
|
unstringify_sections(data)
|
276
|
-
|
275
|
+
HONEYBADGER_CONTEXT_SECTIONS.each_with_object({}) do |section, context|
|
277
276
|
if data[section].present?
|
278
277
|
context[section] = data[section]
|
279
278
|
end
|
280
|
-
context
|
281
279
|
end
|
282
|
-
context_data
|
283
280
|
end
|
284
281
|
end
|
285
282
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
require 'active_support/core_ext/module/delegation.rb'
|
5
|
+
|
6
|
+
module ExceptionHandling
|
7
|
+
module LoggingMethods # included on models and controllers
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
delegate :log_error_rack, :log_warning, :log_info, :log_debug, :escalate_error, :escalate_warning, :ensure_escalation, :alert_warning, :log_error, to: ExceptionHandling
|
13
|
+
|
14
|
+
def ensure_safe(exception_context = "")
|
15
|
+
yield
|
16
|
+
rescue => ex
|
17
|
+
log_error ex, exception_context
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def ensure_alert(*args)
|
22
|
+
ExceptionHandling.ensure_alert(*args) do
|
23
|
+
yield
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,65 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'active_support/concern'
|
4
|
+
require_relative 'logging_methods'
|
4
5
|
|
5
6
|
module ExceptionHandling
|
6
7
|
module Methods # included on models and controllers
|
7
8
|
extend ActiveSupport::Concern
|
9
|
+
include ExceptionHandling::LoggingMethods
|
8
10
|
|
9
11
|
protected
|
10
12
|
|
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
|
-
|
63
13
|
def long_controller_action_timeout
|
64
14
|
if defined?(Rails) && Rails.respond_to?(:env) && Rails.env == 'test'
|
65
15
|
300
|
@@ -88,7 +38,10 @@ module ExceptionHandling
|
|
88
38
|
end
|
89
39
|
|
90
40
|
included do
|
91
|
-
around_filter
|
41
|
+
Deprecation3_0.deprecation_warning('ExceptionHandling::Methods', 'include LoggingMethods; in controllers, set your own around_filter to set logging context')
|
42
|
+
if respond_to? :around_filter
|
43
|
+
around_filter :set_current_controller
|
44
|
+
end
|
92
45
|
end
|
93
46
|
|
94
47
|
class_methods do
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
module ExceptionHandling
|
6
6
|
module Testing
|
7
|
-
class
|
7
|
+
class ControllerStubBase
|
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
|
+
self.around_filter_method = method
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
@@ -44,14 +44,6 @@ 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
|
-
|
55
47
|
def action_name
|
56
48
|
"test_action"
|
57
49
|
end
|
@@ -59,9 +51,27 @@ module ExceptionHandling
|
|
59
51
|
def complete_request_uri
|
60
52
|
"#{@request.protocol}#{@request.host}#{@request.request_uri}"
|
61
53
|
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class LoggingMethodsControllerStub < ControllerStubBase
|
57
|
+
include ExceptionHandling::LoggingMethods
|
58
|
+
|
59
|
+
def controller_name
|
60
|
+
"LoggingMethodsControllerStub"
|
61
|
+
end
|
62
|
+
end
|
62
63
|
|
64
|
+
class MethodsControllerStub < ControllerStubBase
|
63
65
|
include ExceptionHandling::Methods
|
64
66
|
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
|
65
75
|
end
|
66
76
|
end
|
67
77
|
end
|
File without changes
|
@@ -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
|
+
allow(exception_with_nil_message).to receive(:message).and_return(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
|
+
allow(ExceptionHandling).to receive(:send_exception_to_honeybadger).with(anything) { |exception_info| @sent_notifications << exception_info }
|
19
19
|
end
|
20
20
|
end
|
File without changes
|
@@ -1,17 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
require '
|
5
|
-
require '
|
6
|
-
require 'action_mailer'
|
7
|
-
require 'action_dispatch'
|
8
|
-
require 'shoulda'
|
9
|
-
require 'rr'
|
10
|
-
require 'minitest/autorun'
|
11
|
-
require "minitest/reporters"
|
12
|
-
|
13
|
-
junit_ouptut_dir = ENV["JUNIT_OUTPUT_DIR"].presence || "test/reports"
|
14
|
-
Minitest::Reporters.use!([Minitest::Reporters::DefaultReporter.new, Minitest::Reporters::JUnitReporter.new(junit_ouptut_dir)])
|
3
|
+
require 'rspec'
|
4
|
+
require 'rspec/mocks'
|
5
|
+
require 'rspec_junit_formatter'
|
15
6
|
|
16
7
|
require 'pry'
|
17
8
|
require 'honeybadger'
|
@@ -20,8 +11,6 @@ require 'contextual_logger'
|
|
20
11
|
require 'exception_handling'
|
21
12
|
require 'exception_handling/testing'
|
22
13
|
|
23
|
-
ActiveSupport::TestCase.test_order = :sorted
|
24
|
-
|
25
14
|
class LoggerStub
|
26
15
|
include ContextualLogger::LoggerMixin
|
27
16
|
attr_accessor :logged, :level
|
@@ -33,16 +22,20 @@ class LoggerStub
|
|
33
22
|
clear
|
34
23
|
end
|
35
24
|
|
25
|
+
def debug(message, log_context = {})
|
26
|
+
logged << { message: message, context: log_context, severity: 'DEBUG' }
|
27
|
+
end
|
28
|
+
|
36
29
|
def info(message, log_context = {})
|
37
|
-
logged << { message: message, context: log_context }
|
30
|
+
logged << { message: message, context: log_context, severity: 'INFO' }
|
38
31
|
end
|
39
32
|
|
40
33
|
def warn(message, log_context = {})
|
41
|
-
logged << { message: message, context: log_context }
|
34
|
+
logged << { message: message, context: log_context, severity: 'WARN' }
|
42
35
|
end
|
43
36
|
|
44
37
|
def fatal(message, log_context = {})
|
45
|
-
logged << { message: message, context: log_context }
|
38
|
+
logged << { message: message, context: log_context, severity: 'FATAL' }
|
46
39
|
end
|
47
40
|
|
48
41
|
def clear
|
@@ -83,27 +76,17 @@ end
|
|
83
76
|
|
84
77
|
ActionMailer::Base.delivery_method = :test
|
85
78
|
|
86
|
-
_ = ActiveSupport
|
87
|
-
_ = ActiveSupport::TestCase
|
88
79
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
raise "Uh-oh! constant_overrides left over: #{@@constant_overrides.inspect}"
|
95
|
-
end
|
80
|
+
module TestHelper
|
81
|
+
@constant_overrides = []
|
82
|
+
class << self
|
83
|
+
attr_accessor :constant_overrides
|
84
|
+
end
|
96
85
|
|
97
|
-
unless defined?(Rails) && defined?(Rails.env)
|
98
|
-
module ::Rails
|
99
|
-
class << self
|
100
|
-
attr_writer :env
|
101
86
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
end
|
106
|
-
end
|
87
|
+
def setup_constant_overrides
|
88
|
+
unless TestHelper.constant_overrides.nil? || TestHelper.constant_overrides.empty?
|
89
|
+
raise "Uh-oh! constant_overrides left over: #{TestHelper.constant_overrides.inspect}"
|
107
90
|
end
|
108
91
|
|
109
92
|
Time.now_override = nil
|
@@ -123,8 +106,8 @@ class ActiveSupport::TestCase
|
|
123
106
|
ExceptionHandling.sensu_prefix = ""
|
124
107
|
end
|
125
108
|
|
126
|
-
|
127
|
-
|
109
|
+
def teardown_constant_overrides
|
110
|
+
TestHelper.constant_overrides&.reverse&.each do |parent_module, k, v|
|
128
111
|
ExceptionHandling.ensure_safe "constant cleanup #{k.inspect}, #{parent_module}(#{parent_module.class})::#{v.inspect}(#{v.class})" do
|
129
112
|
silence_warnings do
|
130
113
|
if v == :never_defined
|
@@ -135,7 +118,7 @@ class ActiveSupport::TestCase
|
|
135
118
|
end
|
136
119
|
end
|
137
120
|
end
|
138
|
-
|
121
|
+
TestHelper.constant_overrides = []
|
139
122
|
end
|
140
123
|
|
141
124
|
def set_test_const(const_name, value)
|
@@ -155,7 +138,7 @@ class ActiveSupport::TestCase
|
|
155
138
|
end
|
156
139
|
end
|
157
140
|
|
158
|
-
|
141
|
+
TestHelper.constant_overrides << [final_parent_module, final_const_name, original_value]
|
159
142
|
|
160
143
|
silence_warnings { final_parent_module.const_set(final_const_name, value) }
|
161
144
|
end
|
@@ -167,15 +150,15 @@ class ActiveSupport::TestCase
|
|
167
150
|
else
|
168
151
|
original_count = 0
|
169
152
|
end
|
170
|
-
|
153
|
+
expect(ActionMailer::Base.deliveries.size - original_count).to eq(expected), "wrong number of emails#{': ' + message.to_s if message}"
|
171
154
|
end
|
172
155
|
end
|
173
156
|
|
174
157
|
def assert_equal_with_diff(arg1, arg2, msg = '')
|
175
158
|
if arg1 == arg2
|
176
|
-
|
159
|
+
expect(true).to be_truthy # To keep the assertion count accurate
|
177
160
|
else
|
178
|
-
|
161
|
+
expect(arg1).to eq(arg2), "#{msg}\n#{Diff.compare(arg1, arg2)}"
|
179
162
|
end
|
180
163
|
end
|
181
164
|
|
@@ -206,3 +189,35 @@ class Time
|
|
206
189
|
end
|
207
190
|
end
|
208
191
|
end
|
192
|
+
|
193
|
+
RSpec.configure do |config|
|
194
|
+
config.add_formatter(RspecJunitFormatter, 'spec/reports/rspec.xml')
|
195
|
+
config.include TestHelper
|
196
|
+
|
197
|
+
config.before(:each) do
|
198
|
+
setup_constant_overrides
|
199
|
+
unless defined?(Rails) && defined?(Rails.env)
|
200
|
+
module Rails
|
201
|
+
class << self
|
202
|
+
attr_writer :env
|
203
|
+
|
204
|
+
def env
|
205
|
+
@env ||= 'test'
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
config.after(:each) do
|
213
|
+
teardown_constant_overrides
|
214
|
+
end
|
215
|
+
|
216
|
+
config.mock_with :rspec do |mocks|
|
217
|
+
mocks.verify_partial_doubles = true
|
218
|
+
end
|
219
|
+
|
220
|
+
config.expect_with(:rspec, :test_unit)
|
221
|
+
|
222
|
+
RSpec::Support::ObjectFormatter.default_instance.max_formatted_output_length = 2_000
|
223
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../../spec_helper', __dir__)
|
4
|
+
|
5
|
+
module ExceptionHandling
|
6
|
+
describe ExceptionCatalog do
|
7
|
+
|
8
|
+
context "With stubbed yaml content" do
|
9
|
+
before do
|
10
|
+
filter_list = { exception1: { error: "my error message" },
|
11
|
+
exception2: { error: "some other message", session: "misc data" } }
|
12
|
+
allow(YAML).to receive(:load_file) { filter_list }
|
13
|
+
|
14
|
+
# bump modified time up to get the above filter loaded
|
15
|
+
allow(File).to receive(:mtime) { incrementing_mtime }
|
16
|
+
end
|
17
|
+
|
18
|
+
context "with loaded data" do
|
19
|
+
before do
|
20
|
+
allow(File).to receive(:mtime) { incrementing_mtime }
|
21
|
+
@exception_catalog = ExceptionCatalog.new(ExceptionHandling.filter_list_filename)
|
22
|
+
@exception_catalog.send :load_file
|
23
|
+
end
|
24
|
+
|
25
|
+
it "have loaded filters" do
|
26
|
+
expect(@exception_catalog.instance_eval("@filters").size).to eq(2)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "find messages in the catalog" do
|
30
|
+
expect(!@exception_catalog.find(error: "Scott says unlikely to ever match")).to be_truthy
|
31
|
+
end
|
32
|
+
|
33
|
+
it "find matching data" do
|
34
|
+
exception_description = @exception_catalog.find(error: "this is my error message, which should match something")
|
35
|
+
expect(exception_description).to be_truthy
|
36
|
+
expect(exception_description.filter_name).to eq(:exception1)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "write errors loading the yaml file directly to the log file" do
|
41
|
+
@exception_catalog = ExceptionCatalog.new(ExceptionHandling.filter_list_filename)
|
42
|
+
|
43
|
+
expect(ExceptionHandling).to receive(:log_error).never
|
44
|
+
expect(ExceptionHandling).to receive(:write_exception_to_log).with(anything, "ExceptionCatalog#refresh_filters: ./config/exception_filters.yml", anything)
|
45
|
+
expect(@exception_catalog).to receive(:load_file) { raise "noooooo" }
|
46
|
+
|
47
|
+
@exception_catalog.find({})
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "with live yaml content" do
|
52
|
+
before do
|
53
|
+
@filename = File.expand_path('../../../config/exception_filters.yml', __dir__)
|
54
|
+
@exception_catalog = ExceptionCatalog.new(@filename)
|
55
|
+
expect do
|
56
|
+
@exception_catalog.send :load_file
|
57
|
+
end.not_to raise_error
|
58
|
+
end
|
59
|
+
|
60
|
+
it "load the filter data" do
|
61
|
+
expect(!@exception_catalog.find(error: "Scott says unlikely to ever match")).to be_truthy
|
62
|
+
expect(!@exception_catalog.find(error: "Scott says unlikely to ever match")).to be_truthy
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "with no yaml content" do
|
67
|
+
before do
|
68
|
+
@exception_catalog = ExceptionCatalog.new(nil)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "not load filter data" do
|
72
|
+
expect(ExceptionHandling).to receive(:write_exception_to_log).with(any_args).never
|
73
|
+
@exception_catalog.find(error: "Scott says unlikely to ever match")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def incrementing_mtime
|
80
|
+
@mtime ||= Time.now
|
81
|
+
@mtime += 1.day
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|