Extracting user-friendly exception details in Java
I've got a J2EE web application that I'm working on and when an exception occurs, I'd like to get some basic details about the exception and log it. The message that I'm logging should be pretty basic, something that might mean something to the people running the web server(s).
Would using e.getMessage()
开发者_高级运维 be the best thing to log? Thanks.
Possibly. Problem is that without at least the calling method info, this isn't terribly helpful
Consider something like the following
/**
* Returns <i>class.method:linenumber</i> of the caller (or, more accurately
* the caller of the caller).
* </p>
*
* <p>
* Returns unknown if stacktrace is mucked up. Uses reflection and string
* concatenation, so don't overuse this for trivial matters. For exception
* handling and for logging, on the other hand, it is useful.
*
* @return method name of caller's caller and line number (String)
*/
public static String returnCaller( Class ignoreMe )
{
String ignoreClass = ignoreMe.getName();
StackTraceElement[] steArr = new Throwable().getStackTrace();
if (steArr != null)
{
// subscript 1 is returnCaller().
// subscript 2 is the caller of returnCaller()
// subscript 3 is the caller of the caller of returnCaller()...
for( int i = 0; i < steArr.length; i++)
{
if (steArr[i] == null)
break;
String myclass = steArr[i].getClassName();
if (myclass.equals(ErrorHandle.class.getName()))
continue;
if (ignoreClass.equals(myclass))
continue;
return steArr[i].getClassName()
+ "."
+ steArr[i].getMethodName()
+ ":"
+ steArr[i].getLineNumber();
}
}
return "unknown";
}
I have done something very similar. But first, you do not need to relay what the error is to the user, at all. It makes no sense to tell them how the error occurred since you will be logging it anyway. (Does it make sense if the user knows the error occurred because of a NPE or an IO error, even if you rename it to more 'friendly' terms) ?
First you may want to consider re-throwing all of your Exceptions as runtime (unchecked) exceptions. This will force errors to be logged centrally and consistently. Else you will have to hope that each and every instance of your error handling code will log it correctly. To re-throw your SQLExceptions for example:
try{
conn.close();
} catch(SQLException ex){
//logger.error("Cannot close connection"); //don't log this here, do it centrally
throw new RuntimeException(ex);
}
You will want to forward the users to a general error page simply telling them an error has happened. This indicates to them that whatever they were doing wasn't processed. This is very important so that they don't hit back and keep trying to enter a payment over and over again. let them know something has gone wrong and to either (a) contact the helpdesk or (b) place a form inside the default error page that can log a ticket or send an email to the helpdesk. To accomplish this you will need to define this in your web.xml:
<error-page>
<error-code>500</error-code>
<location>/Error.jsp</location>
</error-page>
Next, inside the error.jsp page (or servlet), log the error here if your container allows you to reference the stack trace as request.getAttribute("javax.servlet.error.message"));
.
OR, create a filter that acts only on the response, and like the following:
try {
chain.doFilter(req, res);
} catch (Exception e) {
logger.error("pass any Mapped Diagnostic Context here", e); //log here, centrally
throw new RuntimeException(e); //throws back to the container so that the error page will be seen
}
This will catch all propagated exceptions (unchecked and uncaught), log them, then re-throw them so that they invoke the error page. This will make error reporting
- Accurate (one log per instance of an error)
- Reliable (if the user received the error, you receive the logging details
- Consistent (standardized if you implement re-throwing checked exceptions as unchecked, you will be able to see all exceptions and also prevent users from trucking along into error-prone territory, where you risk disastrous data-cleanup missions because they inserted crap into tables or left key tables unpopulated)
Finally, you may want to consider using log4j and using an appender that outputs to somewhere you can review errors without having to wait on the operations teams to send them to you. Much easier that way if you are routinely maintaining the application.
Logging just the exception message isn't a good idea; you lose what line the exception occurred in, which makes finding a logged exception in the code a nightmare most times.
Log the whole exception/stack trace.
I default to getting the whole stacktrace, that way I can see more of the context of what was going on when the error occurred.
If you use log4j and pass the exception into one of its logging methods it will print whatever message you give it, plus the exception's message and its stacktrace for you:
log.error("caught an exception", throwable);
If you have error-prone things like calls to web services, you could wrap exceptions thrown in an exception that you assign a very clear message to, so when the admins see it that message will tell them what's going on.
For that matter, you could keep separate logs, one with stacktraces for the developers and another that you write admin-friendly messages to. You could have an exception handler that tries to map the exception to a friendly message, then write that message to an admin log.
If you just want to inform the sysadmins, that something went wrong, but no dev has to look at it, because it is some temporary known problem, or can be solved by the sysadmin, a good descriptive message should suffice. In all other cases every dev would be very thankful to get a complete stacktrace plus , if available, the state of the system (input parameter etc. ...) at the time of the exception.
精彩评论