The logging libraries provided with Java 5 and above are pretty good, except they produce output that is almost unreadable. For example, consider the simplest example:
Logger logger = Logger.getLogger("PushServer"); logger.log(Level.SEVERE, "This is a sample message");
That produces the following log entry, on your screen and/or in a file:
Sep 28, 2009 9:30:48 PM javaloggersample.Main main SEVERE: This is a sample message
Individually, that’s OK, but 20 messages? Messy. 1000 messages? Pretty nasty. 100 threads each generating a stream of FINE messages? Yikes.
We recently wrote a Java server application that was massively threaded, and because of the nature of the systems we were communicating with, we needed solid logging output that we could easily scan to get an idea of the health of the software. The default logging format was not it.
While you can get some degree of flexibility by changing configuration options, we found that the best solution, for us, was writing a new Formatter that the logger client could use to generate output. Here’s the above sample, using our formatter:
28/09/2009 21:41:44 SEVERE: This is a sample message
That is vastly more readable when the log file starts filling up, because each message is on a single line and each component of the message is formatted in columns. Here’s our logger class — you should add this class to your own project:
public class SingleLineFormatter extends Formatter { public SingleLineFormatter() { } private SimpleDateFormat dateFormat; static final String lineSep = System.getProperty("line.separator"); public String format(LogRecord record) { StringBuilder buf = new StringBuilder(180); if (dateFormat == null) dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); buf.append(dateFormat.format(new Date(record.getMillis()))); buf.append(' '); buf.append(record.getLevel()); buf.append(": "); buf.append(formatMessage(record)); buf.append(lineSep); Throwable throwable = record.getThrown(); if (throwable != null) { StringWriter sink = new StringWriter(); throwable.printStackTrace(new PrintWriter(sink, true)); buf.append(sink.toString()); } return buf.toString(); } }
When you create your logger, you need to tell it not to use the default formatter, and to use this formatter instead. Since the original Apache Log4j, loggers have been hierarchical — you can set parent/child relationships between logger objects and the child will do as its parent does, plus any configuration of its own. Therefore, to disable the default logger we set UseParentHandler to false. Then, we create a handler and initialize it with our formatter:
Logger logger = Logger.getLogger("PushServer"); logger.setUseParentHandlers(false); ConsoleHandler h = new ConsoleHandler(); h.setFormatter(new SingleLineFormatter()); h.setLevel(Level.ALL); logger.addHandler(h); logger.log(Level.SEVERE, "This is a sample message");
Hopefully this will be useful to somebody. We’re trying out something different here — posting coding tips to the CodeSpaces blog (rather than our company blog — Component Workshop). Also, we don’t often post Java tips, so on both of these points your feedback is most welcomed! Also, let us know if there are any topics you’d like us to cover — I think the next post might be about Middleware-Oriented-Messaging in .NET. — Adrian
This advice and any code samples are provided as-is, and without any warranty. You are free to use them as you wish. If you require professional services, or support on your own projects, get in touch with us and we’ll see if we can help. Note that we are UK based.
Tags: Uncategorized
