SLF4J & Logback

At some point, the original creator of Log4j, Ceki Gülcü, decided to split from the Log4j project and create a successor, which was not called Log4j2, but Logback. You can read about what he tried to make better with Logback here.

Suffice to say, that Logback is a mature and solid logging library, with a ton of features, of which auto-reloading of configuration files in production is one that developers seem to remember the most.

Somewhat in parallel, he also started writing the Simple Logging Facade for Java, also known as SLF4J, which is very similar to the Apache Commons Logging 'bridging' library above, just with a better implementation. Let’s see what that means:

To get started with SLF4J, you only need one library on the classpath, the slf4j-api dependency (see screencast for the following section). If you are using a dependency management tool like Maven, then you would add the following dependency to your dependencies section:

<dependency> <groupId> org.slf4j </groupId> <artifactId> slf4j-api </artifactId> <version> 1.7.25 </version> </dependency>

Having the API on your classpath will allow you to write log statements like the following:

org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MyClass.class); logger.info( " This is an info message " ); logger.error( " This is an error message " ); logger.debug( " Here is a debug message " );

Just like JCL, SLF4J cannot do logging itself. It needs a logging library to do the actual logging, like Log4j, JUL, Logback etc. So, say you want to use Log4j v1, you would then need the slf4j-log4j12 binding library in your classpath:

<dependency> <groupId> org.slf4j </groupId> <artifactId> slf4j-log4j12 </artifactId> <version> 1.7.25 </version> </dependency>

That dependency will transitively pull in Log4j (v1) for you and also make sure that SLF4J logs 'through' Log4j. If you are interested in how this works, read the section about bindings in the SLF4J manual.

Other libraries, like Logback, don’t need a binding library, because they implement SLF4J natively, so you can simply use the slf4j-api dependency, drop in the logback-classic jar as well, and you can log through Logback.

<dependency> <groupId> ch.qos.logback </groupId> <artifactId> logback-classic </artifactId> <version> 1.2.3 </version> </dependency>

The beauty of this approach is, that your code only knows SLF4J. There are no references to Log4j, Logback or Jul. And if you are writing a library, that’s even better. Because if your library uses SLF4J, then the end-user of your library can decide to log either with Log4j, or Logback, or whatever library he wants. Because that choice can simply be made by adding or removing a couple of jars to or from the classpath.

Hold on, aren’t we missing something?

Things get interesting, whenever you are using 3rd party libraries, that are hardcoded to use a specific logging library. Imagine you are using a PDF generator library, which is hardcoded to use Log4j. You are also using an e-mail sending library, which uses JUL. Your own application uses SLF4J, but you cannot just go and change the source code of these libraries to use SLF4J as well.

What to do now?

Thankfully, the SLF4J creators thought about that use-case as well (see screencast here). Let’s see what that looks like, by looking at the Maven dependencies first:

Whenever you pull in a 3rd party-library that uses Log4j, it will, obviously, pull in the Log4j dependency. The Log4j dependency looks like this:

<dependency> <groupId> log4j </groupId> <artifactId> log4j </artifactId> <version> 1.2.17 </version> </dependency>

You then need to make sure to exclude that dependency from your project and use the following drop-in replacement instead:

<dependency> <groupId> org.slf4j </groupId> <artifactId> log4j-over-slf4j </artifactId> <version> 1.7.25 </version> </dependency>

Here’s the trick: Inside that log4j-over-slf4j.jar, you’ll find classes like org.apache.log4j.Logger, but they have nothing to do with Log4j! Instead, these are SLF4J specific classes, i.e. your code 'thinks' it calls Log4j, but instead everything gets routed to SLF4J. (The same is true for the other 'over-slf4j' libraries, except for the JUL library, which you can read about here).

Which in turn means, that you, as the end-user of a library, can use whatever logging library you want, even though the original library creator wanted you to use Log4j specifically.

Real-Life

So, depending on what you are building and the third-party libraries you are using, you might end-up with the following libraries in your classpath:

The SLF4J API

Your SLF4J implementation, like Logback, or Log4j, etc.

One or multiple bridging libraries, like log4j-over-slf4j, jul-to-slf4j, jcl-over-slf4j etc.

Main Takeaway

With SLF4J, you code to the API, and can choose the implementation (Log4j, Logback etc.) later (compile time). In addition, you can use bridging libraries to make legacy 3rd party libraries 'speak' SLF4J.