Rescuing exceptions without notifications or: How to fail without knowing
Brian Landau, Former Developer
Article Category:
Posted on
Have you ever written code like this:
def my_method
begin
object.method_that_can_raise_exception
rescue Exception
# some exception handling
end
end
Whenever we write code like this we should stop ourselves and ask: “Is this an exception I need to know about?” Sometimes the answer is legitimately “no”, but there are important cases where you need to know about these caught exceptions.
For instance, sometimes you’re just rescuing a ActiveRecord::RecordNotFound
error, in these cases you often know exactly why the error happened, and you know exactly what needs to happen because of it. In these very specific cases you have excellent insight into what’s happening, and the error you’re rescuing is very specific, so silently swallowing these errors is fine.
However, there are some common situations where you need to be aware that exceptions are happening:
1. Rescuing a very generic exception class
If you’re going to be rescuing Exception
, StandardError
, TimeoutError
, IOError
, RuntimeError
, a descendant of SignalException
, or something equally generic, you should probably be notifying yourself of these. If you’re going to be rescuing something this generic, it’s a signal to yourself that you probably don’t know a lot about what can go wrong here or why. Capturing what went wrong is the first step to understanding and solving a problem.
2. Making a connection to an external service
If you’re putting a rescue around a network call to an external service (any service you don’t control, and perhaps maybe those you do control but are on separate servers), you should probably notify yourself that something went wrong. Similar to the situation above, you often have little insight (at the time you’re writing the code) into why it might fail. The external service may be down, or maybe your servers are having network issues. Whatever the reason, a remedy needs to be found, and the easiest way to start diagnosing the problem is with good insight into went wrong.
3. Mystery code
Perhaps you’re using someone else’s library, or maybe some code internal to your application was written long ago and is now legacy code. Whatever the reason, even if you’re using a more specific exception class, if you don’t know why you need to rescue the exception you should probably be notifying yourself that it’s happening.
How you notify yourself and track these errors is entirely up to you. We use Airbrake, but there are lots of options available including just using ActionMailer. Here’s an example of how I might use Airbrake to handle catching an error from an external service:
module APIClient
private
def api_call(method, options)
# network code to external service
rescue Exception => exception
Airbrake.notify(exception, {
:component => self.class.to_s,
:action => method,
:url => url_of_api_we_are_using,
:parameters => options,
:cgi_data => ENV
})
end
end
class MyAPI
include APIClient
def some_api_method(options)
# some other code perhaps
api_call('api_method', options)
end
end
With this notification, I’m trying to capture the name of the API (self.class.to_s
), the API endpoint/method called (method
), the exact URL we are hitting (url_of_api_we_are_using
), the options/parameters we’re passing to the API (options
), and any ENV
data that might be configured on the server. If you’re worried about any portion of this having sensitive data (the options
or the ENV
for instance), you should scrub that data first, or not include that data at all. If you’re constructing a large body to post (in an XML API for instance), it can be valuable to capture that too. With Airbrake, I’d probably put a large body like that in the hash I pass to :parameters
.
For errors not connecting to an external service, there’s often a lot less data to report. It might be as simple as this:
Airbrake.notify(exception, {
:component => self.class.to_s,
:cgi_data => ENV
})
You might, however, have something relevant to put in the :parameters
option.
Whatever service you choose and data you send though, it’s important that you get these notifications and investigate exactly what went wrong. You’ll thank yourself later.