Manage Unhandled Exceptions
Learn how to manage the unhandled exceptions in our Rails application.
We'll cover the following
Exception handling
When an exception happens that is not rescued explicitly by our code, it bubbles up a large call stack inside Rails for some sort of handling. If the code was initiated by a controller, Rails will render a default HTTP 500 error. If the code was started by a Rake task, nothing special will happen. If run from a background job, it might be retried, or it might not, it depends. In any case, we need to be able to view and examine these unhandled exceptions because they indicate a problem with our app.
Certainly, unhandled exceptions aren’t business outcomes, but they are a useful bit of telemetry to explain what’s happening with our app. Often, unhandled exceptions indicate bugs in the app that need to be fixed to avoid creating confusion later when we have to diagnose a real failure. For example, if we communicate with a third-party API, we’ll certainly get a handful of network timeouts. Our jobs will retry themselves to recover from these transient network errors. We don’t need to be alerted when this happens.
Tracking unhandled exceptions isn’t something our Rails app can do on its own. While the log will show exceptions and stack traces, the log isn’t a great mechanism for notifying us when exceptions occur or allowing us to analyze the exceptions that are happening over time. We need an exception-handling service.
There are many such services, such as Airbrake, Bugsnag, or Rollbar. They are all more or less equivalent, though there are subtle differences that might matter to us, so do your research before choosing one (though the only wrong choice is not to use one). Most of these services require adding a RubyGem to our app, adding some configuration, and placing an API key in the UNIX environment.
They tend to work by registering a Rails Middleware that catches all unhandled exceptions and notifies the service with relevant information. This information can be invaluable because it can include browser user agents, request parameters, request IDs, or custom metadata we provide. Often, we can view a specific exception in the service we’ve configured, find the request ID, and then look at all the logs related to the request that lead to the exception.
We can’t give specific guidance because it will depend on the service we’ve chosen, but here are some tips for getting the most out of our exception-handling service:
- Learn how the service we’ve chosen works. Learn how they intend their service to be used and use it that way. While the various services are all mostly the same, they differ in subtle ways, and if we try to fight them, we won’t get a lot of value out of the service.
- Try very hard to not let the inbox of unhandled exceptions build up. We want each new exception to be something we both notice and take action on. This will require an initial period of tuning our configuration and the service’s settings to get it right, but ideally, we want a situation where any new notification from the service is actionable and important.
- If the service allows it, try to include additional metadata with unhandled exceptions. Often, we can include the current user’s ID, the request ID we discussed above, or other information that the exception handling service can show us to help figure out why the exception happened.
- Intermittent exceptions are particularly annoying because we don’t necessarily need to know about each one, but if there are too many, we do. Consult our service’s documentation for how to best handle this. We need to be very careful to not create alert fatigue by creating a situation where we are alerted frequently by exceptions that we can ignore.
In addition to having access to view and manage unhandled exceptions, it’s helpful to be able to measure the performance of our app.
Get hands-on with 1400+ tech skills courses.