exception_notification 4.3.0 → 4.5.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 +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
|