Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine logging with an exception handling chain?

Suppose I have the following code:

void foo() {
    /* ... */
    try {
        bar(param1);
    } catch (MyException e) {
        /* ??? */
    }
}

void bar(Object param1) throws MyException {
    /* ... */
    try {
       baz(param2);
    } catch (MyException e) {
        /* ??? */
    }
}

void baz(Object param2) throws MyException {
    /* ... */
    if (itsAllATerribleMistakeOhNo) {
        /* ??? */
        throw new MyException("oops, error.");
    }
}

I'm wondering where and how I should be logging the error.

  • Where the error occurs, below, in baz(), I know exactly what operation went awry and can log that fact.
  • At the top I have the most general context (e.g. what's the IP of the connection during whose handling we encountered the error.)
  • Along the way I might have some context which isn't known either at the top or at the bottom.

Another complication is that the error at the bottom might not really be considered an error when you look at it from the top (e.g. looking up something in a database fails; maybe you weren't sure ) - so I might choose to logger.WARN() instead of logger.ERROR().

So, above I described 3 locations (bottom, top, and along the way) - but it's not just a question of where to log, but also what to throw up. At every level in the middle, you have 2x2 options:

  • Log/Don't log a message
  • Throw the original exception / wrap the exception in a new exception with the added message.

What are the best practices, or some common wisdom, regarding these complex choices?

Note: I'm not asking about error handling/exception use in general, just about the dilemmae described above.

like image 854
einpoklum Avatar asked Mar 04 '13 08:03

einpoklum


3 Answers

When it comes to logging, I prefer to keep all my logging at the top at the application boundary. Usually I use an interceptor or filter to handle all logging in a general way. By this concept, I can guarantee that everything is logged once and only once.

In this case, you would log inside your foo() method or whatever the entry point to your application is (you mentioned the IP address, I suppose we are talking about a servlet container or application server).

Than, catch your own exception in the filter/interceptor and log it depending on your needs. Add a catch throwable to catch all other exceptions that you did not handle in your code and log them as an error, since obviously you missed something further down in the stack trace.

This concept requires some planning ahead. You will probably use your own ApplicationException that stores the Error Message (String) along with some severity level (probably in an Enum). You need this to choose the correct log level when you do the actual logging.

This works well for all cases and has the advantage that all logging is happening exactly once. However, there is one case where you still need logging in your code: if you can fully deal with an error somewhere in your code (that is, an exception happens and you can do something that allows you to continue working without (re)throwing an exception). Since your are not throwing an exception, nothing would be logged otherwise.

To sum it up:

  • Log at the topmost position in a general way, preferably using an interceptor or filter.
  • Wrap exceptions inside your own ApplicationExceptions and add severity plus other things of interest for logging in your application.
like image 178
phisch Avatar answered Sep 19 '22 14:09

phisch


Some suggestions that I tend to follow:

Link for some best practices

1) Trace the exception where it occurs. As the point where the exception occurs if the class or API knows the context in which the exception occurs then tracing and providing a proper log is better. But if the API cannot handle or comment on the exact context then API should not log the event and leave it on the caller.

2) Wrapping the exceptions : When there are lot of exceptions that can be thrown and all exceptions form a similar group (SQLException) which provides single exception and lets you to extract information if needed. Otherwise there would have been an explosion of exceptions that the caller needs to handle.

3) Re-Throwing the exceptions: If the API logs the exception and user can take some actions on that then the Exception MUST be rethrown to tell the user that some error condition occured.

4) Proper cause of exception : The exception message should not be too techy for the caller to understand, the message itself should guide the user to understand the underlying reason for the exception.

UPDATE: Exception Management in Java

like image 25
Narendra Pathai Avatar answered Sep 19 '22 14:09

Narendra Pathai


When I throw Exceptions in my code, I do not usually log anything. The exception is information enough. The only exception to this is, when I am at the border of my system, that is, when the exception will leave the boundary of my system, then I log as I am not sure what the other system will do with the error.

When I handle exceptions, I log them when I actively handle them, that means when I am in a catch clause which does something more then just rethrowing the exception. Usually this is rather at the top, but this depends on the situation.

like image 33
Matthias Avatar answered Sep 22 '22 14:09

Matthias