General

Help - I'm getting a StackOverFlowError every time I log or println!

You are probably using an SLF4J implementation that tries to write to the console using one of the print(ln) methods on PrintStream. These are in turn redirected back to your SLF4J implementation by sysout-over-slf4j, resulting in an infinite loop. To prevent this, add the following call early in the lifecycle of your application:

SysOutOverSLF4J.registerLoggingSystem("package.of.slf4j.implementation");

sysout-over-slf4j will then recognise all attempts to write to system out or err from your logging system and will delegate them to the original system out or err PrintStream.

Note that you should ONLY register a logging system if you need to - many logging systems (including Logback, Log4J and JULI) use the write methods on PrintStream, which always delegate directly to the original PrintStream, and so do not have this problem.


What if I want System.out calls to log as debug rather than info? Or System.err calls to log as warn rather than error?

You can customise the levels at which sysout-over-slf4j logs when initially configuring it at application startup. Simply call:

SysOutOverSLF4J.sendSystemOutAndErrToSLF4J(LogLevel.DEBUG, LogLevel.WARN);

instead of the no argument version. The first argument represents the level of calls to System.out, and the second argument represents the level of calls to System.err.


Why does sysout-over-slf4j log a separate log statement for every element in the stack in a stacktrace?

When Throwable.printStackTrace() is called it iterates over the frames in the Throwable's stack trace and prints each to the System.err as a separate method call. This leaves no reliable means of reconstituting the original Throwable, or recognising the last frame of the stack trace. Whilst logging each frame seperately is clearly sub-optimal, it is at present the only known way of reliably retaining all the information from a printStackTrace call and maintaining its ordering.

In recognition that this is unsatisfactory sysout-over-slf4j allows you to substitute in your own exception handling mechanism by implementing ExceptionHandlingStrategyFactory and passing an instance of your implementation in when configuring sysout-over-slf4j at startup:

ExceptionHandlingStrategyFactory factory = new CustomExceptionHandlingStrategyFactory();
SysOutOverSLF4J.sendSystemOutAndErrToSLF4J(factory);

Most System out or err calls are going through SLF4J, but I'm still seeing output getting directly to the console - why?

You are probably seeing output from calls to the System.out.write methods. The most common SLF4J implementations (Log4J, Logback and java.util.logging) use the write methods on the System output PrintStreams in their console appenders. If sysout-over-slf4j redirected these calls to SLF4J an expensive examination of the stack would be needed to prevent infinite recursion. Since the vast majority of usage of System.out.println for logging does not use the write methods, leaving them writing to the original print stream seemed the best compromised between performance and utility.


Don't most logging systems print to the console? Won't that mean infinite recursion?

As mentioned above, the most common SLF4J implementations use the write methods on PrintStream, which always delegate directly to the original PrintStream, and so are not affected by use of sysout-over-slf4j. If you use a logging system that does not use the write methods on the original PrintStream you will need to register it, and should be aware that there will be a significant performance impact whenever logging to the console.


What are the performance implications of using sysout-over-slf4j?

The overhead for Log4J, JULI and Logback when printing to the console should be minimal, because SLF4J simply proxies calls to the write methods through to the original PrintStreams without doing any work.

The overhead for some other SLF4J implementation that does not use the PrintStream write methods, and so needs to be registered, will be greater; on every attempt by it to print to the console its fully qualified classname has to be matched against registered package names in order to determine whether it should be permitted direct access.

Finally, the overhead of actual System.out and System.err calls will be much greater, due to the expense of generating the thread's stacktrace and examining it to determine the origin of the call. It would be much better if all logging were done via SLF4J directly and this module were not necessary.


I'm using Tomcat, and after I unloaded or reloaded the web application I used to redirect System out and err to SLF4J all calls to System.out.println fail with a java.lang.NoClassDefFoundError: uk/org/lidalia/sysoutslf4j/common/ReflectionUtils$1! I'm also seeing lots of IllegalStateExceptions in the logs. Why?

This is a failing of sysout-over-slf4j 1.0.x; it happens because System.out and System.err are static instances of a class loaded in the local web app's classloader. Tomcat works aggressively to prevent classloader leaks by discarding webapp classes when a webapp is unloaded. A fix will be available in sysout-over-slf4j 2.


How does it work?

The System.out and System.err PrintStreams are replaced with new SLF4JPrintStreams. Each time a call to System.out.println (or similar) is made, the current thread's stacktrace is examined to determine which class made the call. An SLF4J Logger named after that class's fully qualified name is retrieved and the message logged at the configured level on that logger (by default info for System.out calls and error for System.err calls).

Calls to Throwable.printStackTrace() are likewise logged at the configured level for each System output. By default there will be a message logged for every line of the stack trace; this is an unfortunate side effect of not being able to retrieve the original exception that is being printed reliably.

A servlet container may contain multiple web applications. If it has child first class loading and these applications package SLF4J in the web-app/lib directory then there will be multiple SLF4J instances running in the JVM. However, there is only one System.out and one System.err for the whole JVM. In order to ensure that the correct SLF4J instance is used for the correct web application, inside the new PrintStreams SLF4J instances are mapped against the context class loader to ensure that the same SLF4J instance used in "normal" logging is also used when calling System.out.println.

The PrintStreams only maintain a weak reference to the classloaders and their SLF4J instances, so that no classloader leak occurs if the classloader is discarded.