super_exception_notifier 2.0.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.
- data/MIT-LICENSE +21 -0
- data/README.rdoc +528 -0
- data/VERSION.yml +4 -0
- data/init.rb +1 -0
- data/lib/exception_notifiable.rb +175 -0
- data/lib/exception_notifier.rb +185 -0
- data/lib/exception_notifier_helper.rb +60 -0
- data/lib/notifiable.rb +91 -0
- data/lib/super_exception_notifier/custom_exception_classes.rb +16 -0
- data/lib/super_exception_notifier/custom_exception_methods.rb +50 -0
- data/lib/super_exception_notifier/deprecated_methods.rb +60 -0
- data/lib/super_exception_notifier/git_blame.rb +52 -0
- data/lib/super_exception_notifier/helpful_hashes.rb +66 -0
- data/lib/super_exception_notifier/hooks_notifier.rb +55 -0
- data/lib/super_exception_notifier/notifiable_helper.rb +79 -0
- data/rails/app/views/exception_notifiable/400.html +5 -0
- data/rails/app/views/exception_notifiable/403.html +6 -0
- data/rails/app/views/exception_notifiable/404.html +6 -0
- data/rails/app/views/exception_notifiable/405.html +6 -0
- data/rails/app/views/exception_notifiable/410.html +7 -0
- data/rails/app/views/exception_notifiable/418.html +6 -0
- data/rails/app/views/exception_notifiable/422.html +5 -0
- data/rails/app/views/exception_notifiable/423.html +6 -0
- data/rails/app/views/exception_notifiable/501.html +8 -0
- data/rails/app/views/exception_notifiable/503.html +6 -0
- data/rails/app/views/exception_notifiable/method_disabled.html.erb +6 -0
- data/rails/init.rb +25 -0
- data/tasks/notified_task.rake +15 -0
- data/test/exception_notifiable_test.rb +34 -0
- data/test/exception_notifier_helper_test.rb +76 -0
- data/test/exception_notifier_test.rb +41 -0
- data/test/exception_notify_functional_test.rb +139 -0
- data/test/mocks/controllers.rb +82 -0
- data/test/notifiable_test.rb +79 -0
- data/test/test_helper.rb +32 -0
- data/views/exception_notifier/_backtrace.html.erb +1 -0
- data/views/exception_notifier/_environment.html.erb +14 -0
- data/views/exception_notifier/_inspect_model.html.erb +16 -0
- data/views/exception_notifier/_request.html.erb +8 -0
- data/views/exception_notifier/_session.html.erb +6 -0
- data/views/exception_notifier/_title.html.erb +3 -0
- data/views/exception_notifier/background_exception_notification.text.plain.erb +10 -0
- data/views/exception_notifier/exception_notification.text.plain.erb +15 -0
- metadata +119 -0
data/VERSION.yml
ADDED
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/rails/init"
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
|
3
|
+
module ExceptionNotifiable
|
4
|
+
include SuperExceptionNotifier::NotifiableHelper
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
|
9
|
+
# Adds the following class attributes to the classes that include ExceptionNotifiable
|
10
|
+
# HTTP status codes and what their 'English' status message is
|
11
|
+
base.cattr_accessor :http_status_codes
|
12
|
+
base.http_status_codes = HTTP_STATUS_CODES
|
13
|
+
# error_layout:
|
14
|
+
# can be defined at controller level to the name of the desired error layout,
|
15
|
+
# or set to true to render the controller's own default layout,
|
16
|
+
# or set to false to render errors with no layout
|
17
|
+
base.cattr_accessor :error_layout
|
18
|
+
base.error_layout = nil
|
19
|
+
# Rails error classes to rescue and how to rescue them (which error code to use)
|
20
|
+
base.cattr_accessor :error_class_status_codes
|
21
|
+
base.error_class_status_codes = self.codes_for_error_classes
|
22
|
+
# Verbosity of the gem
|
23
|
+
base.cattr_accessor :exception_notifiable_verbose
|
24
|
+
base.exception_notifiable_verbose = false
|
25
|
+
# Do Not Ever send error notification emails for these Error Classes
|
26
|
+
base.cattr_accessor :exception_notifiable_silent_exceptions
|
27
|
+
base.exception_notifiable_silent_exceptions = SILENT_EXCEPTIONS
|
28
|
+
# Notification Level
|
29
|
+
base.cattr_accessor :exception_notifiable_notification_level
|
30
|
+
base.exception_notifiable_notification_level = [:render, :email, :web_hooks]
|
31
|
+
end
|
32
|
+
|
33
|
+
module ClassMethods
|
34
|
+
include SuperExceptionNotifier::DeprecatedMethods
|
35
|
+
|
36
|
+
# specifies ip addresses that should be handled as though local
|
37
|
+
def consider_local(*args)
|
38
|
+
local_addresses.concat(args.flatten.map { |a| IPAddr.new(a) })
|
39
|
+
end
|
40
|
+
|
41
|
+
def local_addresses
|
42
|
+
addresses = read_inheritable_attribute(:local_addresses)
|
43
|
+
unless addresses
|
44
|
+
addresses = [IPAddr.new("127.0.0.1")]
|
45
|
+
write_inheritable_attribute(:local_addresses, addresses)
|
46
|
+
end
|
47
|
+
addresses
|
48
|
+
end
|
49
|
+
|
50
|
+
# set the exception_data deliverer OR retrieve the exception_data
|
51
|
+
def exception_data(deliverer = nil)
|
52
|
+
if deliverer
|
53
|
+
write_inheritable_attribute(:exception_data, deliverer)
|
54
|
+
else
|
55
|
+
read_inheritable_attribute(:exception_data)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def be_silent_for_exception?(exception)
|
60
|
+
self.exception_notifiable_silent_exceptions.respond_to?(:any?) && self.exception_notifiable_silent_exceptions.any? {|klass| klass === exception }
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
def be_silent_for_exception?(exception)
|
66
|
+
self.class.be_silent_for_exception?(exception)
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def notification_level_sends_email?
|
73
|
+
self.class.exception_notifiable_notification_level.include?(:email)
|
74
|
+
end
|
75
|
+
|
76
|
+
def notification_level_sends_web_hooks?
|
77
|
+
self.class.exception_notifiable_notification_level.include?(:web_hooks)
|
78
|
+
end
|
79
|
+
|
80
|
+
def notification_level_renders?
|
81
|
+
self.class.exception_notifiable_notification_level.include?(:render)
|
82
|
+
end
|
83
|
+
|
84
|
+
# overrides Rails' local_request? method to also check any ip
|
85
|
+
# addresses specified through consider_local.
|
86
|
+
def local_request?
|
87
|
+
remote = IPAddr.new(request.remote_ip)
|
88
|
+
!self.class.local_addresses.detect { |addr| addr.include?(remote) }.nil?
|
89
|
+
end
|
90
|
+
|
91
|
+
# When the action being executed has its own local error handling (rescue)
|
92
|
+
# Or when the error accurs somewhere without a subsequent render (eg. method calls in console)
|
93
|
+
def rescue_with_handler(exception)
|
94
|
+
to_return = super
|
95
|
+
if to_return
|
96
|
+
verbose = self.class.exception_notifiable_verbose
|
97
|
+
puts "[RESCUE STYLE] rescue_with_handler" if verbose
|
98
|
+
data = get_exception_data
|
99
|
+
status_code = status_code_for_exception(exception)
|
100
|
+
#We only send email if it has been configured in environment
|
101
|
+
send_email = should_email_on_exception?(exception, status_code, verbose)
|
102
|
+
#We only send web hooks if they've been configured in environment
|
103
|
+
send_web_hooks = should_web_hook_on_exception?(exception, status_code, verbose)
|
104
|
+
the_blamed = ExceptionNotifier.config[:git_repo_path].nil? ? nil : lay_blame(exception)
|
105
|
+
verbose_output(exception, status_code, "rescued by handler", send_email, send_web_hooks, nil, the_blamed) if verbose
|
106
|
+
# Send the exception notificaiton email
|
107
|
+
perform_exception_notify_mailing(exception, data, nil, the_blamed, verbose) if send_email
|
108
|
+
# Send Web Hook requests
|
109
|
+
HooksNotifier.deliver_exception_to_web_hooks(ExceptionNotifier.config, exception, self, request, data, the_blamed) if send_web_hooks
|
110
|
+
end
|
111
|
+
to_return
|
112
|
+
end
|
113
|
+
|
114
|
+
# When the action being executed is letting SEN handle the exception completely
|
115
|
+
def rescue_action_in_public(exception)
|
116
|
+
# If the error class is NOT listed in the rails_errror_class hash then we get a generic 500 error:
|
117
|
+
# OTW if the error class is listed, but has a blank code or the code is == '200' then we get a custom error layout rendered
|
118
|
+
# OTW the error class is listed!
|
119
|
+
verbose = self.class.exception_notifiable_verbose
|
120
|
+
puts "[RESCUE STYLE] rescue_action_in_public" if verbose
|
121
|
+
status_code = status_code_for_exception(exception)
|
122
|
+
if status_code == '200'
|
123
|
+
notify_and_render_error_template(status_code, request, exception, ExceptionNotifier.get_view_path_for_class(exception, verbose), verbose)
|
124
|
+
else
|
125
|
+
notify_and_render_error_template(status_code, request, exception, ExceptionNotifier.get_view_path_for_status_code(status_code, verbose), verbose)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def notify_and_render_error_template(status_cd, request, exception, file_path, verbose = false)
|
130
|
+
status = self.class.http_status_codes[status_cd] ? status_cd + " " + self.class.http_status_codes[status_cd] : status_cd
|
131
|
+
data = get_exception_data
|
132
|
+
#We only send email if it has been configured in environment
|
133
|
+
send_email = should_email_on_exception?(exception, status_cd, verbose)
|
134
|
+
#We only send web hooks if they've been configured in environment
|
135
|
+
send_web_hooks = should_web_hook_on_exception?(exception, status_cd, verbose)
|
136
|
+
the_blamed = ExceptionNotifier.config[:git_repo_path].nil? ? nil : lay_blame(exception)
|
137
|
+
|
138
|
+
# Debugging output
|
139
|
+
verbose_output(exception, status_cd, file_path, send_email, send_web_hooks, request, the_blamed) if verbose
|
140
|
+
|
141
|
+
#TODO: is _rescue_action something from rails 3?
|
142
|
+
#if !(self.controller_name == 'application' && self.action_name == '_rescue_action')
|
143
|
+
perform_exception_notify_mailing(exception, data, request, the_blamed, verbose) if send_email
|
144
|
+
# Send Web Hook requests
|
145
|
+
HooksNotifier.deliver_exception_to_web_hooks(ExceptionNotifier.config, exception, self, request, data, the_blamed) if send_web_hooks
|
146
|
+
|
147
|
+
# We put the render call after the deliver call to ensure that, if the
|
148
|
+
# deliver raises an exception, we don't call render twice.
|
149
|
+
# Render the error page to the end user
|
150
|
+
render_error_template(file_path, status)
|
151
|
+
end
|
152
|
+
|
153
|
+
def is_local?
|
154
|
+
(consider_all_requests_local || local_request?)
|
155
|
+
end
|
156
|
+
|
157
|
+
def status_code_for_exception(exception)
|
158
|
+
self.class.error_class_status_codes[exception.class].nil? ?
|
159
|
+
'500' :
|
160
|
+
self.class.error_class_status_codes[exception.class].blank? ?
|
161
|
+
'200' :
|
162
|
+
self.class.error_class_status_codes[exception.class]
|
163
|
+
end
|
164
|
+
|
165
|
+
def render_error_template(file, status)
|
166
|
+
respond_to do |type|
|
167
|
+
type.html { render :file => file,
|
168
|
+
:layout => self.class.error_layout,
|
169
|
+
:status => status }
|
170
|
+
type.all { render :nothing => true,
|
171
|
+
:status => status}
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
class ExceptionNotifier < ActionMailer::Base
|
4
|
+
|
5
|
+
#andrewroth reported that @@config gets clobbered because rails loads this class twice when installed as a plugin, and adding the ||= fixed it.
|
6
|
+
@@config ||= {
|
7
|
+
# If left empty web hooks will not be engaged
|
8
|
+
:web_hooks => [],
|
9
|
+
:app_name => "[MYAPP]",
|
10
|
+
:version => "0.0.0",
|
11
|
+
:sender_address => "super.exception.notifier@example.com",
|
12
|
+
:exception_recipients => [],
|
13
|
+
# Customize the subject line
|
14
|
+
:subject_prepend => "[#{(defined?(Rails) ? Rails.env : RAILS_ENV).capitalize} ERROR] ",
|
15
|
+
:subject_append => nil,
|
16
|
+
# Include which sections of the exception email?
|
17
|
+
:sections => %w(request session environment backtrace),
|
18
|
+
:skip_local_notification => true,
|
19
|
+
:view_path => nil,
|
20
|
+
#Error Notification will be sent if the HTTP response code for the error matches one of the following error codes
|
21
|
+
:notify_error_codes => %W( 405 500 503 ),
|
22
|
+
#Error Notification will be sent if the error class matches one of the following error error classes
|
23
|
+
:notify_error_classes => %W( ),
|
24
|
+
:notify_other_errors => true,
|
25
|
+
:git_repo_path => nil,
|
26
|
+
:template_root => "#{File.dirname(__FILE__)}/../views"
|
27
|
+
}
|
28
|
+
|
29
|
+
cattr_accessor :config
|
30
|
+
|
31
|
+
def self.configure_exception_notifier(&block)
|
32
|
+
yield @@config
|
33
|
+
end
|
34
|
+
|
35
|
+
self.template_root = config[:template_root]
|
36
|
+
|
37
|
+
def self.reloadable?() false end
|
38
|
+
|
39
|
+
# Returns an array of potential filenames to look for
|
40
|
+
# eg. For the Exception Class - SuperExceptionNotifier::CustomExceptionClasses::MethodDisabled
|
41
|
+
# the filename handles are:
|
42
|
+
# super_exception_notifier_custom_exception_classes_method_disabled
|
43
|
+
# method_disabled
|
44
|
+
def self.exception_to_filenames(exception)
|
45
|
+
filenames = []
|
46
|
+
e = exception.to_s
|
47
|
+
filenames << ExceptionNotifier.filenamify(e)
|
48
|
+
|
49
|
+
last_colon = e.rindex(':')
|
50
|
+
unless last_colon.nil?
|
51
|
+
filenames << ExceptionNotifier.filenamify(e[(last_colon + 1)..(e.length - 1)])
|
52
|
+
end
|
53
|
+
filenames
|
54
|
+
end
|
55
|
+
|
56
|
+
# Converts Stringified Class Names to acceptable filename handles with underscores
|
57
|
+
def self.filenamify(str)
|
58
|
+
str.delete(':').gsub( /([A-Za-z])([A-Z])/, '\1' << '_' << '\2').downcase
|
59
|
+
end
|
60
|
+
|
61
|
+
# What is the path of the file we will render to the user based on a given status code?
|
62
|
+
def self.get_view_path_for_status_code(status_cd, verbose = false)
|
63
|
+
file_name = ExceptionNotifier.get_view_path(status_cd, verbose)
|
64
|
+
#ExceptionNotifierHelper::COMPAT_MODE ? "#{File.dirname(__FILE__)}/../rails/app/views/exception_notifiable/500.html" : "500.html"
|
65
|
+
file_name.nil? ? self.catch_all(verbose) : file_name
|
66
|
+
end
|
67
|
+
|
68
|
+
# def self.get_view_path_for_files(filenames = [])
|
69
|
+
# filepaths = filenames.map do |file|
|
70
|
+
# ExceptionNotifier.get_view_path(file)
|
71
|
+
# end.compact
|
72
|
+
# filepaths.empty? ? "#{File.dirname(__FILE__)}/../rails/app/views/exception_notifiable/500.html" : filepaths.first
|
73
|
+
# end
|
74
|
+
|
75
|
+
# What is the path of the file we will render to the user based on a given exception class?
|
76
|
+
def self.get_view_path_for_class(exception, verbose = false)
|
77
|
+
return self.catch_all if exception.nil?
|
78
|
+
return self.catch_all unless exception.is_a?(StandardError) || exception.is_a?(Class) # For some reason exception.is_a?(Class) works in console, but not when running in mongrel (ALWAYS returns false)?!?!?
|
79
|
+
filepaths = ExceptionNotifier.exception_to_filenames(exception).map do |file|
|
80
|
+
ExceptionNotifier.get_view_path(file, verbose)
|
81
|
+
end.compact
|
82
|
+
filepaths.empty? ? self.catch_all(verbose) : filepaths.first
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.catch_all(verbose = false)
|
86
|
+
puts "[CATCH ALL INVOKED] #{File.dirname(__FILE__)}/../rails/app/views/exception_notifiable/500.html" if verbose
|
87
|
+
"#{File.dirname(__FILE__)}/../rails/app/views/exception_notifiable/500.html"
|
88
|
+
end
|
89
|
+
|
90
|
+
# Check the usual suspects
|
91
|
+
def self.get_view_path(file_name, verbose = false)
|
92
|
+
if File.exist?("#{RAILS_ROOT}/public/#{file_name}.html")
|
93
|
+
puts "[FOUND FILE] #{RAILS_ROOT}/public/#{file_name}.html" if verbose
|
94
|
+
"#{RAILS_ROOT}/public/#{file_name}.html"
|
95
|
+
elsif !config[:view_path].nil? && File.exist?("#{RAILS_ROOT}/#{config[:view_path]}/#{file_name}.html.erb")
|
96
|
+
puts "[FOUND FILE] #{RAILS_ROOT}/#{config[:view_path]}/#{file_name}.html.erb" if verbose
|
97
|
+
"#{RAILS_ROOT}/#{config[:view_path]}/#{file_name}.html.erb"
|
98
|
+
elsif !config[:view_path].nil? && File.exist?("#{RAILS_ROOT}/#{config[:view_path]}/#{file_name}.html")
|
99
|
+
puts "[FOUND FILE] #{RAILS_ROOT}/#{config[:view_path]}/#{file_name}.html" if verbose
|
100
|
+
"#{RAILS_ROOT}/#{config[:view_path]}/#{file_name}.html"
|
101
|
+
elsif File.exist?("#{File.dirname(__FILE__)}/../rails/app/views/exception_notifiable/#{file_name}.html.erb")
|
102
|
+
puts "[FOUND FILE] #{File.dirname(__FILE__)}/../rails/app/views/exception_notifiable/#{file_name}.html.erb" if verbose
|
103
|
+
"#{File.dirname(__FILE__)}/../rails/app/views/exception_notifiable/#{file_name}.html.erb"
|
104
|
+
elsif File.exist?("#{File.dirname(__FILE__)}/../rails/app/views/exception_notifiable/#{file_name}.html")
|
105
|
+
#ExceptionNotifierHelper::COMPAT_MODE ? "#{File.dirname(__FILE__)}/../rails/app/views/exception_notifiable/#{file_name}.html" : "#{status_cd}.html"
|
106
|
+
puts "[FOUND FILE] #{File.dirname(__FILE__)}/../rails/app/views/exception_notifiable/#{file_name}.html" if verbose
|
107
|
+
"#{File.dirname(__FILE__)}/../rails/app/views/exception_notifiable/#{file_name}.html"
|
108
|
+
else
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def exception_notification(exception, class_name = nil, method_name = nil, request = nil, data={}, the_blamed=nil)
|
114
|
+
body_hash = error_environment_data_hash(exception, class_name, method_name, request, data, the_blamed)
|
115
|
+
#Prefer to have custom, potentially HTML email templates available
|
116
|
+
#content_type "text/plain"
|
117
|
+
recipients config[:exception_recipients]
|
118
|
+
from config[:sender_address]
|
119
|
+
|
120
|
+
request.session.inspect unless request.nil? # Ensure session data is loaded (Rails 2.3 lazy-loading)
|
121
|
+
|
122
|
+
subject "#{config[:subject_prepend]}#{body_hash[:location]} (#{exception.class}) #{exception.message.inspect}#{config[:subject_append]}"
|
123
|
+
body body_hash
|
124
|
+
end
|
125
|
+
|
126
|
+
def background_exception_notification(exception, data = {}, the_blamed = nil)
|
127
|
+
exception_notification(exception, nil, nil, data, the_blamed)
|
128
|
+
end
|
129
|
+
|
130
|
+
def rake_exception_notification(exception, task, data={})
|
131
|
+
content_type "text/plain"
|
132
|
+
|
133
|
+
subject "#{email_prefix}#{task.name} (#{exception.class}) #{exception.message.inspect}"
|
134
|
+
|
135
|
+
recipients exception_recipients
|
136
|
+
from sender_address
|
137
|
+
|
138
|
+
body data.merge({ :task => task,
|
139
|
+
:exception => exception,
|
140
|
+
:backtrace => sanitize_backtrace(exception.backtrace),
|
141
|
+
:rails_root => rails_root,
|
142
|
+
:data => data,
|
143
|
+
:sections => sections.reject{|s| %w(request session).include?(s) }
|
144
|
+
})
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def error_environment_data_hash(exception, class_name = nil, method_name = nil, request = nil, data={}, the_blamed=nil)
|
150
|
+
data.merge!({
|
151
|
+
:exception => exception,
|
152
|
+
:backtrace => sanitize_backtrace(exception.backtrace),
|
153
|
+
:rails_root => rails_root,
|
154
|
+
:data => data,
|
155
|
+
:the_blamed => the_blamed
|
156
|
+
})
|
157
|
+
|
158
|
+
data.merge!({:class_name => class_name}) if class_name
|
159
|
+
data.merge!({:method_name => method_name}) if method_name
|
160
|
+
if class_name && method_name
|
161
|
+
data.merge!({:location => "#{class_name}##{method_name}"})
|
162
|
+
else
|
163
|
+
data.merge!({:location => sanitize_backtrace([exception.backtrace.first]).first})
|
164
|
+
end
|
165
|
+
if request
|
166
|
+
data.merge!({:request => request})
|
167
|
+
data.merge!({:host => (request.env['HTTP_X_REAL_IP'] || request.env["HTTP_X_FORWARDED_HOST"] || request.env["HTTP_HOST"])})
|
168
|
+
data.merge!({:sections => config[:sections]})
|
169
|
+
else
|
170
|
+
# TODO: with refactoring in the view structure, the environment section could show useful ENV data even without a request?
|
171
|
+
data.merge!({:sections => config[:sections] - %w(request session environment)})
|
172
|
+
end
|
173
|
+
return data
|
174
|
+
end
|
175
|
+
|
176
|
+
def sanitize_backtrace(trace)
|
177
|
+
re = Regexp.new(/^#{Regexp.escape(rails_root)}/)
|
178
|
+
trace.map { |line| Pathname.new(line.gsub(re, "[RAILS_ROOT]")).cleanpath.to_s }
|
179
|
+
end
|
180
|
+
|
181
|
+
def rails_root
|
182
|
+
@rails_root ||= Pathname.new(RAILS_ROOT).cleanpath.to_s
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'pp'
|
2
|
+
|
3
|
+
module ExceptionNotifierHelper
|
4
|
+
VIEW_PATH = "views/exception_notifier" unless defined?(VIEW_PATH)
|
5
|
+
APP_PATH = "#{RAILS_ROOT}/app/#{VIEW_PATH}" unless defined?(APP_PATH)
|
6
|
+
PARAM_FILTER_REPLACEMENT = "[FILTERED]" unless defined?(PARAM_FILTER_REPLACEMENT)
|
7
|
+
COMPAT_MODE = defined?(RAILS_GEM_VERSION) ? RAILS_GEM_VERSION < '2' : false unless defined?(COMPAT_MODE)
|
8
|
+
|
9
|
+
def render_section(section)
|
10
|
+
RAILS_DEFAULT_LOGGER.info("rendering section #{section.inspect}")
|
11
|
+
summary = render_overridable(section).strip
|
12
|
+
unless summary.blank?
|
13
|
+
title = render_overridable(:title, :locals => { :title => section }).strip
|
14
|
+
"#{title}\n\n#{summary.gsub(/^/, " ")}\n\n"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def render_overridable(partial, options={})
|
19
|
+
if File.exist?(path = "#{APP_PATH}/_#{partial}.html.erb") ||
|
20
|
+
File.exist?(path = "#{File.dirname(__FILE__)}/../#{VIEW_PATH}/_#{partial}.html.erb") ||
|
21
|
+
File.exist?(path = "#{APP_PATH}/_#{partial}.rhtml") ||
|
22
|
+
File.exist?(path = "#{APP_PATH}/_#{partial}.erb")
|
23
|
+
render(options.merge(:file => path, :use_full_path => false))
|
24
|
+
else
|
25
|
+
""
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect_model_object(model, locals={})
|
30
|
+
render_overridable(:inspect_model,
|
31
|
+
:locals => { :inspect_model => model,
|
32
|
+
:show_instance_variables => true,
|
33
|
+
:show_attributes => true }.merge(locals))
|
34
|
+
end
|
35
|
+
|
36
|
+
def inspect_value(value)
|
37
|
+
len = 512
|
38
|
+
result = object_to_yaml(value).gsub(/\n/, "\n ").strip
|
39
|
+
result = result[0,len] + "... (#{result.length-len} bytes more)" if result.length > len+20
|
40
|
+
result
|
41
|
+
end
|
42
|
+
|
43
|
+
def object_to_yaml(object)
|
44
|
+
object.to_yaml.sub(/^---\s*/m, "")
|
45
|
+
end
|
46
|
+
|
47
|
+
def exclude_raw_post_parameters?
|
48
|
+
@controller && @controller.respond_to?(:filter_parameters)
|
49
|
+
end
|
50
|
+
|
51
|
+
def filter_sensitive_post_data_parameters(parameters)
|
52
|
+
exclude_raw_post_parameters? ? COMPAT_MODE ? @controller.filter_parameters(parameters) : @controller.__send__(:filter_parameters, parameters) : parameters
|
53
|
+
end
|
54
|
+
|
55
|
+
def filter_sensitive_post_data_from_env(env_key, env_value)
|
56
|
+
return env_value unless exclude_raw_post_parameters?
|
57
|
+
return PARAM_FILTER_REPLACEMENT if (env_key =~ /RAW_POST_DATA/i)
|
58
|
+
return COMPAT_MODE ? @controller.filter_parameters({env_key => env_value}).values[0] : @controller.__send__(:filter_parameters, {env_key => env_value}).values[0]
|
59
|
+
end
|
60
|
+
end
|
data/lib/notifiable.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
module Notifiable
|
2
|
+
include SuperExceptionNotifier::NotifiableHelper
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
|
7
|
+
# Verbosity of the gem
|
8
|
+
base.cattr_accessor :notifiable_verbose
|
9
|
+
base.notifiable_verbose = false
|
10
|
+
# Do Not Ever send error notification emails for these Error Classes
|
11
|
+
base.cattr_accessor :notifiable_silent_exceptions
|
12
|
+
base.notifiable_silent_exceptions = SILENT_EXCEPTIONS
|
13
|
+
# Notification Level
|
14
|
+
base.cattr_accessor :notifiable_notification_level
|
15
|
+
base.notifiable_notification_level = [:email, :web_hooks]
|
16
|
+
|
17
|
+
# Since there is no concept of locality from a request here allow user to explicitly define which env's are noisy (send notifications)
|
18
|
+
base.cattr_accessor :notifiable_noisy_environments
|
19
|
+
base.notifiable_noisy_environments = [:production]
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
# set the exception_data deliverer OR retrieve the exception_data
|
24
|
+
def exception_data(deliverer = nil)
|
25
|
+
if deliverer
|
26
|
+
write_inheritable_attribute(:exception_data, deliverer)
|
27
|
+
else
|
28
|
+
read_inheritable_attribute(:exception_data)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def be_silent_for_exception?(exception)
|
33
|
+
self.notifiable_silent_exceptions.respond_to?(:any?) && self.notifiable_silent_exceptions.any? {|klass| klass === exception }
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
# Usage:
|
39
|
+
# notifiable { Klass.some_method }
|
40
|
+
# This will rescue any errors that occur within Klass.some_method
|
41
|
+
def notifiable(&block)
|
42
|
+
yield
|
43
|
+
rescue => exception
|
44
|
+
rescue_with_hooks(exception)
|
45
|
+
raise
|
46
|
+
end
|
47
|
+
|
48
|
+
def be_silent_for_exception?(exception)
|
49
|
+
self.class.be_silent_for_exception?(exception)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def notification_level_sends_email?
|
55
|
+
self.class.notifiable_notification_level.include?(:email)
|
56
|
+
end
|
57
|
+
|
58
|
+
def notification_level_sends_web_hooks?
|
59
|
+
self.class.notifiable_notification_level.include?(:web_hooks)
|
60
|
+
end
|
61
|
+
|
62
|
+
def rescue_with_hooks(exception)
|
63
|
+
verbose = self.class.notifiable_verbose
|
64
|
+
puts "[RESCUE STYLE] rescue_with_hooks" if verbose
|
65
|
+
data = get_exception_data
|
66
|
+
# With ExceptionNotifiable you have an inherent request, and using a status code makes sense.
|
67
|
+
# With Notifiable class to wrap around everything that doesn't have a request,
|
68
|
+
# the errors you want to be notified of need to be specified either positively or negatively
|
69
|
+
# 1. positive eg. set ExceptionNotifier.config[:notify_error_classes] to an array of classes
|
70
|
+
# set ExceptionNotifier.config[:notify_other_errors] to false
|
71
|
+
# 1. negative eg. set Klass.silent_exceptions to the ones to keep quiet
|
72
|
+
# set ExceptionNotifier.config[:notify_other_errors] to true
|
73
|
+
status_code = nil
|
74
|
+
#We only send email if it has been configured in environment
|
75
|
+
send_email = should_email_on_exception?(exception, status_code, verbose)
|
76
|
+
#We only send web hooks if they've been configured in environment
|
77
|
+
send_web_hooks = should_web_hook_on_exception?(exception, status_code, verbose)
|
78
|
+
the_blamed = ExceptionNotifier.config[:git_repo_path].nil? ? nil : lay_blame(exception)
|
79
|
+
verbose_output(exception, status_code, "rescued by handler", send_email, send_web_hooks, nil, the_blamed) if verbose
|
80
|
+
# Send the exception notificaiton email
|
81
|
+
perform_exception_notify_mailing(exception, data, nil, the_blamed, verbose) if send_email
|
82
|
+
# Send Web Hook requests
|
83
|
+
HooksNotifier.deliver_exception_to_web_hooks(ExceptionNotifier.config, exception, self, request, data, the_blamed) if send_web_hooks
|
84
|
+
end
|
85
|
+
|
86
|
+
def is_local? #like asking is_silent?
|
87
|
+
eenv = defined?(Rails) ? Rails.env : RAILS_ENV
|
88
|
+
!self.notifiable_noisy_environments.include?(eenv)
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|