[java] How to log as much information as possible for a Java Exception?

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

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

As an 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 this question I was nearly going to have a stab at this myself but don't really 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 :-)

This question is related to java exception logging methods stack-trace

The answer is


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).


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. :)


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)

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);
}

A logging script that I have written some time ago might be of help, although it is not exactly what you want. It acts in a way like a System.out.println but with much more information about StackTrace etc. It also provides Clickable text for Eclipse:

private static final SimpleDateFormat   extended    = new SimpleDateFormat( "dd MMM yyyy (HH:mm:ss) zz" );

public static java.util.logging.Logger initLogger(final String name) {
    final java.util.logging.Logger logger = java.util.logging.Logger.getLogger( name );
    try {

        Handler ch = new ConsoleHandler();
        logger.addHandler( ch );

        logger.setLevel( Level.ALL ); // Level selbst setzen

        logger.setUseParentHandlers( false );

        final java.util.logging.SimpleFormatter formatter = new SimpleFormatter() {

            @Override
            public synchronized String format(final LogRecord record) {
                StackTraceElement[] trace = new Throwable().getStackTrace();
                String clickable = "(" + trace[ 7 ].getFileName() + ":" + trace[ 7 ].getLineNumber() + ") ";
                /* Clickable text in Console. */

                for( int i = 8; i < trace.length; i++ ) {
                    /* 0 - 6 is the logging trace, 7 - x is the trace until log method was called */
                    if( trace[ i ].getFileName() == null )
                        continue;
                    clickable = "(" + trace[ i ].getFileName() + ":" + trace[ i ].getLineNumber() + ") -> " + clickable;
                }

                final String time = "<" + extended.format( new Date( record.getMillis() ) ) + "> ";

                StringBuilder level = new StringBuilder("[" + record.getLevel() + "] ");
                while( level.length() < 15 ) /* extend for tabby display */
                    level.append(" ");

                StringBuilder name = new StringBuilder(record.getLoggerName()).append(": ");
                while( name.length() < 15 ) /* extend for tabby display */
                    name.append(" ");

                String thread = Thread.currentThread().getName();
                if( thread.length() > 18 ) /* trim if too long */
                    thread = thread.substring( 0, 16 ) + "..."; 
                else {
                    StringBuilder sb = new StringBuilder(thread);
                    while( sb.length() < 18 ) /* extend for tabby display */
                        sb.append(" ");
                    thread = sb.insert( 0, "Thread " ).toString();
                }

                final String message = "\"" + record.getMessage() + "\" ";

                return level + time + thread + name + clickable + message + "\n";
            }
        };
        ch.setFormatter( formatter );
        ch.setLevel( Level.ALL );
    } catch( final SecurityException e ) {
        e.printStackTrace();
    }
    return logger;
}

Notice this outputs to the console, you can change that, see http://docs.oracle.com/javase/1.4.2/docs/api/java/util/logging/Logger.html for more information on that.

Now, the following will probably do what you want. It will go through all causes of a Throwable and save it in a String. Note that this does not use StringBuilder, so you can optimize by changing it.

Throwable e = ...
String detail = e.getClass().getName() + ": " + e.getMessage();
for( final StackTraceElement s : e.getStackTrace() )
    detail += "\n\t" + s.toString();
while( ( e = e.getCause() ) != null ) {
    detail += "\nCaused by: ";
    for( final StackTraceElement s : e.getStackTrace() )
        detail += "\n\t" + s.toString();
}

Regards,
Danyel


Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to exception

Connection Java-MySql : Public Key Retrieval is not allowed How to print an exception in Python 3? ASP.NET Core Web API exception handling Catching FULL exception message How to get exception message in Python properly What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean? what does Error "Thread 1:EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)" mean? Argument Exception "Item with Same Key has already been added" The given key was not present in the dictionary. Which key? sql try/catch rollback/commit - preventing erroneous commit after rollback

Examples related to logging

How to redirect docker container logs to a single file? Console logging for react? Hide strange unwanted Xcode logs Where are logs located? Retrieve last 100 lines logs Spring Boot - How to log all requests and responses with exceptions in single place? How do I get logs from all pods of a Kubernetes replication controller? Where is the Docker daemon log? How to log SQL statements in Spring Boot? How to do logging in React Native?

Examples related to methods

String method cannot be found in a main class method Calling another method java GUI ReactJS - Call One Component Method From Another Component multiple conditions for JavaScript .includes() method java, get set methods includes() not working in all browsers Python safe method to get value of nested dictionary Calling one method from another within same class in Python TypeError: method() takes 1 positional argument but 2 were given Android ListView with onClick items

Examples related to stack-trace

Couldn't load memtrack module Logcat Error How to Add Stacktrace or debug Option when Building Android Studio Project How to log as much information as possible for a Java Exception? e.printStackTrace equivalent in python Why is exception.printStackTrace() considered bad practice? How to store printStackTrace into a string Get exception description and stack trace which caused an exception, all as a string How to send a stacktrace to log4j? How to print full stack trace in exception? What is a stack trace, and how can I use it to debug my application errors?