exception_notification 4.3.0 → 4.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Appraisals +4 -2
- data/CHANGELOG.rdoc +47 -0
- data/CONTRIBUTING.md +18 -0
- data/Gemfile +3 -1
- data/README.md +97 -945
- data/Rakefile +4 -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 +56 -0
- data/examples/sinatra/Gemfile +8 -6
- data/examples/sinatra/config.ru +3 -1
- data/examples/sinatra/sinatra_app.rb +19 -11
- data/exception_notification.gemspec +30 -24
- data/gemfiles/{rails4_0.gemfile → rails5_2.gemfile} +2 -2
- data/gemfiles/{rails4_1.gemfile → rails6_0.gemfile} +2 -2
- data/gemfiles/{rails4_2.gemfile → rails6_1.gemfile} +2 -2
- data/gemfiles/{rails5_0.gemfile → rails7_0.gemfile} +2 -2
- data/lib/exception_notification/rack.rb +28 -30
- data/lib/exception_notification/rails.rb +2 -0
- data/lib/exception_notification/resque.rb +10 -10
- data/lib/exception_notification/sidekiq.rb +10 -12
- data/lib/exception_notification/version.rb +5 -0
- data/lib/exception_notification.rb +3 -0
- data/lib/exception_notifier/base_notifier.rb +10 -5
- data/lib/exception_notifier/datadog_notifier.rb +156 -0
- data/lib/exception_notifier/email_notifier.rb +73 -88
- data/lib/exception_notifier/google_chat_notifier.rb +27 -119
- data/lib/exception_notifier/hipchat_notifier.rb +13 -12
- data/lib/exception_notifier/irc_notifier.rb +36 -33
- data/lib/exception_notifier/mattermost_notifier.rb +54 -137
- data/lib/exception_notifier/modules/backtrace_cleaner.rb +2 -2
- data/lib/exception_notifier/modules/error_grouping.rb +24 -13
- data/lib/exception_notifier/modules/formatter.rb +125 -0
- data/lib/exception_notifier/notifier.rb +9 -6
- data/lib/exception_notifier/slack_notifier.rb +65 -40
- data/lib/exception_notifier/sns_notifier.rb +23 -13
- data/lib/exception_notifier/teams_notifier.rb +67 -46
- 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 +17 -14
- data/lib/exception_notifier.rb +65 -10
- data/lib/generators/exception_notification/install_generator.rb +11 -5
- data/lib/generators/exception_notification/templates/{exception_notification.rb → exception_notification.rb.erb} +13 -11
- data/test/exception_notification/rack_test.rb +75 -13
- data/test/exception_notification/resque_test.rb +54 -0
- data/test/exception_notifier/datadog_notifier_test.rb +153 -0
- data/test/exception_notifier/email_notifier_test.rb +275 -153
- data/test/exception_notifier/google_chat_notifier_test.rb +158 -101
- data/test/exception_notifier/hipchat_notifier_test.rb +84 -81
- data/test/exception_notifier/irc_notifier_test.rb +36 -34
- data/test/exception_notifier/mattermost_notifier_test.rb +213 -67
- data/test/exception_notifier/modules/error_grouping_test.rb +41 -40
- data/test/exception_notifier/modules/formatter_test.rb +152 -0
- data/test/exception_notifier/sidekiq_test.rb +9 -17
- data/test/exception_notifier/slack_notifier_test.rb +66 -63
- data/test/exception_notifier/sns_notifier_test.rb +84 -32
- data/test/exception_notifier/teams_notifier_test.rb +25 -26
- data/test/exception_notifier/webhook_notifier_test.rb +52 -48
- data/test/exception_notifier_test.rb +150 -41
- data/test/support/exception_notifier_helper.rb +14 -0
- 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 +14 -13
- metadata +134 -175
- data/gemfiles/rails5_1.gemfile +0 -7
- data/lib/exception_notifier/campfire_notifier.rb +0 -40
- 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/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/config.ru +0 -4
- 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/test/exception_notifier/campfire_notifier_test.rb +0 -120
@@ -1,24 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'resque/failure/base'
|
2
4
|
|
3
5
|
module ExceptionNotification
|
4
6
|
class Resque < Resque::Failure::Base
|
5
|
-
|
6
7
|
def self.count
|
7
|
-
Stat[:failed]
|
8
|
+
::Resque::Stat[:failed]
|
8
9
|
end
|
9
10
|
|
10
11
|
def save
|
11
12
|
data = {
|
12
|
-
:
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
13
|
+
error_class: exception.class.name,
|
14
|
+
error_message: exception.message,
|
15
|
+
failed_at: Time.now.to_s,
|
16
|
+
payload: payload,
|
17
|
+
queue: queue,
|
18
|
+
worker: worker.to_s
|
18
19
|
}
|
19
20
|
|
20
|
-
ExceptionNotifier.notify_exception(exception, :
|
21
|
+
ExceptionNotifier.notify_exception(exception, data: { resque: data })
|
21
22
|
end
|
22
|
-
|
23
23
|
end
|
24
24
|
end
|
@@ -1,18 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'sidekiq'
|
2
4
|
|
3
5
|
# Note: this class is only needed for Sidekiq version < 3.
|
4
6
|
module ExceptionNotification
|
5
7
|
class Sidekiq
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
ExceptionNotifier.notify_exception(exception, :data => { :sidekiq => msg })
|
12
|
-
raise exception
|
13
|
-
end
|
8
|
+
def call(_worker, msg, _queue)
|
9
|
+
yield
|
10
|
+
rescue Exception => e
|
11
|
+
ExceptionNotifier.notify_exception(e, data: { sidekiq: msg })
|
12
|
+
raise e
|
14
13
|
end
|
15
|
-
|
16
14
|
end
|
17
15
|
end
|
18
16
|
|
@@ -24,8 +22,8 @@ if ::Sidekiq::VERSION < '3'
|
|
24
22
|
end
|
25
23
|
else
|
26
24
|
::Sidekiq.configure_server do |config|
|
27
|
-
config.error_handlers <<
|
28
|
-
ExceptionNotifier.notify_exception(ex, :
|
29
|
-
|
25
|
+
config.error_handlers << proc do |ex, context|
|
26
|
+
ExceptionNotifier.notify_exception(ex, data: { sidekiq: context })
|
27
|
+
end
|
30
28
|
end
|
31
29
|
end
|
@@ -1,12 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ExceptionNotifier
|
2
4
|
class BaseNotifier
|
3
5
|
attr_accessor :base_options
|
4
6
|
|
5
|
-
def initialize(options={})
|
7
|
+
def initialize(options = {})
|
6
8
|
@base_options = options
|
7
9
|
end
|
8
10
|
|
9
|
-
def send_notice(exception, options, message, message_opts=nil)
|
11
|
+
def send_notice(exception, options, message, message_opts = nil)
|
10
12
|
_pre_callback(exception, options, message, message_opts)
|
11
13
|
result = yield(message, message_opts)
|
12
14
|
_post_callback(exception, options, message, message_opts)
|
@@ -14,12 +16,15 @@ module ExceptionNotifier
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def _pre_callback(exception, options, message, message_opts)
|
17
|
-
|
19
|
+
return unless @base_options[:pre_callback].respond_to?(:call)
|
20
|
+
|
21
|
+
@base_options[:pre_callback].call(options, self, exception.backtrace, message, message_opts)
|
18
22
|
end
|
19
23
|
|
20
24
|
def _post_callback(exception, options, message, message_opts)
|
21
|
-
|
22
|
-
end
|
25
|
+
return unless @base_options[:post_callback].respond_to?(:call)
|
23
26
|
|
27
|
+
@base_options[:post_callback].call(options, self, exception.backtrace, message, message_opts)
|
28
|
+
end
|
24
29
|
end
|
25
30
|
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_dispatch'
|
4
|
+
|
5
|
+
module ExceptionNotifier
|
6
|
+
class DatadogNotifier < BaseNotifier
|
7
|
+
attr_reader :client,
|
8
|
+
:default_options
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
super
|
12
|
+
@client = options.fetch(:client)
|
13
|
+
@default_options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(exception, options = {})
|
17
|
+
client.emit_event(
|
18
|
+
datadog_event(exception, options)
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def datadog_event(exception, options = {})
|
23
|
+
DatadogExceptionEvent.new(
|
24
|
+
exception,
|
25
|
+
options.reverse_merge(default_options)
|
26
|
+
).event
|
27
|
+
end
|
28
|
+
|
29
|
+
class DatadogExceptionEvent
|
30
|
+
include ExceptionNotifier::BacktraceCleaner
|
31
|
+
|
32
|
+
MAX_TITLE_LENGTH = 120
|
33
|
+
MAX_VALUE_LENGTH = 300
|
34
|
+
MAX_BACKTRACE_SIZE = 3
|
35
|
+
ALERT_TYPE = 'error'
|
36
|
+
|
37
|
+
attr_reader :exception,
|
38
|
+
:options
|
39
|
+
|
40
|
+
def initialize(exception, options)
|
41
|
+
@exception = exception
|
42
|
+
@options = options
|
43
|
+
end
|
44
|
+
|
45
|
+
def request
|
46
|
+
@request ||= ActionDispatch::Request.new(options[:env]) if options[:env]
|
47
|
+
end
|
48
|
+
|
49
|
+
def controller
|
50
|
+
@controller ||= options[:env] && options[:env]['action_controller.instance']
|
51
|
+
end
|
52
|
+
|
53
|
+
def backtrace
|
54
|
+
@backtrace ||= exception.backtrace ? clean_backtrace(exception) : []
|
55
|
+
end
|
56
|
+
|
57
|
+
def tags
|
58
|
+
options[:tags] || []
|
59
|
+
end
|
60
|
+
|
61
|
+
def title_prefix
|
62
|
+
options[:title_prefix] || ''
|
63
|
+
end
|
64
|
+
|
65
|
+
def event
|
66
|
+
title = formatted_title
|
67
|
+
body = formatted_body
|
68
|
+
|
69
|
+
Dogapi::Event.new(
|
70
|
+
body,
|
71
|
+
msg_title: title,
|
72
|
+
alert_type: ALERT_TYPE,
|
73
|
+
tags: tags,
|
74
|
+
aggregation_key: [title]
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
def formatted_title
|
79
|
+
title =
|
80
|
+
"#{title_prefix}#{controller_subtitle} (#{exception.class}) #{exception.message.inspect}"
|
81
|
+
|
82
|
+
truncate(title, MAX_TITLE_LENGTH)
|
83
|
+
end
|
84
|
+
|
85
|
+
def formatted_body
|
86
|
+
text = []
|
87
|
+
|
88
|
+
text << '%%%'
|
89
|
+
text << formatted_request if request
|
90
|
+
text << formatted_session if request
|
91
|
+
text << formatted_backtrace
|
92
|
+
text << '%%%'
|
93
|
+
|
94
|
+
text.join("\n")
|
95
|
+
end
|
96
|
+
|
97
|
+
def formatted_key_value(key, value)
|
98
|
+
"**#{key}:** #{value}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def formatted_request
|
102
|
+
text = []
|
103
|
+
text << '### **Request**'
|
104
|
+
text << formatted_key_value('URL', request.url)
|
105
|
+
text << formatted_key_value('HTTP Method', request.request_method)
|
106
|
+
text << formatted_key_value('IP Address', request.remote_ip)
|
107
|
+
text << formatted_key_value('Parameters', request.filtered_parameters.inspect)
|
108
|
+
text << formatted_key_value('Timestamp', Time.current)
|
109
|
+
text << formatted_key_value('Server', Socket.gethostname)
|
110
|
+
text << formatted_key_value('Rails root', Rails.root) if defined?(Rails) && Rails.respond_to?(:root)
|
111
|
+
text << formatted_key_value('Process', $PROCESS_ID)
|
112
|
+
text << '___'
|
113
|
+
text.join("\n")
|
114
|
+
end
|
115
|
+
|
116
|
+
def formatted_session
|
117
|
+
text = []
|
118
|
+
text << '### **Session**'
|
119
|
+
text << formatted_key_value('Data', request.session.to_hash)
|
120
|
+
text << '___'
|
121
|
+
text.join("\n")
|
122
|
+
end
|
123
|
+
|
124
|
+
def formatted_backtrace
|
125
|
+
size = [backtrace.size, MAX_BACKTRACE_SIZE].min
|
126
|
+
|
127
|
+
text = []
|
128
|
+
text << '### **Backtrace**'
|
129
|
+
text << '````'
|
130
|
+
size.times { |i| text << backtrace[i] }
|
131
|
+
text << '````'
|
132
|
+
text << '___'
|
133
|
+
text.join("\n")
|
134
|
+
end
|
135
|
+
|
136
|
+
def truncate(string, max)
|
137
|
+
string.length > max ? "#{string[0...max]}..." : string
|
138
|
+
end
|
139
|
+
|
140
|
+
def inspect_object(object)
|
141
|
+
case object
|
142
|
+
when Hash, Array
|
143
|
+
truncate(object.inspect, MAX_VALUE_LENGTH)
|
144
|
+
else
|
145
|
+
object.to_s
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def controller_subtitle
|
152
|
+
"#{controller.controller_name} #{controller.action_name}" if controller
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'active_support/core_ext/time'
|
3
4
|
require 'action_mailer'
|
4
5
|
require 'action_dispatch'
|
@@ -6,47 +7,61 @@ require 'pp'
|
|
6
7
|
|
7
8
|
module ExceptionNotifier
|
8
9
|
class EmailNotifier < BaseNotifier
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
DEFAULT_OPTIONS = {
|
11
|
+
sender_address: %("Exception Notifier" <exception.notifier@example.com>),
|
12
|
+
exception_recipients: [],
|
13
|
+
email_prefix: '[ERROR] ',
|
14
|
+
email_format: :text,
|
15
|
+
sections: %w[request session environment backtrace],
|
16
|
+
background_sections: %w[backtrace data],
|
17
|
+
verbose_subject: true,
|
18
|
+
normalize_subject: false,
|
19
|
+
include_controller_and_action_names_in_subject: true,
|
20
|
+
delivery_method: nil,
|
21
|
+
mailer_settings: nil,
|
22
|
+
email_headers: {},
|
23
|
+
mailer_parent: 'ActionMailer::Base',
|
24
|
+
template_path: 'exception_notifier',
|
25
|
+
deliver_with: nil
|
26
|
+
}.freeze
|
14
27
|
|
15
28
|
module Mailer
|
16
29
|
class MissingController
|
17
|
-
def method_missing(*args, &block)
|
18
|
-
end
|
30
|
+
def method_missing(*args, &block); end
|
19
31
|
end
|
20
32
|
|
21
33
|
def self.extended(base)
|
22
34
|
base.class_eval do
|
23
|
-
|
35
|
+
send(:include, ExceptionNotifier::BacktraceCleaner)
|
24
36
|
|
25
37
|
# Append application view path to the ExceptionNotifier lookup context.
|
26
|
-
|
38
|
+
append_view_path "#{File.dirname(__FILE__)}/views"
|
27
39
|
|
28
|
-
def exception_notification(env, exception, options={}, default_options={})
|
40
|
+
def exception_notification(env, exception, options = {}, default_options = {})
|
29
41
|
load_custom_views
|
30
42
|
|
31
43
|
@env = env
|
32
44
|
@exception = exception
|
33
|
-
|
45
|
+
|
46
|
+
env_options = env['exception_notifier.options'] || {}
|
47
|
+
@options = default_options.merge(env_options).merge(options)
|
48
|
+
|
34
49
|
@kontroller = env['action_controller.instance'] || MissingController.new
|
35
50
|
@request = ActionDispatch::Request.new(env)
|
36
51
|
@backtrace = exception.backtrace ? clean_backtrace(exception) : []
|
37
52
|
@timestamp = Time.current
|
38
53
|
@sections = @options[:sections]
|
39
54
|
@data = (env['exception_notifier.exception_data'] || {}).merge(options[:data] || {})
|
40
|
-
@sections
|
55
|
+
@sections += %w[data] unless @data.empty?
|
41
56
|
|
42
57
|
compose_email
|
43
58
|
end
|
44
59
|
|
45
|
-
def background_exception_notification(exception, options={}, default_options={})
|
60
|
+
def background_exception_notification(exception, options = {}, default_options = {})
|
46
61
|
load_custom_views
|
47
62
|
|
48
63
|
@exception = exception
|
49
|
-
@options =
|
64
|
+
@options = default_options.merge(options).symbolize_keys
|
50
65
|
@backtrace = exception.backtrace || []
|
51
66
|
@timestamp = Time.current
|
52
67
|
@sections = @options[:background_sections]
|
@@ -59,13 +74,17 @@ module ExceptionNotifier
|
|
59
74
|
private
|
60
75
|
|
61
76
|
def compose_subject
|
62
|
-
subject =
|
77
|
+
subject = @options[:email_prefix].to_s.dup
|
63
78
|
subject << "(#{@options[:accumulated_errors_count]} times)" if @options[:accumulated_errors_count].to_i > 1
|
64
|
-
subject << "#{@kontroller.controller_name}
|
79
|
+
subject << "#{@kontroller.controller_name}##{@kontroller.action_name}" if include_controller?
|
65
80
|
subject << " (#{@exception.class})"
|
66
81
|
subject << " #{@exception.message.inspect}" if @options[:verbose_subject]
|
67
82
|
subject = EmailNotifier.normalize_digits(subject) if @options[:normalize_subject]
|
68
|
-
subject.length > 120 ? subject[0...120] +
|
83
|
+
subject.length > 120 ? subject[0...120] + '...' : subject
|
84
|
+
end
|
85
|
+
|
86
|
+
def include_controller?
|
87
|
+
@kontroller && @options[:include_controller_and_action_names_in_subject]
|
69
88
|
end
|
70
89
|
|
71
90
|
def set_data_variables
|
@@ -82,17 +101,17 @@ module ExceptionNotifier
|
|
82
101
|
|
83
102
|
def inspect_object(object)
|
84
103
|
case object
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
104
|
+
when Hash, Array
|
105
|
+
truncate(object.inspect, 300)
|
106
|
+
else
|
107
|
+
object.to_s
|
89
108
|
end
|
90
109
|
end
|
91
110
|
|
92
111
|
helper_method :safe_encode
|
93
112
|
|
94
113
|
def safe_encode(value)
|
95
|
-
value.encode(
|
114
|
+
value.encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
|
96
115
|
end
|
97
116
|
|
98
117
|
def html_mail?
|
@@ -106,11 +125,11 @@ module ExceptionNotifier
|
|
106
125
|
exception_recipients = maybe_call(@options[:exception_recipients])
|
107
126
|
|
108
127
|
headers = {
|
109
|
-
:
|
110
|
-
:
|
111
|
-
:
|
112
|
-
:
|
113
|
-
:
|
128
|
+
delivery_method: @options[:delivery_method],
|
129
|
+
to: exception_recipients,
|
130
|
+
from: @options[:sender_address],
|
131
|
+
subject: subject,
|
132
|
+
template_name: name
|
114
133
|
}.merge(@options[:email_headers])
|
115
134
|
|
116
135
|
mail = mail(headers) do |format|
|
@@ -124,9 +143,9 @@ module ExceptionNotifier
|
|
124
143
|
end
|
125
144
|
|
126
145
|
def load_custom_views
|
127
|
-
|
128
|
-
|
129
|
-
|
146
|
+
return unless defined?(Rails) && Rails.respond_to?(:root)
|
147
|
+
|
148
|
+
prepend_view_path Rails.root.nil? ? 'app/views' : "#{Rails.root}/app/views"
|
130
149
|
end
|
131
150
|
|
132
151
|
def maybe_call(maybe_proc)
|
@@ -138,82 +157,48 @@ module ExceptionNotifier
|
|
138
157
|
|
139
158
|
def initialize(options)
|
140
159
|
super
|
160
|
+
|
141
161
|
delivery_method = (options[:delivery_method] || :smtp)
|
142
162
|
mailer_settings_key = "#{delivery_method}_settings".to_sym
|
143
163
|
options[:mailer_settings] = options.delete(mailer_settings_key)
|
144
164
|
|
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)}
|
165
|
+
@base_options = DEFAULT_OPTIONS.merge(options)
|
151
166
|
end
|
152
167
|
|
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
|
164
|
-
end
|
165
|
-
|
166
|
-
def call(exception, options={})
|
168
|
+
def call(exception, options = {})
|
167
169
|
message = create_email(exception, options)
|
168
170
|
|
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
|
171
|
+
message.send(base_options[:deliver_with] || default_deliver_with(message))
|
179
172
|
end
|
180
173
|
|
181
|
-
def create_email(exception, options={})
|
174
|
+
def create_email(exception, options = {})
|
182
175
|
env = options[:env]
|
183
|
-
|
184
|
-
|
185
|
-
|
176
|
+
|
177
|
+
send_notice(exception, options, nil, base_options) do |_, default_opts|
|
178
|
+
if env.nil?
|
186
179
|
mailer.background_exception_notification(exception, options, default_opts)
|
187
|
-
|
188
|
-
else
|
189
|
-
send_notice(exception, options, nil, default_options) do |_, default_opts|
|
180
|
+
else
|
190
181
|
mailer.exception_notification(env, exception, options, default_opts)
|
191
182
|
end
|
192
183
|
end
|
193
184
|
end
|
194
185
|
|
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
186
|
def self.normalize_digits(string)
|
216
187
|
string.gsub(/[0-9]+/, 'N')
|
217
188
|
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def mailer
|
193
|
+
@mailer ||= Class.new(base_options[:mailer_parent].constantize).tap do |mailer|
|
194
|
+
mailer.extend(EmailNotifier::Mailer)
|
195
|
+
mailer.mailer_name = base_options[:template_path]
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def default_deliver_with(message)
|
200
|
+
# FIXME: use `if Gem::Version.new(ActionMailer::VERSION::STRING) < Gem::Version.new('4.1')`
|
201
|
+
message.respond_to?(:deliver_now) ? :deliver_now : :deliver
|
202
|
+
end
|
218
203
|
end
|
219
204
|
end
|