Rescuing exceptions without notifications or: How to fail without knowing

Brian Landau, Former Developer

Article Category: #Code

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 ExceptionStandardErrorTimeoutErrorIOErrorRuntimeError, 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.

Related Articles