Skip to content

Commit 5e54d13

Browse files
committed
Refactor exception notifier for Rails 3
1 parent b7c60d6 commit 5e54d13

17 files changed

+110
-312
lines changed

README

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,6 @@ just set any or all of the following values:
3939
# defaults to "[ERROR] "
4040
ExceptionNotifier.email_prefix = "[APP] "
4141

42-
Email notifications will only occur when the IP address is determined not to
43-
be local. You can specify certain addresses to always be local so that you'll
44-
get a detailed error instead of the generic error page. You do this in your
45-
controller (or even per-controller):
46-
47-
consider_local "64.72.18.143", "14.17.21.25"
48-
49-
You can specify subnet masks as well, so that all matching addresses are
50-
considered local:
51-
52-
consider_local "64.72.18.143/24"
53-
54-
The address "127.0.0.1" is always considered local. If you want to completely
55-
reset the list of all addresses (for instance, if you wanted "127.0.0.1" to
56-
NOT be considered local), you can simply do, somewhere in your controller:
57-
58-
local_addresses.clear
5942

6043
== Customization
6144

@@ -68,9 +51,7 @@ access to the following variables:
6851
* @controller: the controller that caused the error
6952
* @request: the current request object
7053
* @exception: the exception that was raised
71-
* @host: the name of the host that made the request
7254
* @backtrace: a sanitized version of the exception's backtrace
73-
* @rails_root: a sanitized version of RAILS_ROOT
7455
* @data: a hash of optional data values that were passed to the notifier
7556
* @sections: the array of sections to include in the email
7657

@@ -82,13 +63,14 @@ new section requires information that isn't available by default, make sure
8263
it is made available to the email using the exception_data macro:
8364

8465
class ApplicationController < ActionController::Base
66+
before_filter :log_additional_data
8567
...
8668
protected
87-
exception_data :additional_data
88-
89-
def additional_data
90-
{ :document => @document,
91-
:person => @person }
69+
def log_additional_data
70+
request.env["exception_notifier.exception_data"] = {
71+
:document => @document,
72+
:person => @person
73+
}
9274
end
9375
...
9476
end
@@ -97,15 +79,5 @@ In the above case, @document and @person would be made available to the email
9779
renderer, allowing your new section(s) to access and display them. See the
9880
existing sections defined by the plugin for examples of how to write your own.
9981

100-
== Advanced Customization
101-
102-
By default, the email notifier will only notify on critical errors. For
103-
ActiveRecord::RecordNotFound and ActionController::UnknownAction, it will
104-
simply render the contents of your public/404.html file. Other exceptions
105-
will render public/500.html and will send the email notification. If you want
106-
to use different rules for the notification, you will need to implement your
107-
own rescue_action_in_public method. You can look at the default implementation
108-
in ExceptionNotifiable for an example of how to go about that.
109-
11082

11183
Copyright (c) 2005 Jamis Buck, released under the MIT license

init.rb

Lines changed: 0 additions & 4 deletions
This file was deleted.

lib/exception_notifiable.rb

Lines changed: 0 additions & 99 deletions
This file was deleted.

lib/exception_notifier.rb

Lines changed: 12 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,16 @@
1-
require 'pathname'
1+
require 'action_dispatch'
2+
require 'exception_notifier/notifier'
23

3-
# Copyright (c) 2005 Jamis Buck
4-
#
5-
# Permission is hereby granted, free of charge, to any person obtaining
6-
# a copy of this software and associated documentation files (the
7-
# "Software"), to deal in the Software without restriction, including
8-
# without limitation the rights to use, copy, modify, merge, publish,
9-
# distribute, sublicense, and/or sell copies of the Software, and to
10-
# permit persons to whom the Software is furnished to do so, subject to
11-
# the following conditions:
12-
#
13-
# The above copyright notice and this permission notice shall be
14-
# included in all copies or substantial portions of the Software.
15-
#
16-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23-
class ExceptionNotifier < ActionMailer::Base
24-
@@sender_address = %("Exception Notifier" <[email protected]>)
25-
cattr_accessor :sender_address
26-
27-
@@exception_recipients = []
28-
cattr_accessor :exception_recipients
29-
30-
@@email_prefix = "[ERROR] "
31-
cattr_accessor :email_prefix
32-
33-
@@sections = %w(request session environment backtrace)
34-
cattr_accessor :sections
35-
36-
self.template_root = "#{File.dirname(__FILE__)}/../views"
37-
38-
def self.reloadable?() false end
39-
40-
def exception_notification(exception, controller, request, data={})
41-
content_type "text/plain"
42-
43-
subject "#{email_prefix}#{controller.controller_name}##{controller.action_name} (#{exception.class}) #{exception.message.inspect}"
44-
45-
recipients exception_recipients
46-
from sender_address
47-
48-
body data.merge({ :controller => controller, :request => request,
49-
:exception => exception, :host => (request.env["HTTP_X_FORWARDED_HOST"] || request.env["HTTP_HOST"]),
50-
:backtrace => sanitize_backtrace(exception.backtrace),
51-
:rails_root => rails_root, :data => data,
52-
:sections => sections })
4+
class ExceptionNotifier
5+
def initialize(app, options = {})
6+
@app, @options = app, options
537
end
548

55-
private
56-
57-
def sanitize_backtrace(trace)
58-
re = Regexp.new(/^#{Regexp.escape(rails_root)}/)
59-
trace.map { |line| Pathname.new(line.gsub(re, "[RAILS_ROOT]")).cleanpath.to_s }
60-
end
61-
62-
def rails_root
63-
@rails_root ||= Pathname.new(RAILS_ROOT).cleanpath.to_s
64-
end
65-
9+
def call(env)
10+
@app.call(env)
11+
rescue Exception => exception
12+
(env['exception_notifier.options'] ||= {}).reverse_merge!(@options)
13+
Notifier.deliver_exception_notification(env, exception)
14+
raise exception
15+
end
6616
end

lib/exception_notifier/notifier.rb

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
require 'action_mailer'
2+
require 'pp'
3+
4+
class ExceptionNotifier
5+
class Notifier < ActionMailer::Base
6+
self.mailer_name = 'exception_notifier'
7+
self.view_paths << "#{File.dirname(__FILE__)}/views"
8+
9+
class << self
10+
def default_sender_address
11+
%("Exception Notifier" <[email protected]>)
12+
end
13+
14+
def default_exception_recipients
15+
[]
16+
end
17+
18+
def default_email_prefix
19+
"[ERROR] "
20+
end
21+
22+
def default_sections
23+
%w(request session environment backtrace)
24+
end
25+
26+
def default_options
27+
{ :sender_address => default_sender_address,
28+
:exception_recipients => default_exception_recipients,
29+
:email_prefix => default_email_prefix,
30+
:sections => default_sections }
31+
end
32+
end
33+
34+
def exception_notification(env, exception)
35+
@env = env
36+
@exception = exception
37+
@options = (env['exception_notifier.options'] || {}).reverse_merge(self.class.default_options)
38+
@controller = env['action_controller.instance']
39+
@request = ActionDispatch::Request.new(env)
40+
@backtrace = clean_backtrace(exception)
41+
@sections = @options[:sections]
42+
data = env['exception_notifier.exception_data'] || {}
43+
44+
data.each do |name, value|
45+
instance_variable_set("@#{name}", value)
46+
end
47+
48+
content_type "text/plain"
49+
50+
prefix = "#{@options[:email_prefix]}#{@controller.controller_name}##{@controller.action_name}"
51+
subject "#{prefix} (#{@exception.class}) #{@exception.message.inspect}"
52+
53+
recipients @options[:exception_recipients]
54+
from @options[:sender_address]
55+
56+
render "#{mailer_name}/exception_notification"
57+
end
58+
59+
private
60+
def clean_backtrace(exception)
61+
Rails.respond_to?(:backtrace_cleaner) ?
62+
Rails.backtrace_cleaner.send(:filter, exception.backtrace) :
63+
exception.backtrace
64+
end
65+
end
66+
end
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<% filtered_env = @request.filtered_env -%>
2+
<% max = filtered_env.keys.max { |a, b| a.length <=> b.length } -%>
3+
<% filtered_env.keys.sort.each do |key| -%>
4+
* <%= "%-*s: %s" % [max.length, key, filtered_env[key].to_s.strip] %>
5+
<% end -%>
6+
7+
* Process: <%= $$ %>
8+
* Server : <%= `hostname -s`.chomp %>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
* URL : <%= @request.url %>
2+
* IP address: <%= @request.remote_ip %>
3+
* Parameters: <%= @request.fitered_params.inspect %>
4+
* Rails root: <%= Rails.root %>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* session id: <%= @request.session['session_id'].inspect %>
2+
* data: <%= PP.pp @request.session.inspect.gsub(/\n/, "\n ").strip %>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
A <%= @exception.class %> occurred in <%= @controller.controller_name %>#<%= @controller.action_name %>:
2+
3+
<%= @exception.message %>
4+
<%= @backtrace.first %>
5+
6+
<%= @sections.map { |section|
7+
summary = render(section).strip
8+
unless summary.blank?
9+
title = render("title", :locals => { :title => section }).strip
10+
"#{title}\n\n#{summary.gsub(/^/, " ")}\n\n"
11+
end
12+
}.join %>

0 commit comments

Comments
 (0)