How can I configure log4j to not print the exception stacktrace?
We use Log4j (and Commons Logging) to log our error messages. Now we want to set up an additional log appender that outputs fatal errors to syslog, but without the exceptionally long Java stacktraces (those will still be available in the full log file). How would one configure this (using log4j.xml)? Is there a filter available to ignore the stack traces?
Another use case where one could want this is when you are running a suite of tests that contain negative tests that are expected to throw exceptions that we always want to log in production. We don’t want our test logs to be crowded with exceptions that are par for the course
8 Answers 8
Edit after reading some more of the source:
You still need to subclass PatternLayout, but the method you want to override is ignoresThrowable(): it should return false, which will prevent the appender from writing the Throwable (it assumes that the layout has done so already).
No way to specify this in the configuration: PatternLayout has a hardcoded «return true».
Okay, that works, thanks. Too bad that this is not a configuration option with log4j. I wonder why this is handled so much different than the other options available to PatternLayout. Seems like a natural formatting option to me.
It is a little strange: when I first looked at the code, I figured that the layouts handled everything, then discovered that it was actually the appender. And that the appenders are slightly different. If you’re going to do this, you could add a setter for the ignoreThroable() and submit a patch.
Here’s the actual code I use:
import org.apache.log4j.PatternLayout; public class NoStackTracePatterLayout extends PatternLayout < @Override public boolean ignoresThrowable()< return false; >>
Thanks. Again, having to write a subclass just for this is not nice. Should have been a configuration option on PatternLayout.
If you use log4j > 1.2.16, you can use the EnhancedPatternLayout layout.
Example (with a log4j.properties file), define it as the layout of your appender, and then add %throwable in the conversion pattern:
log4j.appender.XXX.layout=org.apache.log4j.EnhancedPatternLayout log4j.appender.XXX.layout.ConversionPattern=%d %-5p %c:%L - %m%n%throwable
This is exactly what I need, but with the slf4j wrapper slf4j-log4j12 version 1.7.12 (under the hood using log4j version 1.2.17 ) it’s still showing the full JSONException 🙁 Does anyone have any idea why?
In 1.2.16 you can use EnhancedPatternLayout
As of Log4j2, you can just add «%ex» to your log pattern (assuming you’re using PatternLayout)
The «nopex» or «nopexception» conversion word in logback-classic (log4j’s successor) disables printing stack traces. The «nopex» conversion word is documented along with the rest of conversion words. You need to scroll down a little.
If you need further information on this topic, please contact the logback-user mailing list.
You may need to write a custom layout to do it (which isn’t that bad to do; you could subclass PatternLayout).
If you can change the source code, then another option is available for consideration.
In my applications, I always and only log FATAL messages from my applications entry point (e.g., «main()»), since I only know that they are fatal if I am about to exit the application because of them.
Therefore, in this one place (or handful if you have multiple application entry points), instantiate a Log4j Logger with a special class or MDC of «syslog» or similar. Upon catching a soon-to-be-FATAL error, log it in the usual way (for your other log files and such), but also invoke the fatal() method on this new «syslog» Logger with only the precise message that you want (such as only the exception class and message but without the stack trace). Then configure Log4j to direct only this «syslog» class or MDC to a newly-configured Appender that targets the SysLog.
Log runtime Exceptions in Java using log4j
I am currently building an application using Tomcat, Spring and JAVA. I am using Log4J as my logging library. I currently am logging everything to a text file. One of the issues I’m having is that RuntimeExceptions are not being logged to any files. I was wondering if there is a way to log all RuntimeExceptions that maybe thrown to my application log file. If not is it possible to have it log to another log file? Is there a standard way to do this? If so is there a standard way to do this when running your application within Tomcat? Thank you in advance for your help!
3 Answers 3
I’m not sure if this is what you are looking for, but there is a handler for exceptions that terminate threads. It is a handler for any exception that is not caught explicitly by the target of the thread.
The default «uncaught exception handler» simply calls printStackTrace() on the Throwable to print the stack trace to System.err . However, you could replace this with your own UncaughtExceptionHandler that logs the exception to log4j instead:
class Log4jBackstop implements Thread.UncaughtExceptionHandler < private static Logger log = Logger.getLogger(Log4jBackstop.class); public void uncaughtException(Thread t, Throwable ex) < log.error("Uncaught exception in thread: " + t.getName(), ex); >>
If you are using an executor framework to which you pass a Runnable object, its threads probably have their own catch block that prevent exceptions from reaching the uncaught exception handler. If you want to catch Runtime exceptions there, one way to do it is to wrap each task in a logging wrapper, like this:
class Log4jWrapper < private final Logger log; private final Runnable target; Log4jWrapper(Logger log, Runnable target) < this.log = Objects.requireNonNull(log); this.target = Objects.requireNonNull(target); >public void run() < try < target.run(); >catch(RuntimeException ex) < log.error("Uncaught exception.", ex); throw ex; >> > . Runnable realTask = . ; executor.submit(new Log4jWrapper(Logger.getLogger(Whatever.class), realTask));
How to log exceptions in Java?
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.
7 Answers 7
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.
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);
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.
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.
In addition to this idea, i checked how to do it using SL4J and founded this: baeldung.com/slf4j-log-exceptions
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.
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) .
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.
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.
@flup Why would you consider this «rolling your own»? This way does use the standard Java method for this.
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(.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 = " "; 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 )