Handling Rails errors: custom error pages, logging, notification

I wanted to improve error handling In my Rails applications. Specifically, I wanted three things:

  • a custom error page that matched the site's design
  • the errors to appear in the error log (production.log)
  • email notification of server errors

Note: these steps are for Rails 2.3.2–2.3.8. Other versions may require different instructions.

Custom error page

Normally, when server error occurs, Rails renders public/500.html. You can customize this. However, if you include your site's layout information here, you violate the DRY principle.

  1. Create a directory for error templates mkdir app/views/errors
  2. Create the error file: touch app/views/errors/500.html.erb
  3. Remove the static file: rm public/500.html
  4. In application_controller.rb, override render_optional_error_file:
    protected def render_optional_error_file(status_code) render :template => "errors/500", :status => 500, :layout => 'application' end
  5. To test your error handler in the development environment, in application_controller.rb, add this line: alias_method :rescue_action_locally, :rescue_action_in_public be sure to comment this out when you're done, so you can see backtraces.

Logging errors

Because we overrode render_optional_error_file instead of using rescue_from (2), errors still go to the log (production.log).

Email notification

There's a terrific exception notification plugin that handles this for you.

This is a bit confusing, because there are three versions of the plugin floating around:

  1. The master version on github, for Rails 3
  2. A branch on github, for Rails 2.3
  3. The old version, which also works with Rails 2

Important If you install via the standard method (script/plugin install), you'll get an old version of the plugin, and the current instructions (on github) won't work. (See below for legacy instructions.)

If you install from github, you'll get a version for Rails 3 by default, and that won't work, either.

Here's what to do:

  1. Install the plugin from the 2-3-stable branch from github: script/plugin install git://github.com/rails/exception_notification.git -r 2-3-stable
  2. In application_controller.rb, place this at the top: include ExceptionNotification::Notifiable
  3. In environment.rb, add these lines at the very end, after the |config| block:
    ExceptionNotification::Notifier.exception_recipients = %w([email protected]) ExceptionNotification::Notifier.sender_address = %("Application Error" <[email protected]>) ExceptionNotification::Notifier.email_prefix = "[Fancy App] "
  4. To have sendmail send email (instead of using raw SMTP), add this line to environment.rb: ActionMailer::Base.delivery_method = :sendmail

Legacy exception_notification instructions

  1. Install the plugin: script/plugin install exception_notification
  2. In application_controller.rb, place this at the top: include ExceptionNotifiable
  3. In environment.rb, add these lines at the very end, after the |config| block:
    ExceptionNotifier.exception_recipients = %w([email protected]) ExceptionNotifier.sender_address = %("Application Error" <[email protected]>) ExceptionNotifier.email_prefix = "[Fancy App] "

Join the Conversation

8 Comments

  1. Thanks for the post! After you put this in place, did you find that exception notification emailed you for 404s? I used what you did, but overwrote render_optional_error_file in a slightly different way and started getting notifications on 404s. Here’s what I did:

      def render_optional_error_file(status_code)
        status = interpret_status(status_code)[0,3]    
        if ["404", "422", "500"].include?(status)
          render :template => "/errors/#{status}.html.erb", :status => status
        else
          render :template => "/errors/unknown.html.erb", :status => status
        end
      end
    
  2. Oh, I had effectively disabled 404s by redirecting everyone home:
    map.connect '*path', :controller => :home, :action => :error

    Your idea seems better.

  3. Ah, gotcha. The problem with my approach was that I would get emailed on all 404s as well, which was driving me crazy. I narrowed the problem down to a method in exception notification:


    def deliver_exception_notification?
    !self.class.skip_exception_notifications? && ![404, "404 Not Found"].include?(response.status)
    end

    I overrode this method in my application_controller by simply changing 404 (int) to “404” (string). That worked (I’m getting emails on all errors now except for 404s), but I’m trying to figure out if that’s really a bug in exception notification and, if so, if I handled it correctly.

Leave a comment

Your email address will not be published. Required fields are marked *