SimpleLogFileHandler.java

package gov.usgs.util.logging;

import gov.usgs.util.StreamUtils;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

/**
 * A java.util.logging style Handler that does daily log rotation by default.
 */
public class SimpleLogFileHandler extends Handler {

	/** Default format used. */
	public static final String DEFAULT_FILENAME_FORMAT = "'log_'yyyyMMdd'.log'";

	/** The directory where log files are written. */
	private File logDirectory;

	/** Used to generate filename for current log message. */
	private SimpleDateFormat filenameFormat;

	/** The last filename used when logging. */
	private String currentFilename;

	/** Handle to the current log file. */
	private OutputStream currentStream;

	/**
	 * Create a default SimpleLogHandler.
	 *
	 * Uses the system locale to roll log files once a day, and default filename
	 * format "log_YYYYMMDD.log".
	 *
	 * @param logDirectory
	 *            the directory to write log files.
	 */
	public SimpleLogFileHandler(final File logDirectory) {
		this(logDirectory, new SimpleDateFormat(DEFAULT_FILENAME_FORMAT));
	}

	/**
	 * Create a SimpleLogHandler with a custom filename format.
	 *
	 * @param logDirectory
	 *            the directory to write log files.
	 * @param filenameFormat
	 *            the format for log files. Files are opened as soon as the
	 *            format output changes for a given log's message.
	 */
	public SimpleLogFileHandler(final File logDirectory,
			final SimpleDateFormat filenameFormat) {
		this.logDirectory = logDirectory;
		this.filenameFormat = filenameFormat;
		this.currentFilename = null;
		this.currentStream = null;
	}

	/**
	 * Closes the current log file.
	 */
	public void close() throws SecurityException {
		if (currentStream != null) {
			try {
				// log when the file was closed, if possible
				currentStream.write(("\nClosing log file at "
						+ new Date().toString() + "\n\n").getBytes());
			} catch (IOException e) {
				// ignore
			} finally {
				StreamUtils.closeStream(currentStream);
				currentStream = null;
			}
		}
	}

	/**
	 * Attempts to flush any buffered content. If exceptions occur, the stream
	 * is closed.
	 */
	public void flush() {
		if (currentStream != null) {
			try {
				currentStream.flush();
			} catch (IOException e) {
				close();
				currentStream = null;
			}
		}
	}

	/**
	 * Retrieve the outputstream for the current log file.
	 *
	 * @param date
	 *            the date of the message about to be logged.
	 * @return and OutputStream where the log message may be written.
	 * @throws IOException
	 *             if errors occur.
	 */
	protected OutputStream getOutputStream(final Date date) throws IOException {
		String filename = filenameFormat.format(date);
		if (currentStream == null || currentFilename == null
				|| !filename.equals(currentFilename)) {
			// close any existing stream
			close();

			// filename is what is being opened
			currentFilename = filename;
			currentStream = StreamUtils.getOutputStream(new File(logDirectory,
					filename), true);

			// log when the file was opened
			currentStream
					.write(("Opened log file at " + new Date().toString() + "\n\n")
							.getBytes());
			currentStream.flush();
		}
		return currentStream;
	}

	/**
	 * Add a LogRecord to the log file.
	 */
	public void publish(LogRecord record) {
		if (record == null) {
			return;
		}

		String message = getFormatter().format(record);
		try {
			OutputStream stream = getOutputStream(new Date(record.getMillis()));
			stream.write(message.getBytes());
			flush();
		} catch (Exception e) {
			// close if any exceptions occur
			close();
		}
	}

	private static final Logger LOGGER = Logger
			.getLogger(SimpleLogFileHandler.class.getName());

		/**
		 * Testing for handler
		 * @param args CLI args
		 * @throws Exception if error occurs
		 */
			public static void main(final String[] args) throws Exception {
		SimpleDateFormat ridiculouslyShortLogs = new SimpleDateFormat(
				"'log_'yyyyMMddHHmmss'.log'");
		File logDirectory = new File("log");

		SimpleLogFileHandler handler = new SimpleLogFileHandler(logDirectory,
				ridiculouslyShortLogs);
		handler.setFormatter(new SimpleLogFormatter());
		LOGGER.addHandler(handler);

		// there should be at least 2 log files created, and more likely 3
		LOGGER.info("message to log");
		handler.close();
		LOGGER.info("message to log");
		Thread.sleep(1000);
		LOGGER.info("another message to log");
		Thread.sleep(250);
		LOGGER.info("another message to log");
		Thread.sleep(1000);
		LOGGER.info("yet another message to log");
		Thread.sleep(250);
		LOGGER.info("yet another message to log");
	}

}