exception_notification 4.3.0 → 4.4.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/Appraisals +2 -2
- data/CHANGELOG.rdoc +14 -0
- data/CONTRIBUTING.md +18 -0
- data/Gemfile +1 -1
- data/README.md +64 -935
- data/Rakefile +2 -2
- data/docs/notifiers/campfire.md +50 -0
- data/docs/notifiers/custom.md +42 -0
- data/docs/notifiers/datadog.md +51 -0
- data/docs/notifiers/email.md +195 -0
- data/docs/notifiers/google_chat.md +31 -0
- data/docs/notifiers/hipchat.md +66 -0
- data/docs/notifiers/irc.md +97 -0
- data/docs/notifiers/mattermost.md +115 -0
- data/docs/notifiers/slack.md +161 -0
- data/docs/notifiers/sns.md +37 -0
- data/docs/notifiers/teams.md +54 -0
- data/docs/notifiers/webhook.md +60 -0
- data/examples/sample_app.rb +54 -0
- data/examples/sinatra/Gemfile +6 -6
- data/examples/sinatra/config.ru +1 -1
- data/examples/sinatra/sinatra_app.rb +14 -10
- data/exception_notification.gemspec +27 -22
- data/gemfiles/rails4_0.gemfile +3 -3
- data/gemfiles/rails4_1.gemfile +3 -3
- data/gemfiles/rails4_2.gemfile +3 -3
- data/gemfiles/rails5_0.gemfile +3 -3
- data/gemfiles/rails5_1.gemfile +3 -3
- data/gemfiles/rails5_2.gemfile +7 -0
- data/gemfiles/rails6_0.gemfile +7 -0
- data/lib/exception_notification.rb +1 -0
- data/lib/exception_notification/rack.rb +8 -21
- data/lib/exception_notification/resque.rb +8 -10
- data/lib/exception_notification/sidekiq.rb +8 -12
- data/lib/exception_notification/version.rb +3 -0
- data/lib/exception_notifier.rb +20 -3
- data/lib/exception_notifier/base_notifier.rb +2 -3
- data/lib/exception_notifier/campfire_notifier.rb +12 -13
- data/lib/exception_notifier/datadog_notifier.rb +153 -0
- data/lib/exception_notifier/email_notifier.rb +64 -87
- data/lib/exception_notifier/google_chat_notifier.rb +25 -119
- data/lib/exception_notifier/hipchat_notifier.rb +11 -12
- data/lib/exception_notifier/irc_notifier.rb +32 -30
- data/lib/exception_notifier/mattermost_notifier.rb +47 -140
- data/lib/exception_notifier/modules/backtrace_cleaner.rb +0 -2
- data/lib/exception_notifier/modules/error_grouping.rb +5 -5
- data/lib/exception_notifier/modules/formatter.rb +118 -0
- data/lib/exception_notifier/notifier.rb +5 -6
- data/lib/exception_notifier/slack_notifier.rb +63 -40
- data/lib/exception_notifier/sns_notifier.rb +17 -11
- data/lib/exception_notifier/teams_notifier.rb +58 -44
- data/lib/exception_notifier/views/exception_notifier/_backtrace.html.erb +1 -1
- data/lib/exception_notifier/views/exception_notifier/_environment.text.erb +1 -1
- data/lib/exception_notifier/views/exception_notifier/_request.text.erb +1 -1
- data/lib/exception_notifier/views/exception_notifier/exception_notification.html.erb +2 -2
- data/lib/exception_notifier/views/exception_notifier/exception_notification.text.erb +2 -2
- data/lib/exception_notifier/webhook_notifier.rb +14 -11
- data/lib/generators/exception_notification/install_generator.rb +5 -5
- data/lib/generators/exception_notification/templates/{exception_notification.rb → exception_notification.rb.erb} +13 -11
- data/test/exception_notification/rack_test.rb +27 -11
- data/test/exception_notification/resque_test.rb +52 -0
- data/test/exception_notifier/campfire_notifier_test.rb +42 -42
- data/test/exception_notifier/datadog_notifier_test.rb +151 -0
- data/test/exception_notifier/email_notifier_test.rb +269 -153
- data/test/exception_notifier/google_chat_notifier_test.rb +154 -101
- data/test/exception_notifier/hipchat_notifier_test.rb +78 -81
- data/test/exception_notifier/irc_notifier_test.rb +34 -34
- data/test/exception_notifier/mattermost_notifier_test.rb +164 -67
- data/test/exception_notifier/modules/error_grouping_test.rb +39 -40
- data/test/exception_notifier/modules/formatter_test.rb +150 -0
- data/test/exception_notifier/sidekiq_test.rb +6 -6
- data/test/exception_notifier/slack_notifier_test.rb +61 -60
- data/test/exception_notifier/sns_notifier_test.rb +27 -32
- data/test/exception_notifier/teams_notifier_test.rb +23 -26
- data/test/exception_notifier/webhook_notifier_test.rb +48 -46
- data/test/exception_notifier_test.rb +41 -38
- data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.html.erb +0 -0
- data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.text.erb +0 -0
- data/test/{dummy/app → support}/views/exception_notifier/_new_section.html.erb +0 -0
- data/test/{dummy/app → support}/views/exception_notifier/_new_section.text.erb +0 -0
- data/test/test_helper.rb +11 -14
- metadata +136 -166
- data/test/dummy/.gitignore +0 -4
- data/test/dummy/Rakefile +0 -7
- data/test/dummy/app/controllers/application_controller.rb +0 -3
- data/test/dummy/app/controllers/posts_controller.rb +0 -30
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/helpers/posts_helper.rb +0 -2
- data/test/dummy/app/models/post.rb +0 -2
- data/test/dummy/app/views/layouts/application.html.erb +0 -14
- data/test/dummy/app/views/posts/_form.html.erb +0 -0
- data/test/dummy/app/views/posts/new.html.erb +0 -0
- data/test/dummy/app/views/posts/show.html.erb +0 -0
- data/test/dummy/config.ru +0 -4
- data/test/dummy/config/application.rb +0 -42
- data/test/dummy/config/boot.rb +0 -6
- data/test/dummy/config/database.yml +0 -22
- data/test/dummy/config/environment.rb +0 -17
- data/test/dummy/config/environments/development.rb +0 -25
- data/test/dummy/config/environments/production.rb +0 -50
- data/test/dummy/config/environments/test.rb +0 -35
- data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/test/dummy/config/initializers/inflections.rb +0 -10
- data/test/dummy/config/initializers/mime_types.rb +0 -5
- data/test/dummy/config/initializers/secret_token.rb +0 -8
- data/test/dummy/config/initializers/session_store.rb +0 -8
- data/test/dummy/config/locales/en.yml +0 -5
- data/test/dummy/config/routes.rb +0 -3
- data/test/dummy/db/migrate/20110729022608_create_posts.rb +0 -15
- data/test/dummy/db/schema.rb +0 -24
- data/test/dummy/db/seeds.rb +0 -7
- data/test/dummy/lib/tasks/.gitkeep +0 -0
- data/test/dummy/public/404.html +0 -26
- data/test/dummy/public/422.html +0 -26
- data/test/dummy/public/500.html +0 -26
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/images/rails.png +0 -0
- data/test/dummy/public/index.html +0 -239
- data/test/dummy/public/javascripts/application.js +0 -2
- data/test/dummy/public/javascripts/controls.js +0 -965
- data/test/dummy/public/javascripts/dragdrop.js +0 -974
- data/test/dummy/public/javascripts/effects.js +0 -1123
- data/test/dummy/public/javascripts/prototype.js +0 -6001
- data/test/dummy/public/javascripts/rails.js +0 -191
- data/test/dummy/public/robots.txt +0 -5
- data/test/dummy/public/stylesheets/.gitkeep +0 -0
- data/test/dummy/public/stylesheets/scaffold.css +0 -56
- data/test/dummy/script/rails +0 -6
- data/test/dummy/test/functional/posts_controller_test.rb +0 -237
- data/test/dummy/test/test_helper.rb +0 -7
data/lib/exception_notifier.rb
CHANGED
|
@@ -8,6 +8,7 @@ module ExceptionNotifier
|
|
|
8
8
|
include ErrorGrouping
|
|
9
9
|
|
|
10
10
|
autoload :BacktraceCleaner, 'exception_notifier/modules/backtrace_cleaner'
|
|
11
|
+
autoload :Formatter, 'exception_notifier/modules/formatter'
|
|
11
12
|
|
|
12
13
|
autoload :Notifier, 'exception_notifier/notifier'
|
|
13
14
|
autoload :EmailNotifier, 'exception_notifier/email_notifier'
|
|
@@ -20,6 +21,7 @@ module ExceptionNotifier
|
|
|
20
21
|
autoload :TeamsNotifier, 'exception_notifier/teams_notifier'
|
|
21
22
|
autoload :SnsNotifier, 'exception_notifier/sns_notifier'
|
|
22
23
|
autoload :GoogleChatNotifier, 'exception_notifier/google_chat_notifier'
|
|
24
|
+
autoload :DatadogNotifier, 'exception_notifier/datadog_notifier'
|
|
23
25
|
|
|
24
26
|
class UndefinedNotifierError < StandardError; end
|
|
25
27
|
|
|
@@ -29,7 +31,7 @@ module ExceptionNotifier
|
|
|
29
31
|
|
|
30
32
|
# Define a set of exceptions to be ignored, ie, dont send notifications when any of them are raised.
|
|
31
33
|
mattr_accessor :ignored_exceptions
|
|
32
|
-
@@ignored_exceptions = %w
|
|
34
|
+
@@ignored_exceptions = %w[ActiveRecord::RecordNotFound Mongoid::Errors::DocumentNotFound AbstractController::ActionNotFound ActionController::RoutingError ActionController::UnknownFormat ActionController::UrlGenerationError]
|
|
33
35
|
|
|
34
36
|
mattr_accessor :testing_mode
|
|
35
37
|
@@testing_mode = false
|
|
@@ -45,9 +47,10 @@ module ExceptionNotifier
|
|
|
45
47
|
self.testing_mode = true
|
|
46
48
|
end
|
|
47
49
|
|
|
48
|
-
def notify_exception(exception, options={}, &block)
|
|
50
|
+
def notify_exception(exception, options = {}, &block)
|
|
49
51
|
return false if ignored_exception?(options[:ignore_exceptions], exception)
|
|
50
52
|
return false if ignored?(exception, options)
|
|
53
|
+
|
|
51
54
|
if error_grouping
|
|
52
55
|
errors_count = group_error!(exception, options)
|
|
53
56
|
return false unless send_notification?(exception, errors_count)
|
|
@@ -92,13 +95,20 @@ module ExceptionNotifier
|
|
|
92
95
|
@@ignores << block
|
|
93
96
|
end
|
|
94
97
|
|
|
98
|
+
def ignore_crawlers(crawlers)
|
|
99
|
+
ignore_if do |_exception, opts|
|
|
100
|
+
opts.key?(:env) && from_crawler(opts[:env], crawlers)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
95
104
|
def clear_ignore_conditions!
|
|
96
105
|
@@ignores.clear
|
|
97
106
|
end
|
|
98
107
|
|
|
99
108
|
private
|
|
109
|
+
|
|
100
110
|
def ignored?(exception, options)
|
|
101
|
-
@@ignores.any?{ |condition| condition.call(exception, options) }
|
|
111
|
+
@@ignores.any? { |condition| condition.call(exception, options) }
|
|
102
112
|
rescue Exception => e
|
|
103
113
|
raise e if @@testing_mode
|
|
104
114
|
|
|
@@ -130,5 +140,12 @@ module ExceptionNotifier
|
|
|
130
140
|
rescue NameError => e
|
|
131
141
|
raise UndefinedNotifierError, "No notifier named '#{name}' was found. Please, revise your configuration options. Cause: #{e.message}"
|
|
132
142
|
end
|
|
143
|
+
|
|
144
|
+
def from_crawler(env, ignored_crawlers)
|
|
145
|
+
agent = env['HTTP_USER_AGENT']
|
|
146
|
+
Array(ignored_crawlers).any? do |crawler|
|
|
147
|
+
agent =~ Regexp.new(crawler)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
133
150
|
end
|
|
134
151
|
end
|
|
@@ -2,11 +2,11 @@ module ExceptionNotifier
|
|
|
2
2
|
class BaseNotifier
|
|
3
3
|
attr_accessor :base_options
|
|
4
4
|
|
|
5
|
-
def initialize(options={})
|
|
5
|
+
def initialize(options = {})
|
|
6
6
|
@base_options = options
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
def send_notice(exception, options, message, message_opts=nil)
|
|
9
|
+
def send_notice(exception, options, message, message_opts = nil)
|
|
10
10
|
_pre_callback(exception, options, message, message_opts)
|
|
11
11
|
result = yield(message, message_opts)
|
|
12
12
|
_post_callback(exception, options, message, message_opts)
|
|
@@ -20,6 +20,5 @@ module ExceptionNotifier
|
|
|
20
20
|
def _post_callback(exception, options, message, message_opts)
|
|
21
21
|
@base_options[:post_callback].call(options, self, exception.backtrace, message, message_opts) if @base_options[:post_callback].respond_to?(:call)
|
|
22
22
|
end
|
|
23
|
-
|
|
24
23
|
end
|
|
25
24
|
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
module ExceptionNotifier
|
|
2
2
|
class CampfireNotifier < BaseNotifier
|
|
3
|
-
|
|
4
3
|
attr_accessor :subdomain
|
|
5
4
|
attr_accessor :token
|
|
6
5
|
attr_accessor :room
|
|
@@ -12,22 +11,22 @@ module ExceptionNotifier
|
|
|
12
11
|
room_name = options.delete(:room_name)
|
|
13
12
|
@campfire = Tinder::Campfire.new subdomain, options
|
|
14
13
|
@room = @campfire.find_room_by_name room_name
|
|
15
|
-
rescue
|
|
14
|
+
rescue StandardError
|
|
16
15
|
@campfire = @room = nil
|
|
17
16
|
end
|
|
18
17
|
end
|
|
19
18
|
|
|
20
|
-
def call(exception, options={})
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
def call(exception, options = {})
|
|
20
|
+
return unless active?
|
|
21
|
+
|
|
22
|
+
message = if options[:accumulated_errors_count].to_i > 1
|
|
23
|
+
"The exception occurred #{options[:accumulated_errors_count]} times: '#{exception.message}'"
|
|
24
|
+
else
|
|
25
|
+
"A new exception occurred: '#{exception.message}'"
|
|
26
|
+
end
|
|
27
|
+
message += " on '#{exception.backtrace.first}'" if exception.backtrace
|
|
28
|
+
send_notice(exception, options, message) do |msg, _|
|
|
29
|
+
@room.paste msg
|
|
31
30
|
end
|
|
32
31
|
end
|
|
33
32
|
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
require 'action_dispatch'
|
|
2
|
+
|
|
3
|
+
module ExceptionNotifier
|
|
4
|
+
class DatadogNotifier < BaseNotifier
|
|
5
|
+
attr_reader :client,
|
|
6
|
+
:default_options
|
|
7
|
+
|
|
8
|
+
def initialize(options)
|
|
9
|
+
super
|
|
10
|
+
@client = options.fetch(:client)
|
|
11
|
+
@default_options = options
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(exception, options = {})
|
|
15
|
+
client.emit_event(
|
|
16
|
+
datadog_event(exception, options)
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def datadog_event(exception, options = {})
|
|
21
|
+
DatadogExceptionEvent.new(
|
|
22
|
+
exception,
|
|
23
|
+
options.reverse_merge(default_options)
|
|
24
|
+
).event
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class DatadogExceptionEvent
|
|
28
|
+
include ExceptionNotifier::BacktraceCleaner
|
|
29
|
+
|
|
30
|
+
MAX_TITLE_LENGTH = 120
|
|
31
|
+
MAX_VALUE_LENGTH = 300
|
|
32
|
+
MAX_BACKTRACE_SIZE = 3
|
|
33
|
+
ALERT_TYPE = 'error'.freeze
|
|
34
|
+
|
|
35
|
+
attr_reader :exception,
|
|
36
|
+
:options
|
|
37
|
+
|
|
38
|
+
def initialize(exception, options)
|
|
39
|
+
@exception = exception
|
|
40
|
+
@options = options
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def request
|
|
44
|
+
@request ||= ActionDispatch::Request.new(options[:env]) if options[:env]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def controller
|
|
48
|
+
@controller ||= options[:env] && options[:env]['action_controller.instance']
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def backtrace
|
|
52
|
+
@backtrace ||= exception.backtrace ? clean_backtrace(exception) : []
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def tags
|
|
56
|
+
options[:tags] || []
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def title_prefix
|
|
60
|
+
options[:title_prefix] || ''
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def event
|
|
64
|
+
title = formatted_title
|
|
65
|
+
body = formatted_body
|
|
66
|
+
|
|
67
|
+
Dogapi::Event.new(
|
|
68
|
+
body,
|
|
69
|
+
msg_title: title,
|
|
70
|
+
alert_type: ALERT_TYPE,
|
|
71
|
+
tags: tags,
|
|
72
|
+
aggregation_key: [title]
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def formatted_title
|
|
77
|
+
title = ''
|
|
78
|
+
title << title_prefix
|
|
79
|
+
title << "#{controller.controller_name} #{controller.action_name}" if controller
|
|
80
|
+
title << " (#{exception.class})"
|
|
81
|
+
title << " #{exception.message.inspect}"
|
|
82
|
+
|
|
83
|
+
truncate(title, MAX_TITLE_LENGTH)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def formatted_body
|
|
87
|
+
text = []
|
|
88
|
+
|
|
89
|
+
text << '%%%'
|
|
90
|
+
text << formatted_request if request
|
|
91
|
+
text << formatted_session if request
|
|
92
|
+
text << formatted_backtrace
|
|
93
|
+
text << '%%%'
|
|
94
|
+
|
|
95
|
+
text.join("\n")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def formatted_key_value(key, value)
|
|
99
|
+
"**#{key}:** #{value}"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def formatted_request
|
|
103
|
+
text = []
|
|
104
|
+
text << '### **Request**'
|
|
105
|
+
text << formatted_key_value('URL', request.url)
|
|
106
|
+
text << formatted_key_value('HTTP Method', request.request_method)
|
|
107
|
+
text << formatted_key_value('IP Address', request.remote_ip)
|
|
108
|
+
text << formatted_key_value('Parameters', request.filtered_parameters.inspect)
|
|
109
|
+
text << formatted_key_value('Timestamp', Time.current)
|
|
110
|
+
text << formatted_key_value('Server', Socket.gethostname)
|
|
111
|
+
if defined?(Rails) && Rails.respond_to?(:root)
|
|
112
|
+
text << formatted_key_value('Rails root', Rails.root)
|
|
113
|
+
end
|
|
114
|
+
text << formatted_key_value('Process', $PROCESS_ID)
|
|
115
|
+
text << '___'
|
|
116
|
+
text.join("\n")
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def formatted_session
|
|
120
|
+
text = []
|
|
121
|
+
text << '### **Session**'
|
|
122
|
+
text << formatted_key_value('Data', request.session.to_hash)
|
|
123
|
+
text << '___'
|
|
124
|
+
text.join("\n")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def formatted_backtrace
|
|
128
|
+
size = [backtrace.size, MAX_BACKTRACE_SIZE].min
|
|
129
|
+
|
|
130
|
+
text = []
|
|
131
|
+
text << '### **Backtrace**'
|
|
132
|
+
text << '````'
|
|
133
|
+
size.times { |i| text << backtrace[i] }
|
|
134
|
+
text << '````'
|
|
135
|
+
text << '___'
|
|
136
|
+
text.join("\n")
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def truncate(string, max)
|
|
140
|
+
string.length > max ? "#{string[0...max]}..." : string
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def inspect_object(object)
|
|
144
|
+
case object
|
|
145
|
+
when Hash, Array
|
|
146
|
+
truncate(object.inspect, MAX_VALUE_LENGTH)
|
|
147
|
+
else
|
|
148
|
+
object.to_s
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
require "active_support/core_ext/hash/reverse_merge"
|
|
2
1
|
require 'active_support/core_ext/time'
|
|
3
2
|
require 'action_mailer'
|
|
4
3
|
require 'action_dispatch'
|
|
@@ -6,47 +5,61 @@ require 'pp'
|
|
|
6
5
|
|
|
7
6
|
module ExceptionNotifier
|
|
8
7
|
class EmailNotifier < BaseNotifier
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
DEFAULT_OPTIONS = {
|
|
9
|
+
sender_address: %("Exception Notifier" <exception.notifier@example.com>),
|
|
10
|
+
exception_recipients: [],
|
|
11
|
+
email_prefix: '[ERROR] ',
|
|
12
|
+
email_format: :text,
|
|
13
|
+
sections: %w[request session environment backtrace],
|
|
14
|
+
background_sections: %w[backtrace data],
|
|
15
|
+
verbose_subject: true,
|
|
16
|
+
normalize_subject: false,
|
|
17
|
+
include_controller_and_action_names_in_subject: true,
|
|
18
|
+
delivery_method: nil,
|
|
19
|
+
mailer_settings: nil,
|
|
20
|
+
email_headers: {},
|
|
21
|
+
mailer_parent: 'ActionMailer::Base',
|
|
22
|
+
template_path: 'exception_notifier',
|
|
23
|
+
deliver_with: nil
|
|
24
|
+
}.freeze
|
|
14
25
|
|
|
15
26
|
module Mailer
|
|
16
27
|
class MissingController
|
|
17
|
-
def method_missing(*args, &block)
|
|
18
|
-
end
|
|
28
|
+
def method_missing(*args, &block); end
|
|
19
29
|
end
|
|
20
30
|
|
|
21
31
|
def self.extended(base)
|
|
22
32
|
base.class_eval do
|
|
23
|
-
|
|
33
|
+
send(:include, ExceptionNotifier::BacktraceCleaner)
|
|
24
34
|
|
|
25
35
|
# Append application view path to the ExceptionNotifier lookup context.
|
|
26
|
-
|
|
36
|
+
append_view_path "#{File.dirname(__FILE__)}/views"
|
|
27
37
|
|
|
28
|
-
def exception_notification(env, exception, options={}, default_options={})
|
|
38
|
+
def exception_notification(env, exception, options = {}, default_options = {})
|
|
29
39
|
load_custom_views
|
|
30
40
|
|
|
31
41
|
@env = env
|
|
32
42
|
@exception = exception
|
|
33
|
-
|
|
43
|
+
|
|
44
|
+
env_options = env['exception_notifier.options'] || {}
|
|
45
|
+
@options = default_options.merge(env_options).merge(options)
|
|
46
|
+
|
|
34
47
|
@kontroller = env['action_controller.instance'] || MissingController.new
|
|
35
48
|
@request = ActionDispatch::Request.new(env)
|
|
36
49
|
@backtrace = exception.backtrace ? clean_backtrace(exception) : []
|
|
37
50
|
@timestamp = Time.current
|
|
38
51
|
@sections = @options[:sections]
|
|
39
52
|
@data = (env['exception_notifier.exception_data'] || {}).merge(options[:data] || {})
|
|
40
|
-
@sections
|
|
53
|
+
@sections += %w[data] unless @data.empty?
|
|
41
54
|
|
|
42
55
|
compose_email
|
|
43
56
|
end
|
|
44
57
|
|
|
45
|
-
def background_exception_notification(exception, options={}, default_options={})
|
|
58
|
+
def background_exception_notification(exception, options = {}, default_options = {})
|
|
46
59
|
load_custom_views
|
|
47
60
|
|
|
48
61
|
@exception = exception
|
|
49
|
-
@options =
|
|
62
|
+
@options = default_options.merge(options).symbolize_keys
|
|
50
63
|
@backtrace = exception.backtrace || []
|
|
51
64
|
@timestamp = Time.current
|
|
52
65
|
@sections = @options[:background_sections]
|
|
@@ -59,13 +72,13 @@ module ExceptionNotifier
|
|
|
59
72
|
private
|
|
60
73
|
|
|
61
74
|
def compose_subject
|
|
62
|
-
subject =
|
|
75
|
+
subject = @options[:email_prefix].to_s.dup
|
|
63
76
|
subject << "(#{@options[:accumulated_errors_count]} times)" if @options[:accumulated_errors_count].to_i > 1
|
|
64
77
|
subject << "#{@kontroller.controller_name} #{@kontroller.action_name}" if @kontroller && @options[:include_controller_and_action_names_in_subject]
|
|
65
78
|
subject << " (#{@exception.class})"
|
|
66
79
|
subject << " #{@exception.message.inspect}" if @options[:verbose_subject]
|
|
67
80
|
subject = EmailNotifier.normalize_digits(subject) if @options[:normalize_subject]
|
|
68
|
-
subject.length > 120 ? subject[0...120] +
|
|
81
|
+
subject.length > 120 ? subject[0...120] + '...' : subject
|
|
69
82
|
end
|
|
70
83
|
|
|
71
84
|
def set_data_variables
|
|
@@ -82,17 +95,17 @@ module ExceptionNotifier
|
|
|
82
95
|
|
|
83
96
|
def inspect_object(object)
|
|
84
97
|
case object
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
98
|
+
when Hash, Array
|
|
99
|
+
truncate(object.inspect, 300)
|
|
100
|
+
else
|
|
101
|
+
object.to_s
|
|
89
102
|
end
|
|
90
103
|
end
|
|
91
104
|
|
|
92
105
|
helper_method :safe_encode
|
|
93
106
|
|
|
94
107
|
def safe_encode(value)
|
|
95
|
-
value.encode(
|
|
108
|
+
value.encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
|
|
96
109
|
end
|
|
97
110
|
|
|
98
111
|
def html_mail?
|
|
@@ -106,11 +119,11 @@ module ExceptionNotifier
|
|
|
106
119
|
exception_recipients = maybe_call(@options[:exception_recipients])
|
|
107
120
|
|
|
108
121
|
headers = {
|
|
109
|
-
:
|
|
110
|
-
:
|
|
111
|
-
:
|
|
112
|
-
:
|
|
113
|
-
:
|
|
122
|
+
delivery_method: @options[:delivery_method],
|
|
123
|
+
to: exception_recipients,
|
|
124
|
+
from: @options[:sender_address],
|
|
125
|
+
subject: subject,
|
|
126
|
+
template_name: name
|
|
114
127
|
}.merge(@options[:email_headers])
|
|
115
128
|
|
|
116
129
|
mail = mail(headers) do |format|
|
|
@@ -124,9 +137,7 @@ module ExceptionNotifier
|
|
|
124
137
|
end
|
|
125
138
|
|
|
126
139
|
def load_custom_views
|
|
127
|
-
if defined?(Rails) && Rails.respond_to?(:root)
|
|
128
|
-
self.prepend_view_path Rails.root.nil? ? "app/views" : "#{Rails.root}/app/views"
|
|
129
|
-
end
|
|
140
|
+
prepend_view_path Rails.root.nil? ? 'app/views' : "#{Rails.root}/app/views" if defined?(Rails) && Rails.respond_to?(:root)
|
|
130
141
|
end
|
|
131
142
|
|
|
132
143
|
def maybe_call(maybe_proc)
|
|
@@ -138,82 +149,48 @@ module ExceptionNotifier
|
|
|
138
149
|
|
|
139
150
|
def initialize(options)
|
|
140
151
|
super
|
|
152
|
+
|
|
141
153
|
delivery_method = (options[:delivery_method] || :smtp)
|
|
142
154
|
mailer_settings_key = "#{delivery_method}_settings".to_sym
|
|
143
155
|
options[:mailer_settings] = options.delete(mailer_settings_key)
|
|
144
156
|
|
|
145
|
-
|
|
146
|
-
:sender_address, :exception_recipients, :pre_callback,
|
|
147
|
-
:post_callback, :email_prefix, :email_format,
|
|
148
|
-
:sections, :background_sections, :verbose_subject, :normalize_subject,
|
|
149
|
-
:include_controller_and_action_names_in_subject, :delivery_method, :mailer_settings,
|
|
150
|
-
:email_headers, :mailer_parent, :template_path, :deliver_with].include?(k)}.each{|k,v| send("#{k}=", v)}
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def options
|
|
154
|
-
@options ||= {}.tap do |opts|
|
|
155
|
-
self.instance_variables.each { |var| opts[var[1..-1].to_sym] = self.instance_variable_get(var) }
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def mailer
|
|
160
|
-
@mailer ||= Class.new(mailer_parent.constantize).tap do |mailer|
|
|
161
|
-
mailer.extend(EmailNotifier::Mailer)
|
|
162
|
-
mailer.mailer_name = template_path
|
|
163
|
-
end
|
|
157
|
+
@base_options = DEFAULT_OPTIONS.merge(options)
|
|
164
158
|
end
|
|
165
159
|
|
|
166
|
-
def call(exception, options={})
|
|
160
|
+
def call(exception, options = {})
|
|
167
161
|
message = create_email(exception, options)
|
|
168
162
|
|
|
169
|
-
|
|
170
|
-
if deliver_with == :default
|
|
171
|
-
if message.respond_to?(:deliver_now)
|
|
172
|
-
message.deliver_now
|
|
173
|
-
else
|
|
174
|
-
message.deliver
|
|
175
|
-
end
|
|
176
|
-
else
|
|
177
|
-
message.send(deliver_with)
|
|
178
|
-
end
|
|
163
|
+
message.send(base_options[:deliver_with] || default_deliver_with(message))
|
|
179
164
|
end
|
|
180
165
|
|
|
181
|
-
def create_email(exception, options={})
|
|
166
|
+
def create_email(exception, options = {})
|
|
182
167
|
env = options[:env]
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
168
|
+
|
|
169
|
+
send_notice(exception, options, nil, base_options) do |_, default_opts|
|
|
170
|
+
if env.nil?
|
|
186
171
|
mailer.background_exception_notification(exception, options, default_opts)
|
|
187
|
-
|
|
188
|
-
else
|
|
189
|
-
send_notice(exception, options, nil, default_options) do |_, default_opts|
|
|
172
|
+
else
|
|
190
173
|
mailer.exception_notification(env, exception, options, default_opts)
|
|
191
174
|
end
|
|
192
175
|
end
|
|
193
176
|
end
|
|
194
177
|
|
|
195
|
-
def self.default_options
|
|
196
|
-
{
|
|
197
|
-
:sender_address => %("Exception Notifier" <exception.notifier@example.com>),
|
|
198
|
-
:exception_recipients => [],
|
|
199
|
-
:email_prefix => "[ERROR] ",
|
|
200
|
-
:email_format => :text,
|
|
201
|
-
:sections => %w(request session environment backtrace),
|
|
202
|
-
:background_sections => %w(backtrace data),
|
|
203
|
-
:verbose_subject => true,
|
|
204
|
-
:normalize_subject => false,
|
|
205
|
-
:include_controller_and_action_names_in_subject => true,
|
|
206
|
-
:delivery_method => nil,
|
|
207
|
-
:mailer_settings => nil,
|
|
208
|
-
:email_headers => {},
|
|
209
|
-
:mailer_parent => 'ActionMailer::Base',
|
|
210
|
-
:template_path => 'exception_notifier',
|
|
211
|
-
:deliver_with => :default
|
|
212
|
-
}
|
|
213
|
-
end
|
|
214
|
-
|
|
215
178
|
def self.normalize_digits(string)
|
|
216
179
|
string.gsub(/[0-9]+/, 'N')
|
|
217
180
|
end
|
|
181
|
+
|
|
182
|
+
private
|
|
183
|
+
|
|
184
|
+
def mailer
|
|
185
|
+
@mailer ||= Class.new(base_options[:mailer_parent].constantize).tap do |mailer|
|
|
186
|
+
mailer.extend(EmailNotifier::Mailer)
|
|
187
|
+
mailer.mailer_name = base_options[:template_path]
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def default_deliver_with(message)
|
|
192
|
+
# FIXME: use `if Gem::Version.new(ActionMailer::VERSION::STRING) < Gem::Version.new('4.1')`
|
|
193
|
+
message.respond_to?(:deliver_now) ? :deliver_now : :deliver
|
|
194
|
+
end
|
|
218
195
|
end
|
|
219
196
|
end
|