RedirectConsole.java

package gov.usgs.util.logging;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

/**
 * Console redirection utility.
 * 
 * Replaces system.out and system.err with
 * printstreams that log all writes when flushed.
 */
public class RedirectConsole {

	/** Logger to handle writes to system.err. */
	private static final Logger SYSTEM_ERR_LOGGER = Logger
			.getLogger("system.err");

	/** Logger to handle writes to system.out. */
	private static final Logger SYSTEM_OUT_LOGGER = Logger
			.getLogger("system.out");

	/** The previous system.err stream. */
	private static PrintStream PREVIOUS_SYSTEM_ERR;

	/** The previous system.out.stream. */
	private static PrintStream PREVIOUS_SYSTEM_OUT;

	/**
	 * Redirect System.out and System.err to java.util.logging.Logger objects.
	 * 
	 * System.out is redirected to logger "system.out". System.err is redirected
	 * to logger "system.err".
	 * 
	 * Use the cancel method to undo this redirection.
	 */
	public static void redirect() {
		if (PREVIOUS_SYSTEM_ERR != null || PREVIOUS_SYSTEM_OUT != null) {
			cancel();
		}

		PREVIOUS_SYSTEM_OUT = System.out;
		System.setOut(new LoggerPrintStream("STDOUT", SYSTEM_OUT_LOGGER));

		PREVIOUS_SYSTEM_ERR = System.err;
		System.setErr(new LoggerPrintStream("STDERR", SYSTEM_ERR_LOGGER));
	}

	/**
	 * Undo a redirection previously setup using redirect().
	 * 
	 * Restores System.out and System.err to their state before redirect was
	 * called.
	 */
	public static void cancel() {
		if (PREVIOUS_SYSTEM_OUT != null) {
			// flush any pending output
			System.out.flush();
			// restore previous output stream
			System.setOut(PREVIOUS_SYSTEM_OUT);
			PREVIOUS_SYSTEM_OUT = null;
		}
		if (PREVIOUS_SYSTEM_ERR != null) {
			// flush any pending output
			System.err.flush();
			// restore previous error stream
			System.setErr(PREVIOUS_SYSTEM_ERR);
			PREVIOUS_SYSTEM_ERR = null;
		}
	}

	/**
	 * A PrintStream that writes messages to a Logger object.
	 */
	private static class LoggerPrintStream extends PrintStream {

		/** Name for output, prepended to LogRecord. */
		private String name;

		/** Logger used when flush is called. */
		private Logger logger;

		public LoggerPrintStream(final String name, final Logger logger) {
			// true is for autoflush
			super(new ByteArrayOutputStream(), true);
			this.name = name;
			this.logger = logger;
		}

		/**
		 * Override to force synchronization.
		 */
		@Override
		public synchronized void write(final byte[] b) throws IOException {
			super.write(b);
		}

		/**
		 * Override to force synchronization.
		 */
		@Override
		public synchronized void write(final int b) {
			super.write(b);
		}

		/**
		 * Override to force synchronization.
		 */
		@Override
		public synchronized void write(final byte[] buf, final int off,
				final int len) {
			super.write(buf, off, len);
		}

		/**
		 * Flush forces message to be written to log file.
		 */
		@Override
		public synchronized void flush() {
			ByteArrayOutputStream baos = (ByteArrayOutputStream) out;
			try {
				logger.log(new LogRecord(Level.INFO, this.name + " "
						+ baos.toString().trim()));
			} finally {
				// tried to write at least, clear buffer...
				out = new ByteArrayOutputStream();
			}
		}

	}

}