A bit of History
At the time the now discontinued log4j was the most commonly used logging framework for java sun decided to implement JSR 47: java.util.logging. There were a lot of discussion, but it seems not been fruitful. java.util.logging was introduced in JDK 1.4 and hasn’t changed much despite its obvious shortcomings.
The Good, The Bad and the Ugly
One of jul best selling points is the integration to J2SE 1.4. Why need another logging framework when there is one bundled with the SDK ? Unfortunately it seems the guys responsible for jul did create a similar mess like java.util.Date.
Whats good in JUL ?
- Integration
Everything is included in the SDK - Easy to get started
Logger logger = Logger.getLogger(“de.glauche.test”);
logger.info(“my info message”);
Whats not so good in JUL ?
- Strange log levels: SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST
The first seems ok, but FINE, FINER, FINEST ? - logger.info, logger.severe etc. only accept Strings
This is a really bad case of inconsistent API, to log an exception for example you need to use logger.log(Level.SEVERE,”Some String”, exception); Why are there no shortcuts like logger.severe(“Some String”,exception); ? - no parameterized logging, which can be a severe performance penality
What is Parameterized Logging ? (and why should i care for it ?)
Imagine you have lots of debug logging statements in your code:
logger.debug("Number " + i + " has the Value: " + entry[i]);
Here, every time the code is reached the String is build first, which can be quite time-consuming if there are many variables which need to be converted to string and concatinated. This can affect the performance of the program quite a bit, especially if it is in some inner loop.
One common solution is to wrap the debug statement with an if-clause:
if(logger.getLevel() >= Level.DEBUG) { logger.debug("Number " + i + " has the Value: " + entry[i]); }
Needless to say this leads to quite messy code, is unreadable and inflexible. Fortunately there is a very underused Class in java called MessageFormat, where you can use placeholders for parameters.
So, when using something like MesageFormat, you can use: format(String pattern, Object[] arguments) for a logger. The logger could decide if the log entry needs to be constructed or not. In worst case the overhead is just a function call.
SLF4j does exactly this. It uses parameterized strings as logmessages as default. So, the above log entry in slf4j would look like this:
logger.debug("Number {} has the Value: {}",i,entry[i]);
Unfortunately SLF4j does not use javas overloading mechanism, so you can only add two Objects, or you need to create an array of them:
logger.debug("Value {} was inserted between {} and {}.", new Object[] {newVal, below, above});
and the Ugly ?
So, whats really ugly in JUL ?
The configuration
The default configuration resides in the JDK/lib directory (!), but can be overwritten by a command line parameter. This is very bad in the J2EE enviroment where one JDK instance can contain many independed applications.
The configuration file is rather nice and straitforward, it is easy to replace parts, or set up log levels.
but wait, there is an API to configure JUL !
There is a limited API to configure the log settings, yes. But it has some drawbacks. First, it is not so easy to replace certain parts. For example when you just want to get rid of the ugly two line default output and have all info in one clean line. With the config file it is easy, but with the API you need to implement your own Handler first.
Another big drawback is that if you configure the root logger (“”), it is still classloader dependent. If some parts of your (J2EE for example) application use different classloader mechanism, the default configuration will be used.
so how does SLF4j help ?
SLF4j is, as the name implies a simple facade for logging. It does not really log anything for itself. For the real logging you can use the build in simplelogger, JUL (if you just want some nicer API for example), logback and even Log4J. On the other hand you can also redirect log-entries from other system into SLF4j, for example Java commons Logging (JCL).
This is especailly nice, as many different libraries still use JCL or log4j.
SLF4j configures the output by the logging implementation in the classpath. Switching the real logging mechanism is as easy as replacing a jar file in your classpath.
SLF4j is fast, one does not need to write if () wrappers around the debug statments, and it has nice parameterized logging.
On the bad side, you usually need at least two additional jars as dependencies in your project, the facade and the actual logger.
There’s also a short introduction how to automaticly construct logging fields in classes with the help of guice.