Emailing Application Exceptions
Problem
During development, you watch the log closely as you exercise new features. When your application fails, you usually see it in the logs and in your browser. Once the application moves to production, the burden of reporting errors often falls on your users. This is far from ideal. If an error occurs, you want to be the first one on the scene with a fixif possible, even before the users notice. To make this kind of awareness feasible, you want the application to send an email when critical exceptions are thrown.
Solution
Install the exception notification plug-in and have critical application errors emailed to your development team. From the root of your application, run:
$ ruby script/plugin install \
> http://dev.rubyonrails.com/svn/rails/plugins/exception_notification/
With the plug-in installed, the next step is to mix-in the plug-in's ExceptionNotifiable module by adding include ExceptionNotifiable to the controllers that you want to send exception notifications. To enable this behavior application-wide, put this line in application.rb:
app/controllers/application.rb:
class ApplicationController < ActionController::Base
include ExceptionNotifiable
#...
end
The remaining step is to specify one or more recipients for the emails in environment.rb:
config/environment.rb:
ExceptionNotifier.exception_recipients = %w(rob@railscookbook.org
bugs@railscookbook.org)
By default, the plug-in does not send email notifications for local requests, that is, requests with an IP address of 127.0.0.1. (The assumption is that local requests are coming from a developer, who should be watching the logs.) If you want the plug-in to send notifications for exceptions that occur while handling local requests, and you are in development mode, set the following config option in environments/development.rb to false:
environments/development.rb:
config.action_controller.consider_all_requests_local = false
If your application is not running in development mode, this option is likely set to TRue. In any case, setting it to false allows you to override what Rails considers a local request. The following line effectively tells the plug-in that no addresses are to be considered local:
app/controllers/application.rb:
class ApplicationController < ActionController::Base
include ExceptionNotifiable
local_addresses.clear
#...
end
On the other hand, if you want to expand the definition of local to include a specific IP address to list of addresses, you can pass them to consider_local in your controller:
consider_local "208.201.239.37"
Discussion
After restarting the server, the next time your application throws a critical exception an email with the exception name and environment details is emailed to the address you specified. The following Rails exceptions are not considered critical and will result in HTTP 404 errors: RecordNotFound, UnknownController, and UnknownAction. All other errors result in an HTTP 500 response, and an email is sent.
To test the plug-in, you can throw a specific exception that you know will trigger the notification mechanism. For example, create a Test Controller with an index action that tries to divide by zero.
app/controller/test_controller.rb:
class TestController < ApplicationController
def index
1/0
end end
Web requests to this action will throw a ZeroDivisionError exception and send an email that will look something like this:
From: Application Error <app.error@localhost>
To: rob@orsini.us Subject: [APP] test#index (ZeroDivisionError) "divided by 0"
Content-Type: text/plain; charset=utf-8
A ZeroDivisionError occurred in test#index:
divided by 0
[RAILS_ROOT]/app/controllers/test_controller.rb:4:in `/'
-------------------------------
Request:
-------------------------------
* URL: http://localhost:3000/test
* Parameters: {"action"=>"index", "controller"=>"test"}
* Rails root: /Users/orsini/rails-cookbook/recipes/Debugging/
Emailing_Application_Exceptions
-------------------------------
Session:
-------------------------------
* @new_session: false
* @data: {"flash"=>{}}
* @session_id: "ab612d8b4e83664a1d7c1f52bea87ef4"
-------------------------------
Environment:
-------------------------------
* GATEWAY_INTERFACE : CGI/1.2
* HTTP_ACCEPT : text/xml,application/xml,application/xhtml+xml,
text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
...
-------------------------------
Backtrace:
-------------------------------
[RAILS_ROOT]/app/controllers/test_controller.rb:4:in `/'
[RAILS_ROOT]/app/controllers/test_controller.rb:4:in `index'
...
To configure the sender address of the emails, set the sender_address for your environment:
ExceptionNotifier.sender_address =
%("Application Error" <app.error@yourapp.com>)
You can also configure the prefix of the subject line in the emails sent, with:
ExceptionNotifier.email_prefix = "[YOURAPP] "
The plug-in comes with a nice facility for configuring the body of the email. To override the default message, create specially named partials in a directory named app/views/exception_notifier. The default email body contains four sections, as defined in this line from the ExceptionNotifiable module definition:
@@sections = %w(request session environment backtrace)
You can customize the order or even the format of these sections. To change the order and exclude the backtrace section, for example, add this line to your environment configuration:
ExceptionNotifier.sections = %w(
request environment session
) So, to override the layout of the request
section, you create a file named _request.rhtml and
place it in app/views/exception_notifier. The
following variables (from the plug-in's RDoc) are available to use
within your customized templates:
@controller
-
The controller that caused the error
request
-
The current request object
@exception
-
The exception that was raised
@host
-
The name of the host that made the request
@backtrace
-
A sanitized version of the exception's backtrace
@rails_root
-
A sanitized version of RAILS_ROOT
@data
-
A hash of optional data values that were passed to the notifier
@sections
-
The array of sections to include in the email
By creating the following partial, the environment section of the
notification email displays only the address of the host the request
originated from and the user agent: app/views/exception_notifier/_environment.rhtml:
* REMOTE_ADDR : <%= request.env['REMOTE_ADDR'].to_s %>
* HTTP_USER_AGENT : <%= request.env['HTTP_USER_AGENT'].to_s %>
Such a partial produces this environment section within the
email:
-------------------------------
Environment:
-------------------------------
* REMOTE_ADDR : 127.0.0.1
* HTTP_USER_AGENT : Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O;
en-US; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4
See Also
|