6 Tips to Improve Your Exception Handling

Getting exception handling right can save you hours (or even days) of troubleshooting.  Unexpected production issues can ruin your dinner and weekend plans.  They can even affect your reputation if not resolved quickly.  Having a clear policy on how to manage exceptions will save you time diagnosing, reproducing, and correcting issues.  Here are 6 tips to improve your exception handling.

1. Use a single, system-wide exception class

Instead of creating separate classes for each exception type, create just one.  And make it extend RuntimeException.  This will reduce your class count and remove the need to declare exceptions you aren’t going to handle anyway.

I know what you’re thinking: How will I tell exceptions apart if they’re all the same type?  And how will I track type-specific properties?  Read on!

2. Use enums for error codes

Most of us were trained to put the cause of an exception into its message.  This is fine when reviewing log files (ugh), but it does have drawbacks:

  1. Messages can’t be translated (unless you’re Google).
  2. Messages can’t be easily mapped to user-friendly text.
  3. Messages can’t be inspected programmatically.

Putting info in the message also leaves the wording up to each developer, which can lead to different phrases for the same failure.

6 tips to improve your exception handling

A better approach is to use enums to indicate the exception’s type.  Create one enum for each category of errors (payments, authentication, etc.).  Make the enums implement an ErrorCode interface and reference it as a field in the exception.

When throwing exceptions, simply pass in the appropriate enum.

Now when you need to test for a specific case, just compare the exception’s code with the enum.

User-friendly, internationalized text can now be retrieved by using the error code as the resource bundle’s lookup key.

3. Add error numbers to enums

In some cases a numerical error code can be associated with each exception. HTTP responses for example. For those cases, add a getNumber method to the ErrorCode interface and implement it in each enum.

Numbering can be globally unique across all enums or each enum can be responsible for numbering itself. You can even use the implicit ordinal() method or load numbers from a file or database.

4. Add dynamic fields to your exceptions

Good exception handling means also recording relevant data, not just the stack trace. Doing this will save you big time when trying to diagnose and reproduce errors.  And customers won’t have to tell you what they were doing when your app stopped working (you’ll already know and hopefully have fixed it).

The easiest way to do this is to add a java.util.Map field to the exception.  The new field’s job will be to hold all your exception related data by name.  You’ll also need to add a generic setter method following the fluent interface pattern.

Throwing exceptions, with relevant data, will now look something like the following.

5. Prevent unnecessary nesting

Long, redundant stack traces help no one. Even worse, they waste your time and resources.  When rethrowing exceptions, call a static wrap method instead of the exception’s constructor . The wrap method will be responsible for deciding when to nest exceptions and when to just return the original instance.

Your new code for rethrowing exceptions will look like the following.

6. Use a central logger with a web dashboard

Consider this tip a bonus. Depending on your situation, getting access to production logs could be quite a hassle. A hassle that may involve multiple go-betweens (since many developers don’t have access to production environments).

Things get worse if you’re in a multi-server environment. Finding the right server — or determining that the problem only affects one server — can be quite a headache.

My recommendations are:

  1. Aggregate your logs in a single place, preferably a database.
  2. Make that database accessible from a web browser.

There are many ways to do this and may products to choose from: log collectors, remote loggers, JMX agents, system monitoring software, etc. You can even build it yourself. The main thing is that you do it soon. Once you have it, you’ll be able to:

  • Troubleshoot issues in a matter of seconds.
  • Have a URL for each exception that you can bookmark or email around.
  • Enable your support staff to determine root causes without involving you.
  • Prevent testers from creating multiple tickets for the same bug.  Plus they’ll have an exception URL to put in their ticket.
  • Save money for your business.
  • Keep your weekend and reputation intact.

What are your Tips?

I hope you find these tips useful.  Many disasters and wasted hours have been averted by having the right info in my exceptions and having them in an easy to access location.  If you have a few exception handling tips of your own, I’d like to hear them.

Download

The exceptions download contains the entire source code (including Eclipse project). The source code is licensed under the terms of the Apache License, Version 2.0.

Happy coding!

Update May 21, 2014

My Java exception tracking tool is now available for download as a web app. Sign-up over at StackHunter.com to get access. No spam, I promise!

About Dele Taylor

Dele Taylor is the founder of NorthConcepts.com, creators of Data Pipeline -- a data migration library for Java. You can follow him on Twitter, G+, and LinkedIn.
Exceptions, Java | permalink

Comments

  1. Nice & clean :). Its a concrete approach!

  2. David

    Very well thought out. I like it.

  3. Obi

    Lovely approach and will put it to use right away!

  4. Alex

    There’s a good chapter in Effective Java on this topic. One of the chapters also discusses ”
    tagged classes”. Otherwise, good work getting more discussion going in better exception handling!

    • Dele Taylor

      Ouch, I guess SystemException could be considered a “tagged class”.

      In its defense, there’re no tag-specific behaviour in this class, like “switch” or “if” statements. And the errorCode field is an interface, not a direct reference to any enum.

      Thanks for the comments and the Effective Java mention.

  5. Kevin

    Excellent approach! I’ve been struggling with how to build a simple but usable REST exception handling process complete with logging to a mongodb for production use. I think this may just fit the bill nicely.

  6. Owen

    This is a good approach although I think one exception class might be a bit far, you could consider creating a base class and then sub exception classes with different Enums. This would allow you to have componentised development but still use the same base mechanism for handling errors.

    Also a very minor point and I’m sure just used for an example but CREDIT_CARD_EXPIRED is not really an exception as this is an expected and predictable state for a credit card to be in and should be handled by the “Main Path” of your program.
    This is my opinion, of course, but Exceptions should only be used for Exceptional circumstances (see a dictionary definition) otherwise you can end up throwing exceptions up several layers of your application because it is easy or using exception for logical flows.

    • Dele Taylor

      Owen — you’re right that a single exception class is taking things far. Probably too far for most Java developers to stomach. For the skeptical, I say try to keep an open mind. I’ve had a surprising amount of success with the single exception approach over the years.

      Having said that, I do like your recommendation. It’s a good middle ground. Most developers can accept it and it probably won’t break any of my production code :) As long as you’re recommending a SystemException subclass per subsystem (accounts, inventory, tickets) and not the class-per-error we see today, then I’m on board.

      Re: CREDIT_CARD_EXPIRED. IMO there’s a fine line between the main, alternate, and exceptional paths. (Is FileNotFoundException really an exceptional case???) I do agree with you that CREDIT_CARD_EXPIRED is questionable, like INVALID_USERNAME_OR_PASSWORD for example. A better example might have been something like UNSUPPORTED_CARD. Thanks for pointing this out.

  7. My framework does something pretty much similar but the part that translate the messages needs to be an interface because does not know anything about specifics enums. So, I have an error service which is initialized in the framework by the app code where it ‘mount’ all error codes to be translated in the future.

    Great article summarizing the technique ! congrats

    • Dele Taylor

      Nice approach. I like your decoupling of user messages from exceptions/error codes. This also let’s you mount the appropriate messages for the current user role. Internal agents might see a different message than partners or customers for the same exception.

      Thanks, I’ll be adding this to my arsenal.

  8. Pingback: 6 porad jak ulepszyć obsługę wyjątków

  9. Frisian

    #1 and #4 together make refactoring more difficult than necessary. Subclasses with dedicated getters and setters provide comile-time safety instead.
    For a meaningful I18N handling, there should be a method returning all fields as an object array. That would allow for using MessageFormat.
    The luxury version would include a facility to translate properties before using them to the MessageFormat. E.g., the text associated with an HTTP error code has to be translated in the first step before being included in a localized error message in the second step.
    Sub classes would make #3 simpler as well with generics being used to declare, which error code class will be returned from a particular sub class.

  10. Ted

    How do I know which map keys I can use?

    Example:

    try {
    someService.doSmth(); // here I don't know which implementation I call. Also, I didn't write that implementation.
    } catch (SystemException e) {
    if (e.getErrorCode() == PaymentCode.CREDIT_CARD_EXPIRED) {
    //now what?
    }
    }

  11. Pingback: » Enlaces de interés. 3era Búsqueda

  12. filt

    well ,I will put it to use the project

  13. Jean-Simon Larochelle

    Interesting article. However, I don’t agree about TIP #1. Doing this makes it impossible to use the Java language catch clause to selectively catch or ignore errors (you have to call methods on the exception to select exceptions and possibly rethrow). A better idea is to make the single exception type a base class and subtype that. This would let you define subtypes like FatalException whose handling can easily be customized. With this approach you can still use the other tips. Remember: one of the strength of Java is that it is a typed language. Not using types in Java is almost always an error.

  14. nimysan

    Very cool. I like this way SystemException.wrap(e).set('key', value)

    But I have one more question. Is there any way to output the log? You know we always need to support production but always can not have the permission to get the log directly. is it exist library to can to output log to web page with filter(like password hidden)? Thanks

    • Dele Taylor

      Thanks for the kudos Nimysan.

      I’ve read about a few log aggregators and cloud loggers, but haven’t personally used any. You can google “log servers” or “log management” to see a few.

      My approach in the past has been to:
      1. Filter out passwords in the SystemException.set method by excluding calls with the keys password, pwd, or passwd.
      2. Log all exceptions to database.
      3. Add a simple exception viewer to the website’s admin page.

      Hope that helps.

  15. Roger

    I’ve got a background in languages that don’t have exceptions and am trying to learn.
    I remain unconvinced that I should ignore the classes-as-types and use enums as types.
    Aren’t subclasses easier to maintain?

  16. dispenser

    good approach!!

  17. john

    I get two errors when trying to use this code.

    1. public enum WSCode implements ErrorCode

    error: Interface Expected Here

    2. public class ErrorCode {
    int getNumber();
    }

    error: Missing method body or declare abstract

    Any ideas; help!

    • Dele Taylor

      ErrorCode is defined as an interface in the zip file, not sure why you’re seeing a class.

      public interface ErrorCode {
      int getNumber();
      }

      Try changing your code to match the above, that should fix both issues.

      Dele

  18. john

    No such luck :(

    For the line: public enum DataAccessCode implements ErrorCode {

    I get the error: java: interface expected here

    I get an error for @Override

    java: method does not override or implement a method from a supertype

  19. Vikash

    Why your errorCode is an interface and why not class?

    • Dele Taylor

      I wanted each category of errorCodes to be represented by a different enum type. Since enums can implement interfaces, but not extend classes, that’s what I used.

  20. Toqeer

    I did same (with little variations) many years back and still using, so that we can automate error alerts. It gave many other benefits too.
    we had following additions:
    ErrorCategory (e.g. config, network, DB, Retryable)
    ErrorSverity (e.g. fatal, error, warn) in error codes.
    Overridden getMessage() for standard error message, which helped us parsing error logs.
    Global exception handler which looked at error properties and acted accordingly.
    Later this approach helped us in internationalisation too.
    I like your dynamic fields in exception, hope I’m allowed to borrow it :)
    Thanks for sharing.

    • Dele Taylor

      Hey Toqeer,

      Thanks for your comment and info. It’s good to hear that others have independently thought of these ideas.

      Yes, please use the dynamic fields in your own apps. Maybe one day something like this will get added to the JDK :-)

      Cheers.