How to log as much information as possible for an exception in Java?

120,387

Solution 1

The java.util.logging package is standard in Java SE. Its Logger includes an overloaded log method that accepts Throwable objects. It will log stacktraces of exceptions and their cause for you.

For example:

import java.util.logging.Level;
import java.util.logging.Logger;

[...]

Logger logger = Logger.getAnonymousLogger();
Exception e1 = new Exception();
Exception e2 = new Exception(e1);
logger.log(Level.SEVERE, "an exception was thrown", e2);

Will log:

SEVERE: an exception was thrown
java.lang.Exception: java.lang.Exception
    at LogStacktrace.main(LogStacktrace.java:21)
Caused by: java.lang.Exception
    at LogStacktrace.main(LogStacktrace.java:20)

Internally, this does exactly what @philipp-wendler suggests, by the way. See the source code for SimpleFormatter.java. This is just a higher level interface.

Solution 2

What's wrong with the printStacktrace() method provided by Throwable (and thus every exception)? It shows all the info you requested, including the type, message, and stack trace of the root exception and all (nested) causes. In Java 7, it even shows you the information about "supressed" exceptions that might occur in a try-with-resources statement.

Of course you wouldn't want to write to System.err, which the no-argument version of the method does, so instead use one of the available overloads.

In particular, if you just want to get a String:

  Exception e = ...
  StringWriter sw = new StringWriter();
  e.printStackTrace(new PrintWriter(sw));
  String exceptionDetails = sw.toString();

If you happen to use the great Guava library, it provides a utility method doing this: com.google.common.base.Throwables#getStackTraceAsString(Throwable).

Solution 3

It should be quite simple if you are using LogBack or SLF4J. I do it as below

//imports
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

//Initialize logger
Logger logger = LoggerFactory.getLogger(<classname>.class);
try {
   //try something
} catch(Exception e){
   //Actual logging of error
   logger.error("some message", e);
}

Solution 4

Something that I do is to have a static method that handles all exceptions and I add the log to a JOptionPane to show it to the user, but you could write the result to a file in FileWriter wraped in a BufeeredWriter. For the main static method, to catch the Uncaught Exceptions I do:

SwingUtilities.invokeLater( new Runnable() {
    @Override
    public void run() {
        //Initializations...
    }
});


Thread.setDefaultUncaughtExceptionHandler( 
    new Thread.UncaughtExceptionHandler() {
        @Override
        public void uncaughtException( Thread t, Throwable ex ) {
            handleExceptions( ex, true );
        }
    }
);

And as for the method:

public static void handleExceptions( Throwable ex, boolean shutDown ) {
    JOptionPane.showMessageDialog( null,
        "A CRITICAL ERROR APPENED!\n",
        "SYSTEM FAIL",
        JOptionPane.ERROR_MESSAGE );

    StringBuilder sb = new StringBuilder(ex.toString());
    for (StackTraceElement ste : ex.getStackTrace()) {
        sb.append("\n\tat ").append(ste);
    }


    while( (ex = ex.getCause()) != null ) {
        sb.append("\n");
        for (StackTraceElement ste : ex.getStackTrace()) {
            sb.append("\n\tat ").append(ste);
        }
    }

    String trace = sb.toString();

    JOptionPane.showMessageDialog( null,
        "PLEASE SEND ME THIS ERROR SO THAT I CAN FIX IT. \n\n" + trace,
        "SYSTEM FAIL",
        JOptionPane.ERROR_MESSAGE);

    if( shutDown ) {
        Runtime.getRuntime().exit( 0 );
    }
}

In you case, instead of "screaming" to the user, you could write a log like I told you before:

String trace = sb.toString();

File file = new File("mylog.txt");
FileWriter myFileWriter = null;
BufferedWriter myBufferedWriter = null;

try {
    //with FileWriter(File file, boolean append) you can writer to 
    //the end of the file
    myFileWriter = new FileWriter( file, true );
    myBufferedWriter = new BufferedWriter( myFileWriter );

    myBufferedWriter.write( trace );
}
catch ( IOException ex1 ) {
    //Do as you want. Do you want to use recursive to handle 
    //this exception? I don't advise that. Trust me...
}
finally {
    try {
        myBufferedWriter.close();
    }
    catch ( IOException ex1 ) {
        //Idem...
    }

    try {
        myFileWriter.close();
    }
    catch ( IOException ex1 ) {
        //Idem...
    }
}

I hope I have helped.

Have a nice day. :)

Solution 5

You can also use Apache's ExceptionUtils.

Example:

import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.log4j.Logger;


public class Test {

    static Logger logger = Logger.getLogger(Test.class);

    public static void main(String[] args) {

        try{
            String[] avengers = null;
            System.out.println("Size: "+avengers.length);
        } catch (NullPointerException e){
            logger.info(ExceptionUtils.getFullStackTrace(e));
        }
    }

}

Console output:

java.lang.NullPointerException
    at com.aimlessfist.avengers.ironman.Test.main(Test.java:11)
Share:
120,387
Steve Chambers
Author by

Steve Chambers

LinkedIn profile: linkedin.com/in/stepc

Updated on July 05, 2022

Comments

  • Steve Chambers
    Steve Chambers almost 2 years

    There's a common problem I've come across a few times when logging exceptions in Java. There seem to be various different types to deal with. E.g. some wrap other exceptions and some don't have a message at all - only a type.

    Most code I've seen logs an exception by using either getMessage() or toString(). But these methods don't always capture all the information needed to pinpoint the problem - other methods such as getCause() and getStackTrace() sometimes provide additional info.

    For example, the exception I'm looking at right now in my Eclipse Inspect window is an InvocationTargetException. The exception itself has no cause, no message, no stacktrace ... but the target from getCause() is InvalidUseOfMatchersException, with these details populated.

    So my question is: Given an exception of any type as an input, please provide a single method that will output a nicely formatted string containing all relevant information about the Exception (e.g. possibly recursively calling getCause() amongst other things?) Before posting, I was nearly going to have a stab at it myself but really don't want to reinvent the wheel - surely such a thing must have been done many times before...?

    Please don't point me at any particular logging or utility framework to do this. I'm looking for a fragment of code rather than a library since I don't have the right to add external dependencies on the project I'm working on and it's actually being logged to part of a webpage rather than a file. If it's a case of copying the code fragment out of such a framework (and attributing it) that's fine :-)

  • Steve Chambers
    Steve Chambers about 11 years
    Thanks for your answer but I think all printStacktrace() does is writes the stacktrace returned from getStacktrace() to the error stream? This isn't really what I'm looking for. Also don't think it would work in the case where the Exception wraps another Exception.
  • Philipp Wendler
    Philipp Wendler about 11 years
    The overloads of printStackStrace write to another target, not to the error stream. And the printed information contains all causes, including their type, message, stack trace, and (nested) causes.
  • Philipp Wendler
    Philipp Wendler about 11 years
    Just try out my 3 lines of code, I'm sure you will be satisfied.
  • flup
    flup about 11 years
    No need to roll your own, standard J2SE provides this. See my answer.
  • Philipp Wendler
    Philipp Wendler about 11 years
    @flup Why would you consider this "rolling your own"? This way does use the standard Java method for this.
  • flup
    flup about 11 years
    Quoting Steve: "The customer has specifically stipulated we shouldn't do this. We could go back and tell them it's ridiculous but I'd rather keep the work. May be proved wrong on this but the "roll your own crap" in the accepted answer seems to work fine so far..."
  • Dynomyte
    Dynomyte over 10 years
    Keep in mind that if you use a custom formatter, you have to specifically support using Throwables (I just copied Oracle code) or else this will not work.
  • Martin Larizzate
    Martin Larizzate almost 4 years
    In addition to this idea, i checked how to do it using SL4J and founded this: baeldung.com/slf4j-log-exceptions
  • Matt Welke
    Matt Welke over 2 years
    This answer was helpful. I was testing whether I could log exceptions in a way that included all the info about the error (including the error message of the deeply-nested original exception) and also allowed the program to continue to process more work. This worked well. The logged string included INFO: could not complete job for user Matt, com.mattwelke.Main$MathException: failed to do math, and Caused by: java.lang.IllegalArgumentException: you tried to divide 10.0 by 0. (meaning I got all the error context I needed in the logs). I imagine this would work well with SLF4J too.
  • ltlBeBoy
    ltlBeBoy almost 2 years
    Starting from version 3 of the language utilities the method has been renamed to ExceptionUtils#getStackTrace(...)!