I’ve started working on a new Java project, and it uses the popular SLF4J logging framework. I’ve never really had the opportunity to use it before; I’m mostly a Log4j guy with the occasional foray into Apache Commons Logging. SLF4J is highly similar to both of those other frameworks, but I got a bit confused because a dependency of this project, Apache HTTPClient, uses Commons Logging, and so there was this weird JAR to bridge them in there, and then Logback was around too. It was then that I fully realized what a mess the Java logging ecosystem is. This post attempts to detangle it.
To be honest, this is Sun’s fault. (Well, Oracle’s now.) Back in Java 1.4, Sun introduced its own logging framework built into the JDK, under the java.util.logging package; that framework is often abbreviated “JUL” after the package name. Being a part of the JDK, it’s an obvious choice for logging, since you don’t have to pull in some other library.
However, JUL is not all that easy to use, at least for me. It lets you set the logging level for both loggers and handlers (roughly equivalent to “appenders” in Log4j parlance), which seems powerful but often caused my log messages to get eaten when I’d forget to update the levels for both of them. Its level definitions also differ from the other logging frameworks, and some don’t have semantic meaning (I’m thinking of FINE, FINER, and FINEST).
JUL didn’t seem to catch on, compared to Log4j which pretty much set the standard for logging in Java. Of course, that wasn’t good enough, and over time a bunch of other logging libraries have arisen. They are all fine and good, but the problem comes when you are using one library that uses Log4j and another uses SLF4J (for example), and now you’ve got no idea what’s going to happen to your logs, and you also have a more complicated choice of which framework to use for your own stuff.
So, I did some research, and I’m going to try to detangle this Gordian knot, and also make some specific recommendations on what to use and what to avoid. Feel free to disagree; I’m sure there is not one right answer.
Frameworks to Consider
Like I said, there are many logging frameworks. I will consider these.
- JUL (java.util.logging, JDK logging, Java 1.4 logging, etc.)
- Log4j 1.2
- Log4j 2
- Apache Commons Logging
Facade vs. Implementation
The first thing to realize is that some frameworks are actual logging implementations, and some are primarily facades over other implementations.
- JUL, Log4j 1.2, Log4j 2 and Logback are full-blown logging implementations. You can just use these directly.
- Apache Commons Logging and SLF4J are facades over actual logging implementations. Each of them does include an internal, simplistic logging implementation (“SimpleLog” for Commons Logging, “Simple” for SLF4J) but you’re meant to use a real implementation in production.
Both Commons Logging and SLF4J can serve as a facade for either JUL or Log4j 1.2. Commons Logging can also front the old Lumberjack and Avalon LogKit / Excalibur frameworks, but those are obsolete. SLF4J doesn’t support either of those, but it can serve as a facade for Log4J 2 or Commons Logging itself. So you can have a facade over a facade, which you wouldn’t choose to do normally.
While I said that Log4j 2 is an implementation, it can also be a facade in front of SLF4J. Yes, that means you can potentially get your logging statements stuck in an infinite loop, since either framework can front the other.
None of the facades can front Logback, but Logback is special: It is a “native” implementation of the SLF4J API. So, SLF4J doesn’t translate logging calls to Logback; it just uses Logback’s implementation.
Here is an awesome ASCII graphic (courtesy of ASCIIFlow) illustrating the facade (and facade-like) relationships between the logging frameworks. Not sure it helps.
+---------+ +-------+ | | <------------> | | | log4j 2 | | slf4j +---+ | | +----+ | | +---------+ | +-+--+--+ | | | | | | | | | +-----------------------------+ | | | | | | | +-----v-----------+ | | | | | | | | | commons-logging | | | |native | | | | |implementation +-----+------+----+ +----------+ | | | | | | | +---------------+ | | | | | | | | | +--------------------------------+ | | | | | | +-----------------v---v---+ +--v-----v--+ +---v-----+ | | | | | | | java.util.logging (JUL) | | log4j 1.2 | | logback | | | | | | | +-------------------------+ +-----------+ +---------+
There’s another concept to understand here, and it’s that some frameworks can bridge to others. This is different from being a facade. A bridge exists to help you switch from one framework to another. For example, if you have a ton of code using JUL and you’ve decided you want to start using SLF4J, you don’t have to sweep through and update all your logging code; there is a bridge JAR you can throw in that will send all your JUL calls to SLF4J. You will still want to pick an implementation for SLF4J (say, Logback).
A bridge is also handy for dealing with a dependency that uses a different logging framework than the one you’d prefer. If you’re using SLF4J and you have a library that uses JUL for logging, the bridge JAR can consolidate the log messages.
I’m not going to draw another awesome picture; I’ll just list the bridges that are available.
- You can bridge to SLF4J from JUL, Log4j 1.2, Commons Logging.
- You can bridge to Log4j 2 from Log4j 1.2 (which makes sense) and Commons Logging.
Here are the promised recommendations for what to do with this mess.
- Don’t use JUL. The other implementations out there are easier to use and way more popular.
- If you like the idea of using a facade, go with SLF4J. It can cover more current implementations, and can even front Commons Logging. Use Logback as your implementation.
- If you just want a straight-up logging implementation, try Log4j 2. It expands and improves on the popular Log4j 1.2; also, if necessary, it can be bridged from Commons Logging and Log4j 1.2, and can serve as a facade over SLF4J.
Overall I’d just go with SLF4J over Log4j 2 since it’s more flexible as a facade, and you can have it front Log4j 2 anyway. Best of both worlds.
My final note is that I didn’t consider performance at all here. Each logging implementation appears to have its own strengths and weaknesses in that regard, and those may influence your logging framework decision more than architectural considerations.