#104 Exception Notifications (revised)
As your Rails applications become more complex exceptions are bound to happen. Below, however, we have a very simple application with a big, red button on it.
This simple application raises an exception when we click the big red button.
When an exception is raised in a production application the end user won’t see this much detail. Instead they’re shown a generic error page. Our goal as developers should be to ensure that this page is never shown so to help weed out errors we need to be notified whenever an exception is raised.
There are a variety of ways of adding exception notifications to an application but we’re going to use the Exception Notification gem. This is a classic plugin but it’s being well maintained by Sebastian Martinez. It’s easy to set up and works by sending an email each time an exception is raised. As it’s a piece of Rack middleware it’s easy to configure, too. All we need to do is add one line to the relevant environment’s config file.
Installing Exception Notification
Installing Exception Notification is simple. All we need to do is add it to the
Gemfile and run
source 'http://rubygems.org' gem 'rails', '3.1.1' gem 'sqlite3' # Gems used only for assets and not required # in production environments by default. group :assets do gem 'sass-rails', '~> 3.1.4' gem 'coffee-rails', '~> 3.1.1' gem 'uglifier', '>= 1.0.3' end gem 'jquery-rails' gem 'exception_notification'
Once it has installed we can configure it in one of the config files in the
/config/environment directory. Normally we’d add it to the
production.rb file, but as we’re just trying it out we’ll add it to
development.rb first to see how it works. At the bottom of the file’s block we’ll add the following line of code.
config.middleware.use ExceptionNotifier, sender_address: 'email@example.com', exception_recipients: 'firstname.lastname@example.org'
There are a number of options we could pass in here but for now we’ll just set the
exception_recipients. We’ve used a simple string for the recipients but if we want to send email to more than one address we can use an array.
ExceptionNotifier sends email we need to configure
action_mailer.delivery_method. Setting up a delivery method in development mode can be awkward so we’ll use a gem called Letter Opener to make this easier. This gem opens any emails that the application tries to send in a web browser. As before we add this gem to the
Gemfile and run
bundle to install it.
gem 'letter_opener', group: :development
With this gem installed we can use
:letter_opener as the delivery method.
config.action_mailer.delivery_method = :letter_opener
If we press the big button in our application now the exception is shown again but this time another window opens showing the email that would be sent.￼
This email contains full information about the exception that was raised. Note that the title will contains the word
[ERROR] so that we can easily filter them out into a separate folder.
Customizing Exception Notifier
There are a number of ways we can customize the Exception Notifier middleware. For example we can add options to specify the email prefix (instead of the default
[ERROR]) or change the content of the email by passing in a
:sections option . We can even add our own sections containing any additional information we want to include. We won’t change any of these now but there is another option that isn’t well documented that we will change. Before we do we’ll take a look at relevant part of the source code to see how it works. The
ExceptionNotifier class contains an option called
default_ignore_exceptions and the source code for this file lists the exceptions that Exception Notifier will ignore.
def self.default_ignore_exceptions .tap do |exceptions| exceptions << ::ActiveRecord::RecordNotFound if defined? ::ActiveRecord::RecordNotFound exceptions << ::AbstractController::ActionNotFound if defined? ::AbstractController::ActionNotFound exceptions << ::ActionController::RoutingError if defined? ::ActionController::RoutingError end end
These errors are ignored as they usually throw a 404 error rather than a 500. If our application throws an exception that we don’t want to receive an email about we can add it to the list of ignored exceptions. The exception that our application throws deliberately is a
RuntimeError so we’ll add this to the list.
config.middleware.use ExceptionNotifier, sender_address: 'email@example.com', exception_recipients: 'firstname.lastname@example.org', ignore_exceptions: ExceptionNotifier.default_ignore_exceptions + [RuntimeError]
We’ll need to restart the server for this change to take effect. After we do the exception will still be thrown when we click the big red button but we won’t receive an email. In a production application we wouldn’t want to ignore RuntimeErrors as they’re important but there are other exceptions that we could ignore, such as those raised when bots visit our application.
Now that we have Exception Notifier configured how we want it we need to move the code we’ve written in
development.rb over to
production.rb. We’ll also need to set up the delivery method in production mode so that sends real email rather than using Letter Opener.
That’s how we set up the Exception Notification gem. It’s easy to use and it works well. If you want a solution that doesn’t send email notifications there are a number of alternatives available. One of these is Whoops. This stores exception notifications in a Mongo database and presents them through a user interface. Another is Airbrake, previously known as Hoptoad. This is a paid solution but it offers more features such as Github integration.
If none of these solutions suit then you could always create your own by using a mountable engine. We showed you exactly how to do this in episode 277 and it’s really not hard to do.