The State of Java Logging

A recent project requirement forced me to learn a lot more about the current state of Java logging that I ever really wanted to know. For the most part, I’ve always let other people pick the logging framework that a project uses and just sort of coasted along without paying too much attention.

Since all of the logging frameworks generally use similar interfaces, I’ve never really had to pay attention to which logging framework a given project is using. I thought I’d write a short piece about the four frameworks that I ran into last week in case other people need to worry about these differences.

log4j

In the beginning, there was log4j. This is an apache project and is often considered the de facto standard for Java logging. It’s fairly easily extensible and is configurable through several mechanisms. While log4j has been around forever and is the logger of choice for several projects that have been around for a while (notably JBoss).

One thing that I hadn’t realized is that log4j development is essentially dead. It’s currently at version 1.2, and plans for version 1.3 were abandoned in favour of developing log4j 2.0. However, it doesn’t appear that 2.0 is in active development. It is worth noting that Ceki Gülcü, the original founder of the log4j project, has moved on to slf4j (see below).

Something that stands out about log4j is that the base logger (helpfully named Logger) is a class and not an interface. This is understandable given log4j’s position in the history of Java logging, but is unfortunate in terms of flexibility of the framework.

Apache commons logging (JCL)

The commons-logging framework is a logging interface. It is most often implemented by log4j, although it can be configured to use the java.util.logging mechanism. By default, it tries to determine the implementation to use by searching the classpath.

I think most people treat JCL as a wrapper for log4j. It’s encouraging that the base logger class (named Log in this case) is an interface, which is then implemented by classes such as Log4jLogger (which acts a bridge between JCL and log4j). Another similarity between log4j and JCL is that the severity levels (TRACE, DEBUG, INFO, WARN, ERROR, FATAL) are identical.

java.util.logging

With JDK 1.4, Sun introduced its own logging framework in the java.util.logging (j.u.l.) package. This is another wrapper interface, and Sun supplies a default implementation.

This framework is used by several projects (CXF and Faces are two examples). Note that Sun’s default implementation spits log messages out to System.err. This causes problems with JBoss, since JBoss traps the System.err stream and reroutes to its log4j ERROR stream. There’s a little writeup on this particular problem here.

java.util.logging uses a different set of severity levels for its messages: FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE. Any bridge implementation that relies on log4j will have to map the different levels to each other.

While it is theoretically possible to do as much with the JDK logging as you can do with log4j, it doesn’t seem as widely accepted in the development community.

slf4j

A number of people had problems with commons-logging, notably in multiple-classloader environments. The Simple Logging Facade for Java (slf4j) is another attempt to provide a standardized logging interface for Java. Many of the log4j and commons-logging people seem to have joined the slf4j team. It hasn’t really caught on yet, although it is used by current versions of Hibernate.

slf4j is a logging interface with the familiar levels of error severity (TRACE, DEBUG, INFO, WARN, ERROR). It ships with its own implementation (Logback) as well as bridges for log4j and java.util.logging. The discovery method used to determine which implementation to use is different than JCL – basically, a jar that is bound at compile time to a certain implementation is included in the application. This gets rid of the classloader problems that people have identified.

Another neat thing about slf4j is that it has been coded with an eye towards the performance of disabled log statements. With other log frameworks, this type of code is more efficient:

if(logger.isDebugEnabled()) {
  logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}

This code gets around the cost of constructing the message by only invoking the message if debugging is turned on. It’s always a huge pain to code up every log statement like that, and a lot of people skip the pain.

slf4j allows the same message to be put together like this:

logger.debug("Entry number: {} is {}.", i, String.valueOf(entry[i]));

In this case, the replacement is not done until the message is actually printed out. So if you’re not logging DEBUG-level messages, you don’t invoke the cost of building the string.

Summary

As pointed out here and in other place, choosing a log method for your application is not nearly as straight-forward as it should be. People get into arguments and political discussions about this all the time. And this is logging – it’s not rocket science.

I think that eventually slf4j will become a standard, but what you choose for now is up to you. If one of these frameworks becomes a clear winner over the others, then there will by necessity be a migration path that leads towards the winner.

Print Friendly

Leave a Reply