exception_handling 2.17.0.pre.tstarck.1 → 3.0.0.pre.2

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.
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'action_mailer'
4
-
5
- module ExceptionHandling
6
- class Mailer < ActionMailer::Base
7
- default content_type: "text/html"
8
-
9
- append_view_path "#{File.dirname(__FILE__)}/../../views"
10
-
11
- [:email_environment, :server_name, :sender_address, :exception_recipients, :escalation_recipients].each do |method|
12
- define_method method do
13
- ExceptionHandling.send(method) or raise "No #{method} set!"
14
- end
15
- end
16
-
17
- def email_prefix
18
- "#{email_environment} exception: "
19
- end
20
-
21
- class << self
22
- def reloadable?
23
- false
24
- end
25
-
26
- def mailer_method_category
27
- {
28
- log_parser_exception_notification: :NetworkOptout
29
- }
30
- end
31
- end
32
-
33
- def escalation_notification(summary, data, custom_recipients = nil)
34
- subject = "#{email_environment} Escalation: #{summary}"
35
- from = sender_address.gsub('xception', 'scalation')
36
- recipients = begin
37
- custom_recipients || escalation_recipients
38
- rescue
39
- exception_recipients
40
- end
41
-
42
- @summary = summary
43
- @server = ExceptionHandling.server_name
44
- @cleaned_data = data
45
-
46
- mail(from: from,
47
- to: recipients,
48
- subject: subject)
49
- end
50
-
51
- def log_parser_exception_notification(cleaned_data, key)
52
- if cleaned_data.is_a?(Hash)
53
- cleaned_data = cleaned_data.symbolize_keys
54
- local_subject = cleaned_data[:error]
55
- else
56
- local_subject = "#{key}: #{cleaned_data}"
57
- cleaned_data = { error: cleaned_data.to_s }
58
- end
59
-
60
- @subject = "#{email_prefix}#{local_subject}"[0, 300]
61
- @recipients = exception_recipients
62
- from = sender_address
63
- @cleaned_data = cleaned_data
64
-
65
- mail(from: from,
66
- to: @recipients,
67
- subject: @subject)
68
- end
69
- end
70
- end
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/concern'
4
- require_relative 'logging_methods'
5
-
6
- module ExceptionHandling
7
- module Methods # included on models and controllers
8
- extend ActiveSupport::Concern
9
- include ExceptionHandling::LoggingMethods
10
-
11
- protected
12
-
13
- def long_controller_action_timeout
14
- if defined?(Rails) && Rails.respond_to?(:env) && Rails.env == 'test'
15
- 300
16
- else
17
- 30
18
- end
19
- end
20
-
21
- def set_current_controller
22
- ExceptionHandling.current_controller = self
23
- result = nil
24
- time = Benchmark.measure do
25
- result = yield
26
- end
27
- if time.real > long_controller_action_timeout && !['development', 'test'].include?(ExceptionHandling.email_environment)
28
- name = begin
29
- " in #{controller_name}::#{action_name}"
30
- rescue
31
- " "
32
- end
33
- log_error("Long controller action detected#{name} %.4fs " % time.real)
34
- end
35
- result
36
- ensure
37
- ExceptionHandling.current_controller = nil
38
- end
39
-
40
- included do
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
45
- end
46
-
47
- class_methods do
48
- def set_long_controller_action_timeout(timeout)
49
- define_method(:long_controller_action_timeout) { timeout }
50
- protected :long_controller_action_timeout
51
- end
52
- end
53
- end
54
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "socket"
4
-
5
- module ExceptionHandling
6
- module Sensu
7
- LEVELS = {
8
- warning: 1,
9
- critical: 2
10
- }.freeze
11
-
12
- class << self
13
- def generate_event(name, message, level = :warning)
14
- status = LEVELS[level] or raise "Invalid alert level #{level}"
15
-
16
- event = { name: ExceptionHandling.sensu_prefix.to_s + name, output: message, status: status }
17
-
18
- send_event(event)
19
- end
20
-
21
- def send_event(event)
22
- Socket.tcp(ExceptionHandling.sensu_host, ExceptionHandling.sensu_port) do |sock|
23
- sock.send(event.to_json, 0)
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,97 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path('../../spec_helper', __dir__)
4
- require 'rails-dom-testing'
5
-
6
- module ExceptionHandling
7
- describe Mailer do
8
-
9
- include ::Rails::Dom::Testing::Assertions::SelectorAssertions
10
-
11
- def dont_stub_log_error
12
- true
13
- end
14
-
15
- context "ExceptionHandling::Mailer" do
16
- before do
17
- ExceptionHandling.email_environment = 'Test'
18
- ExceptionHandling.sender_address = %("Test Exception Mailer" <null_exception@invoca.com>)
19
- ExceptionHandling.exception_recipients = ['test_exception@invoca.com']
20
- ExceptionHandling.escalation_recipients = ['test_escalation@invoca.com']
21
- end
22
-
23
- context "log_parser_exception_notification" do
24
- it "send with string" do
25
- result = ExceptionHandling::Mailer.log_parser_exception_notification("This is my fake error", "My Fake Subj").deliver_now
26
- expect(result.subject).to eq("Test exception: My Fake Subj: This is my fake error")
27
- expect(result.body.to_s).to match(/This is my fake error/)
28
- assert_emails 1
29
- end
30
- end
31
-
32
- context "escalation_notification" do
33
- before do
34
- def document_root_element
35
- @body_html.root
36
- end
37
- end
38
-
39
- it "send all the information" do
40
- ExceptionHandling.email_environment = 'Staging Full'
41
- ExceptionHandling.server_name = 'test-fe3'
42
-
43
- ExceptionHandling::Mailer.escalation_notification("Your Favorite <b>Feature<b> Failed", error_string: "It failed because of an error\n <i>More Info<i>", timestamp: 1234567).deliver_now
44
-
45
- assert_emails 1
46
- result = ActionMailer::Base.deliveries.last
47
- @body_html = Nokogiri::HTML(result.body.to_s)
48
- assert_equal_with_diff ['test_escalation@invoca.com'], result.to
49
- expect(result[:from].formatted).to eq(["Test Escalation Mailer <null_escalation@invoca.com>"])
50
- expect(result.subject).to eq("Staging Full Escalation: Your Favorite <b>Feature<b> Failed")
51
- assert_select "title", "Exception Escalation"
52
- assert_select "html" do
53
- assert_select "body br", { count: 4 }, result.body.to_s # plus 1 for the multiline summary
54
- assert_select "body h3", "Your Favorite <b>Feature<b> Failed", result.body.to_s
55
- assert_select "body", /1234567/
56
- assert_select "body", /It failed because of an error/
57
- assert_select "body", /\n <i>More Info<i>/
58
- assert_select "body", /test-fe3/
59
- # assert_select "body", /#{Web::Application::GIT_REVISION}/
60
- end
61
- end
62
-
63
- it "use defaults for missing fields" do
64
- result = ExceptionHandling::Mailer.escalation_notification("Your Favorite Feature Failed", error_string: "It failed because of an error\n More Info")
65
- @body_html = Nokogiri::HTML(result.body.to_s)
66
-
67
- assert_equal_with_diff ['test_escalation@invoca.com'], result.to
68
- expect(result.from).to eq(["null_escalation@invoca.com"])
69
- expect(result.subject).to eq('Test Escalation: Your Favorite Feature Failed')
70
- assert_select "html" do
71
- assert_select "body i", true, result.body.to_s do |is|
72
- assert_select is, "i", 'no error #'
73
- end
74
- end
75
- end
76
-
77
- context "ExceptionHandling.escalate_to_production_support" do
78
- before do
79
- Time.now_override = Time.parse('1986-5-21 4:17 am UTC')
80
- end
81
-
82
- it "notify production support" do
83
- subject = "Runtime Error found!"
84
- exception = RuntimeError.new("Test")
85
- recipients = ["prodsupport@example.com"]
86
-
87
- ExceptionHandling.production_support_recipients = recipients
88
- ExceptionHandling.last_exception_timestamp = Time.now.to_i
89
-
90
- expect(ExceptionHandling).to receive(:escalate).with(subject, exception, Time.now.to_i, recipients)
91
- ExceptionHandling.escalate_to_production_support(exception, subject)
92
- end
93
- end
94
- end
95
- end
96
- end
97
- end
@@ -1,105 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path('../../spec_helper', __dir__)
4
-
5
- require "exception_handling/testing"
6
- require_relative '../../helpers/exception_helpers.rb'
7
-
8
- module ExceptionHandling
9
- describe Methods do
10
- include ExceptionHelpers
11
-
12
- def dont_stub_log_error
13
- true
14
- end
15
-
16
- context "ExceptionHandling::Methods" do
17
- before do
18
- @controller = Testing::MethodsControllerStub.new
19
- ExceptionHandling.stub_handler = nil
20
- end
21
-
22
- it "set the around filter" do
23
- expect(Testing::MethodsControllerStub.around_filter_method).to eq(:set_current_controller)
24
- expect(ExceptionHandling.current_controller).to be_nil
25
- @controller.simulate_around_filter do
26
- expect(ExceptionHandling.current_controller).to eq(@controller)
27
- end
28
- expect(ExceptionHandling.current_controller).to be_nil
29
- end
30
-
31
- it "use the current_controller when available" do
32
- capture_notifications
33
-
34
- expect(ExceptionHandling.logger).to receive(:fatal).with(/blah/, any_args).at_least(:once)
35
- @controller.simulate_around_filter do
36
- ExceptionHandling.log_error(ArgumentError.new("blah"))
37
- expect(sent_notifications.size).to eq(1)
38
- expect(/#{Regexp.new(Regexp.escape(@controller.request.request_uri))}/).to match(sent_notifications.last.enhanced_data['request'].to_s)
39
- end
40
- end
41
-
42
- it "report long running controller action" do
43
- expect(@controller.send(:long_controller_action_timeout)).to eq(2)
44
- expect(ExceptionHandling).to receive(:log_error).with(/Long controller action detected in #{@controller.class.name.split("::").last}::test_action/)
45
- @controller.simulate_around_filter do
46
- sleep(3)
47
- end
48
- end
49
-
50
- it "not report long running controller actions if it is less than the timeout" do
51
- expect(@controller.send(:long_controller_action_timeout)).to eq(2)
52
- allow(ExceptionHandling).to receive(:log_error).and_return("Should not timeout")
53
- @controller.simulate_around_filter do
54
- sleep(1)
55
- end
56
- end
57
-
58
- it "default long running controller action(300/30 for test/prod)" do
59
- class DummyController
60
- include ExceptionHandling::Methods
61
- end
62
-
63
- controller = DummyController.new
64
-
65
- Rails.env = 'production'
66
- expect(controller.send(:long_controller_action_timeout)).to eq(30)
67
-
68
- Rails.env = 'test'
69
- expect(controller.send(:long_controller_action_timeout)).to eq(300)
70
- end
71
-
72
- context "#log_warning" do
73
- it "be available to the controller" do
74
- expect(@controller.methods.include?(:log_warning)).to eq(true)
75
- end
76
- end
77
-
78
- context "included deprecation" do
79
- before do
80
- mock_deprecation_3_0
81
- end
82
-
83
- it "deprecate when no around_filter in included hook" do
84
- k = Class.new
85
- k.include ExceptionHandling::Methods
86
- end
87
-
88
- it "deprecate controller around_filter in included hook" do
89
- controller = Class.new
90
- class << controller
91
- def around_filter(*)
92
- end
93
- end
94
- controller.include ExceptionHandling::Methods
95
- end
96
- end
97
-
98
- private
99
-
100
- def mock_deprecation_3_0
101
- expect(STDERR).to receive(:puts).with(/DEPRECATION WARNING: ExceptionHandling::Methods is deprecated and will be removed from exception_handling 3\.0/)
102
- end
103
- end
104
- end
105
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path('../../spec_helper', __dir__)
4
-
5
- module ExceptionHandling
6
- describe Sensu do
7
- context "#generate_event" do
8
- it "create an event" do
9
- expect(ExceptionHandling::Sensu).to receive(:send_event).with({ name: "world_is_ending", output: "stick head between knees and kiss ass goodbye", status: 1 })
10
-
11
- ExceptionHandling::Sensu.generate_event("world_is_ending", "stick head between knees and kiss ass goodbye")
12
- end
13
-
14
- it "add the sensu prefix" do
15
- ExceptionHandling.sensu_prefix = "cnn_"
16
-
17
- expect(ExceptionHandling::Sensu).to receive(:send_event).with({ name: "cnn_world_is_ending", output: "stick head between knees and kiss ass goodbye", status: 1 })
18
-
19
- ExceptionHandling::Sensu.generate_event("world_is_ending", "stick head between knees and kiss ass goodbye")
20
- end
21
-
22
- it "allow the level to be set to critical" do
23
- expect(ExceptionHandling::Sensu).to receive(:send_event).with({ name: "world_is_ending", output: "stick head between knees and kiss ass goodbye", status: 2 })
24
-
25
- ExceptionHandling::Sensu.generate_event("world_is_ending", "stick head between knees and kiss ass goodbye", :critical)
26
- end
27
-
28
- it "error if an invalid level is supplied" do
29
- expect(ExceptionHandling::Sensu).to_not receive(:send_event)
30
-
31
- expect do
32
- ExceptionHandling::Sensu.generate_event("world_is_ending", "stick head between knees and kiss ass goodbye", :hair_on_fire)
33
- end.to raise_exception(RuntimeError, /Invalid alert level/)
34
- end
35
- end
36
-
37
- context "#send_event" do
38
- before do
39
- @event = { name: "world_is_ending", output: "stick head between knees and kiss ass goodbye", status: 1 }
40
- @socket = SocketStub.new
41
- end
42
-
43
- it "send event json to sensu client" do
44
- expect_any_instance_of(Addrinfo).to receive(:connect).with(any_args) { @socket }
45
- ExceptionHandling::Sensu.send_event(@event)
46
-
47
- expect(@socket.sent.first).to eq(@event.to_json)
48
- end
49
- end
50
- end
51
- end
@@ -1,17 +0,0 @@
1
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
- <head>
5
- <title>Exception Escalation</title>
6
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
7
- </head>
8
- <body>
9
- <h3><%=h @summary %></h3>
10
- <b>Error #:</b> <%= h(@cleaned_data[:timestamp]).presence || '<i>no error #</i>'.html_safe -%><br />
11
- <b>Exception:</b> <%= h(@cleaned_data[:error_string]).gsub("\n","<br/>\n").gsub(/ {2,}/) { |spaces| '&nbsp;'*spaces.size }.html_safe %><br />
12
- <b>Server:</b> <%= h(@server).presence || '<i>hostname not set</i>'.html_safe -%><br />
13
- <% if @cleaned_data[:scm_revision] %>
14
- <b>Revision:</b> <%= @cleaned_data[:scm_revision] %><br />
15
- <% end %>
16
- </body>
17
- </html>
@@ -1,17 +0,0 @@
1
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
- <head>
5
- <title>Exception Escalation</title>
6
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
7
- </head>
8
- <body>
9
- <h3><%=h @summary %></h3>
10
- <b>Error #:</b> <%= h(@cleaned_data[:timestamp]).presence || '<i>no error #</i>'.html_safe -%><br />
11
- <b>Exception:</b> <%= h(@cleaned_data[:error_string]).gsub("\n","<br/>\n").gsub(/ {2,}/) { |spaces| '&nbsp;'*spaces.size }.html_safe %><br />
12
- <b>Server:</b> <%= h(@server).presence || '<i>hostname not set</i>'.html_safe -%><br />
13
- <% if @cleaned_data[:scm_revision] %>
14
- <b>Revision:</b> <%= @cleaned_data[:scm_revision] %><br />
15
- <% end %>
16
- </body>
17
- </html>
@@ -1,82 +0,0 @@
1
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
- <head>
5
- <title>Exception Email</title>
6
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
7
- </head>
8
- <body>
9
-
10
- <% if @cleaned_data[:first_seen_at] %>
11
- <p> This exception occurred <%= @cleaned_data[:occurrences] %> times since <%= @cleaned_data[:first_seen_at] %>.</p>
12
- <% end %>
13
-
14
- <b>Error # </b><%= @cleaned_data[:timestamp] -%><br />
15
-
16
- <b>URL:</b><br />
17
- <% if (request = @cleaned_data[:request]) %>
18
- <%= request[:url] || '<i>no URL in request data</i>'.html_safe %> <br />
19
- Referred from: <%= (@cleaned_data[:environment]['HTTP_REFERER'] || '<i>no referrer</i>').html_safe %>
20
- <% else %>
21
- <i>no URL accessed</i>
22
- <% end %>
23
- <br />
24
- <br />
25
-
26
- <b>User summary:</b><br />
27
- <% if (user_details = @cleaned_data[:user_details]) && ( user_details[:user] || user_details[:organization] ) %>
28
- User: <%= h user_details[:user] %> (<%= h user_details[:username] %>)<br />
29
- Organization: <%= user_details[:organization] %> <br />
30
- <%= "Network: #{h user_details[:organization].network if user_details[:organization]}" if !user_details[:organization].is_a?(Network) %>
31
-
32
- <% if @cleaned_data[:user_details][:impersonated_organization] %>
33
- <br />
34
- <b>Impersonating:</b><br />
35
- Organization: <%= h @cleaned_data[:user_details][:impersonated_organization] %>
36
- <% end %>
37
- <% else %>
38
- <i>No user logged in.</i>
39
- <% end %>
40
-
41
- <br />
42
- <br />
43
- <hr />
44
-
45
- <h3>Exception:</h3>
46
- <span id="error">
47
- <%= h(@cleaned_data[:error]).gsub("\n","<br/>\n").gsub(/ {2,}/) { |spaces| '&nbsp;'*spaces.size }.html_safe %>
48
- </span>
49
-
50
- <br />
51
- <br />
52
-
53
- <h3>Where:</h3>
54
- <%= "#{ h location[:controller]}##{ h location[:action]}<br />".html_safe if (location = @cleaned_data[:location]) && location[:controller] -%>
55
- <%= "#{ h location[:file]}, line #{ h location[:line]}<br />".html_safe if (location = @cleaned_data[:location]) && location[:file] -%>
56
-
57
- <br />
58
-
59
-
60
-
61
- <% for section in ExceptionHandling::ExceptionInfo::SECTIONS %>
62
- <% section_value = @cleaned_data[section] %>
63
- <% if section_value %>
64
- <h3><%= section.to_s.capitalize -%>:</h3>
65
- <pre id="<%= section.to_s.capitalize -%>" style="font-size: 12px; font-family: 'Courier New',Arial,sans-serif">
66
- <%= case section_value
67
- when Hash
68
- section_value[:to_s]
69
- when Array
70
- section_value.join( "\n" )
71
- when NilClass # omitted
72
- else
73
- raise "Unexpected value #{section_value.inspect} for section #{section}"
74
- end
75
- -%>
76
- </pre>
77
- <% end %>
78
-
79
- <% end %>
80
-
81
- </body>
82
- </html>