What is a good way to pass useful state information to an exception in Java?

前端 未结 11 1870
说谎
说谎 2021-02-04 06:23

I noticed some confusion initially with my question. I\'m not asking about how to configure a logger nor how to use a logger properly, but rather how to capture all of the infor

11条回答
  •  生来不讨喜
    2021-02-04 07:06

    As for the type of debug information you need, why don't you just always log the value and don't bother so much with a local try/catch. Just use the Log4J config file to point your debug messages to a different log, or use chainsaw so you can remotely follow the log messages. If all that fails maybe you need a new log message type to add to debug()/info()/warn()/error()/fatal() so you have more control over which messages get sent where. This would be the case when defining appenders in the log4j config file is impractical due to the high number of places where this type of debug logging needs to be inserted.

    While we're on the subject, you've touched on one of my pet peeves. Constructing a new exception in the catch block is a code smell.

    Catch(MyDBException eDB)
    {
        throw new UnhandledException("Something bad happened!", eDB);
    }
    

    Put the message in the log and then rethrow the exception. Constructing Exceptions is expensive and can easily hide useful debugging information.

    First off, inexperienced coders and those who like to cut-n-paste (or begin-mark-bug, end-mark-bug, copy-bug, copy-bug, copy-bug) it can transform easily to this:

    Catch(MyDBException eDB)
    {
        throw new UnhandledException("Something bad happened!");
    }
    

    Now you've lost the original stacktrace. Even in the first case, unless the wrapping Exception handles the wrapped exception properly, you can still lose details of the original exception, the stacktrace being the most likely.

    Rethrowing exceptions might be necessary but I've found that it should be handled more generally and as a strategy to communicate between layers, like between your business code and the persistance layer, like so:

    Catch(SqlException eDB)
    {
        throw new UnhandledAppException("Something bad happened!", eDB);
    }
    

    and in this case, the catch block for the UnhandledAppException is much further up the call stack where we can give the user an indication that they either need to retry their action, report a bug, or whatever.

    This let our main() code do something like this

    catch(UnhandledAppException uae)
    {
        \\notify user
        \\log exception
    }
    catch(Throwable tExcp)
    {
        \\for all other unknown failures
        \\log exception
    
    }
    finally
    {
        \\die gracefully
    }
    

    Doing it this way meant that local code could catch the immediate and recoverable exceptions where debug logs could be done and the exception not have to be rethrown. This would be like for DivideByZero or maybe a ParseException of some sort.

    As for "throws" clauses, having a layer-based exception strategy meant being able to limit the number of exception types that have to be listed for each method.

提交回复
热议问题