exception_handling 2.2.1 → 2.3.0.pre.1

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.
Files changed (34) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +5 -0
  4. data/Gemfile +7 -6
  5. data/Gemfile.lock +26 -23
  6. data/README.md +0 -1
  7. data/Rakefile +4 -4
  8. data/config/exception_filters.yml +2 -3
  9. data/exception_handling.gemspec +12 -10
  10. data/lib/exception_handling/exception_catalog.rb +5 -4
  11. data/lib/exception_handling/exception_description.rb +28 -28
  12. data/lib/exception_handling/exception_info.rb +80 -72
  13. data/lib/exception_handling/honeybadger_callbacks.rb +41 -24
  14. data/lib/exception_handling/log_stub_error.rb +10 -8
  15. data/lib/exception_handling/mailer.rb +27 -48
  16. data/lib/exception_handling/methods.rb +15 -11
  17. data/lib/exception_handling/sensu.rb +7 -5
  18. data/lib/exception_handling/testing.rb +21 -19
  19. data/lib/exception_handling/version.rb +1 -1
  20. data/lib/exception_handling.rb +105 -200
  21. data/test/helpers/controller_helpers.rb +3 -1
  22. data/test/helpers/exception_helpers.rb +9 -0
  23. data/test/test_helper.rb +26 -21
  24. data/test/unit/exception_handling/exception_catalog_test.rb +15 -14
  25. data/test/unit/exception_handling/exception_description_test.rb +22 -31
  26. data/test/unit/exception_handling/exception_info_test.rb +76 -37
  27. data/test/unit/exception_handling/honeybadger_callbacks_test.rb +46 -9
  28. data/test/unit/exception_handling/log_error_stub_test.rb +6 -4
  29. data/test/unit/exception_handling/mailer_test.rb +8 -14
  30. data/test/unit/exception_handling/methods_test.rb +9 -6
  31. data/test/unit/exception_handling/sensu_test.rb +6 -4
  32. data/test/unit/exception_handling_test.rb +279 -364
  33. metadata +24 -24
  34. data/views/exception_handling/mailer/exception_notification.html.erb +0 -92
@@ -1,41 +1,58 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ExceptionHandling
2
4
  module HoneybadgerCallbacks
3
- def self.register_callbacks
4
- if ExceptionHandling.honeybadger?
5
- Honeybadger.local_variable_filter(&method(:local_variable_filter))
5
+ class << self
6
+ def register_callbacks
7
+ if ExceptionHandling.honeybadger_defined?
8
+ Honeybadger.local_variable_filter(&method(:local_variable_filter))
9
+ end
6
10
  end
7
- end
8
11
 
9
- private
12
+ private
10
13
 
11
- def self.local_variable_filter(symbol, object, filter_keys)
12
- case object
13
- # Honeybadger will filter these data types for us
14
- when String, Hash, Array, Set, Numeric, TrueClass, FalseClass, NilClass
15
- object
16
- else # handle other Ruby objects, intended for POROs
14
+ def inspect_object(object, filter_keys)
17
15
  inspection_output = object.inspect
16
+
18
17
  if contains_filter_key?(filter_keys, inspection_output)
19
18
  filtered_object(object)
20
19
  else
21
20
  inspection_output
22
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]>"
23
30
  end
24
- end
25
31
 
26
- def self.contains_filter_key?(filter_keys, string)
27
- filter_keys._?.any? { |key| string.include?(key) }
28
- end
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
29
45
 
30
- def self.filtered_object(object)
31
- # make the output look similar to inspect
32
- # use [FILTERED], just like honeybadger does
33
- if object.respond_to?(:to_pk)
34
- "#<#{object.class.name} @pk=#{object.to_pk}, [FILTERED]>"
35
- elsif object.respond_to?(:id)
36
- "#<#{object.class.name} @id=#{object.id}, [FILTERED]>"
37
- else
38
- "#<#{object.class.name} [FILTERED]>"
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
39
56
  end
40
57
  end
41
58
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Used by functional tests to track exceptions.
3
5
  #
@@ -22,7 +24,7 @@ module LogErrorStub
22
24
  message = "log_error expected #{match[:expected]} times with pattern: '#{pattern.is_a?(Regexp) ? pattern.source : pattern}' found #{match[:found]}"
23
25
 
24
26
  if is_mini_test?
25
- flunk(message)
27
+ flunk(message)
26
28
  else
27
29
  add_failure(message)
28
30
  end
@@ -34,8 +36,9 @@ module LogErrorStub
34
36
 
35
37
  # for overriding when testing this module
36
38
  def is_mini_test?
37
- defined?(Minitest::Test) && self.is_a?(Minitest::Test)
39
+ defined?(Minitest::Test) && is_a?(Minitest::Test)
38
40
  end
41
+
39
42
  #
40
43
  # Call this function in your functional tests - usually first line after a "should" statement
41
44
  # once called, you can then call expects_exception
@@ -60,7 +63,7 @@ module LogErrorStub
60
63
  # Did the calling code call expects_exception on this exception?
61
64
  #
62
65
  def exception_filtered?(exception_data)
63
- @exception_whitelist && @exception_whitelist.any? do |expectation|
66
+ @exception_whitelist&.any? do |expectation|
64
67
  if expectation[0] === exception_data[:error]
65
68
  expectation[1][:found] += 1
66
69
  true
@@ -74,8 +77,8 @@ module LogErrorStub
74
77
  def expects_exception(pattern, options = {})
75
78
  @exception_whitelist ||= []
76
79
  expected_count = options[:count] || 1
77
- options = {:expected => expected_count, :found => 0}
78
- if to_increment = @exception_whitelist.find {|ex| ex[0] == pattern}
80
+ options = { expected: expected_count, found: 0 }
81
+ if to_increment = @exception_whitelist.find { |ex| ex[0] == pattern }
79
82
  to_increment[1][:expected] += expected_count
80
83
  else
81
84
  @exception_whitelist << [pattern, options]
@@ -90,10 +93,9 @@ module LogErrorStub
90
93
 
91
94
  def raise_unexpected_exception(exception_data)
92
95
  raise(UnexpectedExceptionLogged,
93
- exception_data[:error] + "\n" +
96
+ exception_data[:error] + "\n" \
94
97
  "---original backtrace---\n" +
95
- exception_data[:backtrace].join("\n") + "\n" +
98
+ exception_data[:backtrace].join("\n") + "\n" \
96
99
  "------")
97
100
  end
98
-
99
101
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'action_mailer'
2
4
 
3
5
  module ExceptionHandling
4
6
  class Mailer < ActionMailer::Base
5
- default :content_type => "text/html"
7
+ default content_type: "text/html"
6
8
 
7
- self.append_view_path "#{File.dirname(__FILE__)}/../../views"
9
+ append_view_path "#{File.dirname(__FILE__)}/../../views"
8
10
 
9
11
  [:email_environment, :server_name, :sender_address, :exception_recipients, :escalation_recipients].each do |method|
10
12
  define_method method do
@@ -16,76 +18,53 @@ module ExceptionHandling
16
18
  "#{email_environment} exception: "
17
19
  end
18
20
 
19
- def self.reloadable?() false end
20
-
21
- def exception_notification( cleaned_data, first_seen_at = nil, occurrences = 0 )
22
- if cleaned_data.is_a?(Hash)
23
- cleaned_data.merge!({:occurrences => occurrences, :first_seen_at => first_seen_at}) if first_seen_at
24
- cleaned_data.merge!({:server => server_name })
21
+ class << self
22
+ def reloadable?
23
+ false
25
24
  end
26
25
 
27
- subject = "#{email_prefix}#{"[#{occurrences} SUMMARIZED]" if first_seen_at}#{cleaned_data[:error]}"[0,300]
28
- recipients = exception_recipients
29
- from = sender_address
30
- @cleaned_data = cleaned_data
31
-
32
- mail(:from => from,
33
- :to => recipients,
34
- :subject => subject)
35
- end
36
-
37
- def escalation_notification( summary, data)
38
- subject = "#{email_environment} Escalation: #{summary}"
39
- from = sender_address.gsub('xception', 'scalation')
40
- recipients = escalation_recipients rescue exception_recipients
41
-
42
- @summary = summary
43
- @server = ExceptionHandling.server_name
44
- @cleaned_data = data
45
-
46
- mail(:from => from,
47
- :to => recipients,
48
- :subject => subject)
26
+ def mailer_method_category
27
+ {
28
+ log_parser_exception_notification: :NetworkOptout
29
+ }
30
+ end
49
31
  end
50
32
 
51
- def escalate_custom(summary, data, recipients)
33
+ def escalation_notification(summary, data, custom_recipients = nil)
52
34
  subject = "#{email_environment} Escalation: #{summary}"
53
35
  from = sender_address.gsub('xception', 'scalation')
54
- recipients = recipients
36
+ recipients = begin
37
+ custom_recipients || escalation_recipients
38
+ rescue
39
+ exception_recipients
40
+ end
55
41
 
56
42
  @summary = summary
57
43
  @server = ExceptionHandling.server_name
58
44
  @cleaned_data = data
59
45
 
60
- mail(:from => from,
61
- :to => recipients,
62
- :subject => subject)
46
+ mail(from: from,
47
+ to: recipients,
48
+ subject: subject)
63
49
  end
64
50
 
65
- def log_parser_exception_notification( cleaned_data, key )
51
+ def log_parser_exception_notification(cleaned_data, key)
66
52
  if cleaned_data.is_a?(Hash)
67
53
  cleaned_data = cleaned_data.symbolize_keys
68
54
  local_subject = cleaned_data[:error]
69
55
  else
70
56
  local_subject = "#{key}: #{cleaned_data}"
71
- cleaned_data = { :error => cleaned_data.to_s }
57
+ cleaned_data = { error: cleaned_data.to_s }
72
58
  end
73
59
 
74
- @subject = "#{email_prefix}#{local_subject}"[0,300]
60
+ @subject = "#{email_prefix}#{local_subject}"[0, 300]
75
61
  @recipients = exception_recipients
76
62
  from = sender_address
77
63
  @cleaned_data = cleaned_data
78
64
 
79
- mail(:from => from,
80
- :to => @recipients,
81
- :subject => @subject)
82
- end
83
-
84
- def self.mailer_method_category
85
- {
86
- :exception_notification => :NetworkOptout,
87
- :log_parser_exception_notification => :NetworkOptout
88
- }
65
+ mail(from: from,
66
+ to: @recipients,
67
+ subject: @subject)
89
68
  end
90
69
  end
91
70
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
 
3
5
  module ExceptionHandling
@@ -20,20 +22,18 @@ module ExceptionHandling
20
22
  end
21
23
 
22
24
  def log_info(message)
23
- ExceptionHandling.logger.info( message )
25
+ ExceptionHandling.logger.info(message)
24
26
  end
25
27
 
26
28
  def log_debug(message)
27
- ExceptionHandling.logger.debug( message )
29
+ ExceptionHandling.logger.debug(message)
28
30
  end
29
31
 
30
32
  def ensure_safe(exception_context = "")
31
- begin
32
- yield
33
- rescue => ex
34
- log_error ex, exception_context
35
- nil
36
- end
33
+ yield
34
+ rescue => ex
35
+ log_error ex, exception_context
36
+ nil
37
37
  end
38
38
 
39
39
  def escalate_error(exception_or_string, email_subject)
@@ -74,9 +74,13 @@ module ExceptionHandling
74
74
  time = Benchmark.measure do
75
75
  result = yield
76
76
  end
77
- if time.real > self.long_controller_action_timeout && !['development', 'test'].include?(ExceptionHandling.email_environment)
78
- name = " in #{controller_name}::#{action_name}" rescue " "
79
- log_error( "Long controller action detected#{name} %.4fs " % time.real )
77
+ if time.real > long_controller_action_timeout && !['development', 'test'].include?(ExceptionHandling.email_environment)
78
+ name = begin
79
+ " in #{controller_name}::#{action_name}"
80
+ rescue
81
+ " "
82
+ end
83
+ log_error("Long controller action detected#{name} %.4fs " % time.real)
80
84
  end
81
85
  result
82
86
  ensure
@@ -1,17 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "socket"
2
4
 
3
5
  module ExceptionHandling
4
6
  module Sensu
5
7
  LEVELS = {
6
- warning: 1,
7
- critical: 2
8
- }
8
+ warning: 1,
9
+ critical: 2
10
+ }.freeze
9
11
 
10
12
  class << self
11
13
  def generate_event(name, message, level = :warning)
12
14
  status = LEVELS[level] or raise "Invalid alert level #{level}"
13
15
 
14
- event = {name: ExceptionHandling.sensu_prefix.to_s + name, output: message, status: status}
16
+ event = { name: ExceptionHandling.sensu_prefix.to_s + name, output: message, status: status }
15
17
 
16
18
  send_event(event)
17
19
  end
@@ -23,4 +25,4 @@ module ExceptionHandling
23
25
  end
24
26
  end
25
27
  end
26
- end
28
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  # some useful test objects
3
4
 
@@ -7,38 +8,44 @@ module ExceptionHandling
7
8
 
8
9
  class Request
9
10
  attr_accessor :parameters, :protocol, :host, :request_uri, :env, :session_options
11
+
10
12
  def initialize
11
- @parameters = {:id => "1"}
13
+ @parameters = { id: "1" }
12
14
  @protocol = 'http'
13
15
  @host = 'localhost'
14
16
  @request_uri = "/fun/testing.html?foo=bar"
15
- @env = {:HOST => "local"}
16
- @session_options = { :id => '93951506217301' }
17
+ @env = { HOST: "local" }
18
+ @session_options = { id: '93951506217301' }
17
19
  end
18
20
  end
19
21
 
20
22
  attr_accessor :request, :session
23
+
21
24
  class << self
22
25
  attr_accessor :around_filter_method
26
+
27
+ def around_filter(method)
28
+ ControllerStub.around_filter_method = method
29
+ end
23
30
  end
24
31
 
25
32
  def initialize
26
33
  @request = Request.new
27
34
  @session_id = "ZKL95"
28
35
  @session =
29
- if defined?(Username)
30
- {
31
- :login_count => 22,
32
- :username_id => Username.first.id,
33
- :user_id => User.first.id,
34
- }
35
- else
36
- { }
37
- end
36
+ if defined?(Username)
37
+ {
38
+ login_count: 22,
39
+ username_id: Username.first.id,
40
+ user_id: User.first.id,
41
+ }
42
+ else
43
+ {}
44
+ end
38
45
  end
39
46
 
40
- def simulate_around_filter( &block )
41
- set_current_controller( &block )
47
+ def simulate_around_filter(&block)
48
+ set_current_controller(&block)
42
49
  end
43
50
 
44
51
  def controller_name
@@ -49,10 +56,6 @@ module ExceptionHandling
49
56
  "test_action"
50
57
  end
51
58
 
52
- def self.around_filter( method )
53
- ControllerStub.around_filter_method = method
54
- end
55
-
56
59
  def complete_request_uri
57
60
  "#{@request.protocol}#{@request.host}#{@request.request_uri}"
58
61
  end
@@ -60,6 +63,5 @@ module ExceptionHandling
60
63
  include ExceptionHandling::Methods
61
64
  set_long_controller_action_timeout 2
62
65
  end
63
-
64
66
  end
65
67
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ExceptionHandling
4
- VERSION = '2.2.1'
4
+ VERSION = '2.3.0.pre.1'
5
5
  end