exception_handling 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/config/exception_filters.yml +5 -0
- data/lib/exception_handling/exception_catalog.rb +40 -0
- data/lib/exception_handling/exception_description.rb +58 -0
- data/lib/exception_handling/version.rb +1 -1
- data/lib/exception_handling.rb +13 -92
- data/test/unit/exception_handling/exception_catalog_test.rb +65 -0
- data/test/unit/exception_handling/exception_description_test.rb +71 -0
- data/test/unit/exception_handling_test.rb +25 -40
- data/views/exception_handling/mailer/exception_notification.html.erb +8 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07258fd3838c1f4ad88afcc407b5a48c515cc1bb
|
4
|
+
data.tar.gz: 6bc4a796d301b5739a71b6436a9a3c348f03f2f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 866f948f04a3ac4c2abf81d221c148a6c236692242dbc318743344dc4f8bc5fb14b39ece122895438ba4c7e48305baef130a489c1b5d712399822b9ff5bad448
|
7
|
+
data.tar.gz: 82e94f9f3eb8030a852a93f341cc7b7694070267edc3773fa74aa3e7e3ac66fbe145a5be242816b20202912016836ebfef6898ea31433b02295920c6e2c35f90
|
data/Gemfile.lock
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
module ExceptionHandling
|
2
|
+
class ExceptionCatalog
|
3
|
+
|
4
|
+
def initialize(filter_path)
|
5
|
+
@filter_path = filter_path
|
6
|
+
@filters = { }
|
7
|
+
@filters_last_modified_time = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def find(exception_data)
|
11
|
+
refresh_filters
|
12
|
+
@filters.values.find { |filter| filter.match?(exception_data) }
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def refresh_filters
|
18
|
+
mtime = last_modified_time
|
19
|
+
if @filters_last_modified_time.nil? || mtime != @filters_last_modified_time
|
20
|
+
ExceptionHandling.logger.info("Reloading filter list from: #{@filter_path}. Last loaded time: #{@filters_last_modified_time}. Last modified time: #{mtime}")
|
21
|
+
load_file
|
22
|
+
end
|
23
|
+
|
24
|
+
rescue => ex # any exceptions
|
25
|
+
ExceptionHandling::log_error(ex, "ExceptionRegexes::refresh_filters: #{@filter_path}", nil, true)
|
26
|
+
end
|
27
|
+
|
28
|
+
def load_file
|
29
|
+
@filters_last_modified_time = last_modified_time # make race condition fall on the side of reloading unnecessarily next time rather than missing a set of changes
|
30
|
+
|
31
|
+
filters = YAML::load_file(@filter_path)
|
32
|
+
filter_hash_values = filters.map { |filter_name, regexes| [filter_name.to_sym, ExceptionDescription.new(filter_name.to_sym, regexes.symbolize_keys)] }
|
33
|
+
@filters = Hash[ filter_hash_values ]
|
34
|
+
end
|
35
|
+
|
36
|
+
def last_modified_time
|
37
|
+
File.mtime(@filter_path)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module ExceptionHandling
|
2
|
+
class ExceptionDescription
|
3
|
+
MATCH_SECTIONS = [:error, :request, :session, :environment, :backtrace, :event_response]
|
4
|
+
|
5
|
+
CONFIGURATION_SECTIONS = {
|
6
|
+
send_email: false, # should email be sent?
|
7
|
+
send_metric: true, # should the metric be sent.
|
8
|
+
metric_name: nil, # Will be derived from section name if not passed
|
9
|
+
notes: nil # Will be included in exception email if set, used to keep notes and relevant links
|
10
|
+
}
|
11
|
+
|
12
|
+
attr_reader :filter_name, :send_email, :send_metric, :metric_name, :notes
|
13
|
+
|
14
|
+
def initialize(filter_name, configuration)
|
15
|
+
@filter_name = filter_name
|
16
|
+
|
17
|
+
invalid_sections = configuration.except(*(CONFIGURATION_SECTIONS.keys + MATCH_SECTIONS))
|
18
|
+
invalid_sections.empty? or raise ArgumentError, "Unknown section: #{invalid_sections.keys.join(",")}"
|
19
|
+
|
20
|
+
@configuration = CONFIGURATION_SECTIONS.merge(configuration)
|
21
|
+
@send_email = @configuration[:send_email]
|
22
|
+
@send_metric = @configuration[:send_metric]
|
23
|
+
@metric_name = (@configuration[:metric_name] || @filter_name ).to_s.gsub(" ","_")
|
24
|
+
@notes = @configuration[:notes]
|
25
|
+
|
26
|
+
regex_config = @configuration.reject { |k,v| k.in?(CONFIGURATION_SECTIONS.keys) || v.blank? }
|
27
|
+
|
28
|
+
@regexes = Hash[regex_config.map { |section, regex| [section, Regexp.new(regex, 'i') ] }]
|
29
|
+
|
30
|
+
!@regexes.empty? or raise ArgumentError, "Filter #{filter_name} has all blank regexes: #{configuration.inspect}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def exception_data
|
34
|
+
{
|
35
|
+
"send_metric" => send_metric,
|
36
|
+
"metric_name" => metric_name,
|
37
|
+
"notes" => notes
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def match?(exception_data)
|
42
|
+
@regexes.all? do |section, regex|
|
43
|
+
case target = exception_data[section.to_s] || exception_data[section]
|
44
|
+
when String
|
45
|
+
regex =~ target
|
46
|
+
when Array
|
47
|
+
target.any? { |row| row =~ regex }
|
48
|
+
when Hash
|
49
|
+
target[:to_s] =~ regex
|
50
|
+
when NilClass
|
51
|
+
false
|
52
|
+
else
|
53
|
+
raise "Unexpected class #{exception_data[section].class.name}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/exception_handling.rb
CHANGED
@@ -7,6 +7,8 @@ require 'invoca/utils'
|
|
7
7
|
require "exception_handling/mailer"
|
8
8
|
require "exception_handling/methods"
|
9
9
|
require "exception_handling/log_stub_error"
|
10
|
+
require "exception_handling/exception_description"
|
11
|
+
require "exception_handling/exception_catalog"
|
10
12
|
|
11
13
|
_ = ActiveSupport::HashWithIndifferentAccess
|
12
14
|
|
@@ -351,22 +353,18 @@ EOF
|
|
351
353
|
|
352
354
|
SECTIONS.each { |section| add_to_s( data[section] ) if data[section].is_a? Hash }
|
353
355
|
|
354
|
-
|
355
|
-
|
356
|
-
end
|
357
|
-
|
358
|
-
if summarize_exception( data ) == :Summarized
|
359
|
-
return
|
360
|
-
end
|
356
|
+
exception_description = exception_catalog.find( data )
|
357
|
+
merged_data = exception_description ? ActiveSupport::HashWithIndifferentAccess.new(exception_description.exception_data.merge(data)) : data
|
361
358
|
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
359
|
+
if exception_description && !exception_description.send_email
|
360
|
+
ExceptionHandling.logger.warn( "Filtered exception using '#{exception_description.filter_name}'; not sending email to notify" )
|
361
|
+
else
|
362
|
+
if summarize_exception( merged_data ) != :Summarized
|
363
|
+
deliver(ExceptionHandling::Mailer.exception_notification(merged_data))
|
364
|
+
end
|
366
365
|
end
|
367
366
|
|
368
|
-
execute_custom_log_error_callback(
|
369
|
-
|
367
|
+
execute_custom_log_error_callback(merged_data, exc)
|
370
368
|
nil
|
371
369
|
end
|
372
370
|
|
@@ -470,8 +468,8 @@ EOF
|
|
470
468
|
end.compact ]
|
471
469
|
end
|
472
470
|
|
473
|
-
def
|
474
|
-
@
|
471
|
+
def exception_catalog
|
472
|
+
@exception_catalog ||= ExceptionCatalog.new( ExceptionHandling.filter_list_filename )
|
475
473
|
end
|
476
474
|
|
477
475
|
def clean_backtrace(exception)
|
@@ -594,81 +592,4 @@ EOF
|
|
594
592
|
result
|
595
593
|
end
|
596
594
|
end
|
597
|
-
|
598
|
-
class ExceptionFilters
|
599
|
-
class Filter
|
600
|
-
def initialize filter_name, regexes
|
601
|
-
@regexes = Hash[ regexes.map do |section, regex|
|
602
|
-
section = section.to_sym
|
603
|
-
raise "Unknown section: #{section}" unless section == :error || section.in?( ExceptionHandling::SECTIONS )
|
604
|
-
[section, (Regexp.new(regex, 'i') unless regex.blank?)]
|
605
|
-
end ]
|
606
|
-
|
607
|
-
raise "Filter #{filter_name} has all blank regexes: #{regexes.inspect}" if @regexes.all? { |section, regex| regex.nil? }
|
608
|
-
end
|
609
|
-
|
610
|
-
def match?(exception_data)
|
611
|
-
@regexes.all? do |section, regex|
|
612
|
-
regex.nil? ||
|
613
|
-
case exception_data[section.to_s]
|
614
|
-
when String
|
615
|
-
regex =~ exception_data[section]
|
616
|
-
when Array
|
617
|
-
exception_data[section].any? { |row| row =~ regex }
|
618
|
-
when Hash
|
619
|
-
exception_data[section] && exception_data[section][:to_s] =~ regex
|
620
|
-
when NilClass
|
621
|
-
false
|
622
|
-
else
|
623
|
-
raise "Unexpected class #{exception_data[section].class.name}"
|
624
|
-
end
|
625
|
-
end
|
626
|
-
end
|
627
|
-
end
|
628
|
-
|
629
|
-
def initialize( filter_path )
|
630
|
-
@filter_path = filter_path
|
631
|
-
@filters = { }
|
632
|
-
@filters_last_modified_time = nil
|
633
|
-
end
|
634
|
-
|
635
|
-
def filtered?( exception_data )
|
636
|
-
refresh_filters
|
637
|
-
|
638
|
-
@filters.any? do |name, filter|
|
639
|
-
if ( match = filter.match?( exception_data ) )
|
640
|
-
ExceptionHandling.logger.warn( "Filtered exception using '#{name}'; not sending email to notify" )
|
641
|
-
end
|
642
|
-
match
|
643
|
-
end
|
644
|
-
end
|
645
|
-
|
646
|
-
private
|
647
|
-
|
648
|
-
def refresh_filters
|
649
|
-
mtime = last_modified_time
|
650
|
-
if @filters_last_modified_time.nil? || mtime != @filters_last_modified_time
|
651
|
-
ExceptionHandling.logger.info( "Reloading filter list from: #{@filter_path}. Last loaded time: #{@filters_last_modified_time}. Last modified time: #{mtime}" )
|
652
|
-
@filters_last_modified_time = mtime # make race condition fall on the side of reloading unnecessarily next time rather than missing a set of changes
|
653
|
-
|
654
|
-
@filters = load_file
|
655
|
-
end
|
656
|
-
|
657
|
-
rescue => ex # any exceptions
|
658
|
-
ExceptionHandling::log_error( ex, "ExceptionRegexes::refresh_filters: #{@filter_path}", nil, true)
|
659
|
-
end
|
660
|
-
|
661
|
-
def load_file
|
662
|
-
# store all strings from YAML file into regex's on initial load, instead of converting to regex on every exception that is logged
|
663
|
-
filters = YAML::load_file( @filter_path )
|
664
|
-
Hash[ filters.map do |filter_name, regexes|
|
665
|
-
[filter_name, Filter.new( filter_name, regexes )]
|
666
|
-
end ]
|
667
|
-
end
|
668
|
-
|
669
|
-
def last_modified_time
|
670
|
-
File.mtime( @filter_path )
|
671
|
-
end
|
672
|
-
|
673
|
-
end
|
674
595
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
module ExceptionHandling
|
4
|
+
class ExceptionCatalogTest < ActiveSupport::TestCase
|
5
|
+
|
6
|
+
context "With stubbed yaml content" do
|
7
|
+
setup do
|
8
|
+
filter_list = { :exception1 => { error: "my error message" },
|
9
|
+
:exception2 => { error: "some other message", session: "misc data" } }
|
10
|
+
stub(YAML).load_file { filter_list }
|
11
|
+
|
12
|
+
# bump modified time up to get the above filter loaded
|
13
|
+
stub(File).mtime { incrementing_mtime }
|
14
|
+
end
|
15
|
+
|
16
|
+
context "with loaded data" do
|
17
|
+
setup do
|
18
|
+
stub(File).mtime { incrementing_mtime }
|
19
|
+
@exception_catalog = ExceptionCatalog.new( ExceptionHandling.filter_list_filename )
|
20
|
+
@exception_catalog.send :load_file
|
21
|
+
end
|
22
|
+
|
23
|
+
should "have loaded filters" do
|
24
|
+
assert_equal 2, @exception_catalog.instance_eval("@filters").size
|
25
|
+
end
|
26
|
+
|
27
|
+
should "find messages in the catalog" do
|
28
|
+
assert !@exception_catalog.find( error: "Scott says unlikely to ever match" )
|
29
|
+
end
|
30
|
+
|
31
|
+
should "find matching data" do
|
32
|
+
exception_description = @exception_catalog.find(error: "this is my error message, which should match something")
|
33
|
+
assert exception_description
|
34
|
+
assert_equal :exception1, exception_description.filter_name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
context "with live yaml content" do
|
41
|
+
setup do
|
42
|
+
@filename = File.expand_path('../../../../config/exception_filters.yml', __FILE__)
|
43
|
+
@exception_catalog = ExceptionCatalog.new( @filename )
|
44
|
+
assert_nothing_raised "Loading the exception filter should not raise" do
|
45
|
+
@exception_catalog.send :load_file
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
should "load the filter data" do
|
50
|
+
assert !@exception_catalog.find( error: "Scott says unlikely to ever match" )
|
51
|
+
assert !@exception_catalog.find( error: "Scott says unlikely to ever match" )
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def incrementing_mtime
|
60
|
+
@mtime ||= Time.now
|
61
|
+
@mtime += 1.day
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
module ExceptionHandling
|
4
|
+
class ExceptionDescriptionTest < ActiveSupport::TestCase
|
5
|
+
|
6
|
+
context "Filter" do
|
7
|
+
|
8
|
+
should "allow direct matching of strings" do
|
9
|
+
@f = ExceptionDescription.new(:filter1, :error => "my error message" )
|
10
|
+
assert @f.match?( 'error' => "my error message")
|
11
|
+
end
|
12
|
+
|
13
|
+
should "allow direct matching of strings on with symbol keys" do
|
14
|
+
@f = ExceptionDescription.new(:filter1, :error => "my error message" )
|
15
|
+
assert @f.match?( :error => "my error message")
|
16
|
+
end
|
17
|
+
|
18
|
+
should "complain when no regexps have a value" do
|
19
|
+
assert_raise(ArgumentError, "has all blank regexe") { ExceptionDescription.new(:filter1, error: nil) }
|
20
|
+
end
|
21
|
+
|
22
|
+
should "report when an invalid key is passed" do
|
23
|
+
assert_raise(ArgumentError, "Unknown section: not_a_parameter") { ExceptionDescription.new(:filter1, error: "my error message", not_a_parameter: false) }
|
24
|
+
end
|
25
|
+
|
26
|
+
should "allow send email to be specified" do
|
27
|
+
assert !ExceptionDescription.new(:filter1, error: "my error message", send_email: false ).send_email
|
28
|
+
assert ExceptionDescription.new(:filter1, error: "my error message", send_email: true ).send_email
|
29
|
+
assert !ExceptionDescription.new(:filter1, error: "my error message" ).send_email
|
30
|
+
end
|
31
|
+
|
32
|
+
should "allow send_metric to be configured" do
|
33
|
+
assert !ExceptionDescription.new(:filter1, error: "my error message", send_metric: false ).send_metric
|
34
|
+
assert ExceptionDescription.new(:filter1, error: "my error message", send_email: true ).send_metric
|
35
|
+
assert ExceptionDescription.new(:filter1, error: "my error message" ).send_metric
|
36
|
+
end
|
37
|
+
|
38
|
+
should "provide metric name" do
|
39
|
+
assert_equal "filter1", ExceptionDescription.new(:filter1, error: "my error message" ).metric_name
|
40
|
+
assert_equal "some_other_metric_name", ExceptionDescription.new(:filter1, error: "my error message", metric_name: :some_other_metric_name ).metric_name
|
41
|
+
end
|
42
|
+
|
43
|
+
should "replace spaces in metric name" do
|
44
|
+
@f = ExceptionDescription.new(:"filter has spaces", :error => "my error message" )
|
45
|
+
assert_equal "filter_has_spaces", @f.metric_name
|
46
|
+
end
|
47
|
+
|
48
|
+
should "allow notes to be recorded" do
|
49
|
+
assert_equal nil, ExceptionDescription.new(:filter1, error: "my error message" ).notes
|
50
|
+
assert_equal "a long string", ExceptionDescription.new(:filter1, error: "my error message", notes: "a long string" ).notes
|
51
|
+
end
|
52
|
+
|
53
|
+
should "not consider config options in the filter set" do
|
54
|
+
assert ExceptionDescription.new(:filter1, error: "my error message", send_email: false ).match?( :error => "my error message")
|
55
|
+
assert ExceptionDescription.new(:filter1, error: "my error message", send_metric: false ).match?( :error => "my error message")
|
56
|
+
assert ExceptionDescription.new(:filter1, error: "my error message", metric_name: "false" ).match?( :error => "my error message")
|
57
|
+
assert ExceptionDescription.new(:filter1, error: "my error message", notes: "hey" ).match?( :error => "my error message")
|
58
|
+
end
|
59
|
+
|
60
|
+
should "provide exception details" do
|
61
|
+
exception_description = ExceptionDescription.new(:filter1, error: "my error message", notes: "hey" )
|
62
|
+
|
63
|
+
expected = {"send_metric" => true, "metric_name" => "filter1", "notes" => "hey"}
|
64
|
+
|
65
|
+
assert_equal expected, exception_description.exception_data
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -48,7 +48,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
|
|
48
48
|
end
|
49
49
|
|
50
50
|
context "configuration" do
|
51
|
-
def
|
51
|
+
def append_organization_info_config(data)
|
52
52
|
begin
|
53
53
|
data[:user_details] = {}
|
54
54
|
data[:user_details][:username] = "CaryP"
|
@@ -58,7 +58,8 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
def
|
61
|
+
def log_error_callback_config(data, ex)
|
62
|
+
@callback_data = data
|
62
63
|
@fail_count += 1
|
63
64
|
end
|
64
65
|
|
@@ -71,17 +72,23 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
|
|
71
72
|
end
|
72
73
|
|
73
74
|
should "support a custom_data_hook" do
|
74
|
-
ExceptionHandling.custom_data_hook = method(:
|
75
|
+
ExceptionHandling.custom_data_hook = method(:append_organization_info_config)
|
75
76
|
ExceptionHandling.ensure_safe("mooo") { raise "Some BS" }
|
76
77
|
assert_match(/Invoca Engineering Dept./, ActionMailer::Base.deliveries[-1].body.to_s)
|
77
78
|
ExceptionHandling.custom_data_hook = nil
|
78
79
|
end
|
79
80
|
|
80
|
-
should "support a log_error hook" do
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
should "support a log_error hook and pass exception data to it" do
|
82
|
+
begin
|
83
|
+
ExceptionHandling.post_log_error_hook = method(:log_error_callback_config)
|
84
|
+
ExceptionHandling.ensure_safe("mooo") { raise "Some BS" }
|
85
|
+
assert_equal 1, @fail_count
|
86
|
+
ensure
|
87
|
+
ExceptionHandling.post_log_error_hook = nil
|
88
|
+
end
|
89
|
+
|
90
|
+
assert_equal "this is used by a test", @callback_data["notes"]
|
91
|
+
assert_match(/this is used by a test/, ActionMailer::Base.deliveries[-1].body.to_s)
|
85
92
|
end
|
86
93
|
|
87
94
|
should "support rescue exceptions from a log_error hook" do
|
@@ -90,6 +97,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
|
|
90
97
|
assert_equal 0, @fail_count
|
91
98
|
ExceptionHandling.post_log_error_hook = nil
|
92
99
|
end
|
100
|
+
|
93
101
|
end
|
94
102
|
|
95
103
|
context "Exception Handling" do
|
@@ -98,18 +106,6 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
|
|
98
106
|
ExceptionHandling.send(:clear_exception_summary)
|
99
107
|
end
|
100
108
|
|
101
|
-
context "exception filter parsing and loading" do
|
102
|
-
should "happen without an error" do
|
103
|
-
stub(File).mtime { incrementing_mtime }
|
104
|
-
exception_filters = ExceptionHandling.send( :exception_filters )
|
105
|
-
assert( exception_filters.is_a?( ExceptionHandling::ExceptionFilters ) )
|
106
|
-
assert_nothing_raised "Loading the exception filter should not raise" do
|
107
|
-
exception_filters.send :load_file
|
108
|
-
end
|
109
|
-
assert !exception_filters.filtered?( "Scott says unlikely to ever match" )
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
109
|
context "ExceptionHandling.ensure_safe" do
|
114
110
|
should "log an exception if an exception is raised." do
|
115
111
|
mock(ExceptionHandling.logger).fatal(/\(blah\):\n.*exception_handling_test\.rb/)
|
@@ -232,6 +228,11 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
|
|
232
228
|
Time.now_override = Time.parse( '1986-5-21 4:17 am UTC' )
|
233
229
|
end
|
234
230
|
|
231
|
+
def log_error_callback(data, ex)
|
232
|
+
@fail_count += 1
|
233
|
+
end
|
234
|
+
|
235
|
+
|
235
236
|
should "include the timestamp when the exception is logged" do
|
236
237
|
mock(ExceptionHandling.logger).fatal(/\(Error:517033020\) ArgumentError mooo \(blah\):\n.*exception_handling_test\.rb/)
|
237
238
|
b = ExceptionHandling.ensure_safe("mooo") { raise ArgumentError.new("blah") }
|
@@ -252,12 +253,15 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
|
|
252
253
|
assert_match(/Exception 2/, ActionMailer::Base.deliveries[-1].subject)
|
253
254
|
end
|
254
255
|
|
255
|
-
should "only send 5 of a repeated error" do
|
256
|
+
should "only send 5 of a repeated error, but call post hook for every exception" do
|
257
|
+
@fail_count = 0
|
258
|
+
ExceptionHandling.post_log_error_hook = method(:log_error_callback)
|
256
259
|
assert_emails 5 do
|
257
260
|
10.times do
|
258
261
|
ExceptionHandling.log_error(exception_1)
|
259
262
|
end
|
260
263
|
end
|
264
|
+
assert_equal 10, @fail_count
|
261
265
|
end
|
262
266
|
|
263
267
|
should "only send 5 of a repeated error but don't send summary if 6th is different" do
|
@@ -671,25 +675,6 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
|
|
671
675
|
end
|
672
676
|
end
|
673
677
|
|
674
|
-
context "Errplane" do
|
675
|
-
module ErrplaneStub
|
676
|
-
end
|
677
|
-
|
678
|
-
setup do
|
679
|
-
set_test_const('Errplane', ErrplaneStub)
|
680
|
-
end
|
681
|
-
|
682
|
-
should "forward exceptions" do
|
683
|
-
mock(Errplane).transmit(exception_1, anything)
|
684
|
-
ExceptionHandling.log_error(exception_1, "context")
|
685
|
-
end
|
686
|
-
|
687
|
-
should "not forward warnings" do
|
688
|
-
stub(Errplane).transmit.times(0)
|
689
|
-
ExceptionHandling.log_warning("warning message")
|
690
|
-
end
|
691
|
-
end
|
692
|
-
|
693
678
|
private
|
694
679
|
|
695
680
|
def incrementing_mtime
|
@@ -27,6 +27,14 @@
|
|
27
27
|
<br />
|
28
28
|
<br />
|
29
29
|
|
30
|
+
<% if @cleaned_data[:notes] %>
|
31
|
+
<b>Exception Notes:</b> (from config/exception.yml)<br />
|
32
|
+
<%= @cleaned_data[:notes].gsub("\n","<br/>\n").gsub(/ {2,}/) { |spaces| ' '*spaces.size }.html_safe %>
|
33
|
+
<br />
|
34
|
+
<br />
|
35
|
+
<% end %>
|
36
|
+
|
37
|
+
|
30
38
|
<b>User summary:</b><br />
|
31
39
|
<% if (user_details = @cleaned_data[:user_details]) && ( user_details[:user] || user_details[:organization] ) %>
|
32
40
|
User: <%= user_details[:user] %> (<%= user_details[:username] %>)<br />
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: exception_handling
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Colin Kelley
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: eventmachine
|
@@ -167,12 +167,16 @@ files:
|
|
167
167
|
- config/exception_filters.yml
|
168
168
|
- exception_handling.gemspec
|
169
169
|
- lib/exception_handling.rb
|
170
|
+
- lib/exception_handling/exception_catalog.rb
|
171
|
+
- lib/exception_handling/exception_description.rb
|
170
172
|
- lib/exception_handling/log_stub_error.rb
|
171
173
|
- lib/exception_handling/mailer.rb
|
172
174
|
- lib/exception_handling/methods.rb
|
173
175
|
- lib/exception_handling/testing.rb
|
174
176
|
- lib/exception_handling/version.rb
|
175
177
|
- test/test_helper.rb
|
178
|
+
- test/unit/exception_handling/exception_catalog_test.rb
|
179
|
+
- test/unit/exception_handling/exception_description_test.rb
|
176
180
|
- test/unit/exception_handling/log_error_stub_test.rb
|
177
181
|
- test/unit/exception_handling/mailer_test.rb
|
178
182
|
- test/unit/exception_handling/methods_test.rb
|
@@ -206,6 +210,8 @@ summary: Invoca's exception handling logger/emailer layer, based on exception_no
|
|
206
210
|
Works with Rails or EventMachine or EventMachine+Synchrony.
|
207
211
|
test_files:
|
208
212
|
- test/test_helper.rb
|
213
|
+
- test/unit/exception_handling/exception_catalog_test.rb
|
214
|
+
- test/unit/exception_handling/exception_description_test.rb
|
209
215
|
- test/unit/exception_handling/log_error_stub_test.rb
|
210
216
|
- test/unit/exception_handling/mailer_test.rb
|
211
217
|
- test/unit/exception_handling/methods_test.rb
|