Redirect System.out to logger

redirectionSkilled developers knows that a logger is the right tool to figure out what applications really do (well… given that you logged that line!). But sometimes we use libraries and legacy code where the only “logger” is System.out.println(). We don’t have to blame too much the authors, even the Sun JAX-WS implementations use it, a notable example is the following:

Example – dump HTTP SOAP messages in JAX-WS
You can enable it for example with (see this answer on stackoverlow for the details):

System.setProperty("com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump", "true");
System.setProperty("com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true");
System.setProperty("com.sun.xml.ws.transport.http.HttpAdapter.dump", "true");
System.setProperty("com.sun.xml.internal.ws.transport.http.HttpAdapter.dump", "true");

Redirect what you want

Given the example, I think it’s particular evident (and desirable) to redirect only specific messages, that’s how you achieve it:

public class SystemOutToSlf4j extends PrintStream {

	private static final PrintStream originalSystemOut = System.out;
	private static SystemOutToSlf4j systemOutToLogger;

	/**
	 * Enable forwarding System.out.println calls to the logger if the stacktrace contains the class parameter
	 * @param clazz
	 */
	public static void enableForClass(Class clazz) {
		systemOutToLogger = new SystemOutToSlf4j(originalSystemOut, clazz.getName());
		System.setOut(systemOutToLogger);
	}


	/**
	 * Enable forwarding System.out.println calls to the logger if the stacktrace contains the package parameter
	 * @param packageToLog
	 */
	public static void enableForPackage(String packageToLog) {
		systemOutToLogger = new SystemOutToSlf4j(originalSystemOut, packageToLog);
		System.setOut(systemOutToLogger);
	}

	/**
	 * Disable forwarding to the logger resetting the standard output to the console
	 */
	public static void disable() {
		System.setOut(originalSystemOut);
		systemOutToLogger = null;
	}

	private String packageOrClassToLog;

	private SystemOutToSlf4j(PrintStream original, String packageOrClassToLog) {
		super(original);
		this.packageOrClassToLog = packageOrClassToLog;
	}

	@Override
	public void println(String line) {
		StackTraceElement[] stack = Thread.currentThread().getStackTrace();
		StackTraceElement caller = findCallerToLog(stack);
		if (caller == null) {
			super.println(line);
			return;
		}

		org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(caller.getClass());
		log.info(line);
	}

	public StackTraceElement findCallerToLog(StackTraceElement[] stack) {
		for (StackTraceElement element : stack) {
			if (element.getClassName().startsWith(packageOrClassToLog))
				return element;
		}

		return null;
	}

}

And you can use it in this way:

SystemOutToSlf4j.enableForClass(MyService.class)

After that, all the line printed to the standard output will be redirected only if they’re originated in MyService class.

If you want stop the redirection:

SystemOutToSlf4j.disable()

 

Leave a Reply

Your email address will not be published. Required fields are marked *