Switching to SLF4J
After years of fanatical devotion I have decided to cast aside Jakarta Commons Logging (JCL) in favour of Simple Logging Facade for Java (SLF4J).
Why make the switch?
I have never encountered any of the dreaded class loader problems while working with JCL. But I noticed a problem debbugging my data access objects after upgrading to Hibernate 3.3 SP1. The unit tests I had written in JUnit and DbUnit were failing but I couldn’t get Hibernate to log anything to the console except for the SQL statements that get output when you set show_sql=true. Just before I went completely bald I decided to debug into the Hibernate and noticed that it was using SLF4J as the logging facade instead of JCL!
Making the switch
Switching to SLF4J was very straightforward. There are 3 elements to SLF4J:
- The API that implements the facade (slf4j-api.jar)
- The provider that adapts the underlying logging system (e.g. slf4j-log4j12.jar)
- Optional briges wrap or replace legacy logging facades (e.g. jcl-over-slf4j.jar)
Anybody familiar with JCL or Log4J will have no trouble adapting to SLF4J. This is illustrated in the sample code below.
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MyClass { private static final Logger LOG = LoggerFactory.getLog(MyClass.class); public void foo() { if (MyClass.LOG.isDebugEnabled()) { MyClass.LOG.debug("Entering foo()"); } } }
SLF4J relies on static linking to discover the logging provider. So you just need to include the provider and appropriate adapter as dependencies. For example if you are using Log4J then you would include slf4j-log4j12.jar and configure log4j.properties or log4j.xml as normal.
Below are Maven POM snippets for specifying the API and provider dependencies:
<properties> <org.slf4j.version>1.5.2</org.slf4j.version> </properties> <dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${org.slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${org.slf4j.version}</version> </dependency> </dependencies>
Dealing with legacy code
Many of the open source frameworks that I use are still dependent on JCL. The jcl-over-slf4j.jar bridge takes care of this. This is a drop in replacement for commons-logging.jar.
Below is the Maven POM snippet for specifying the JCL bridge dependency:
<dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>${org.slf4j.version}</version> </dependency> </dependencies>
A couple of gotchas
Dependencies on JCL
JCL has been around for some time and is very popular. So you are going to have to deal with third parties sucking commons-logging.jar into your build as dependency. You can use the <exclusions/> element as illustrated in the example below to avoid this problem:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${org.springframework.version}</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency>
DbUnit
DbUnit 2.2.3 had already dumped JCL in favour of SLF4J. But unfortunately it has declared a dependency on slf4j-nop.jar in its POM. I mentioned earlier that you can only have one provider. The class loader was pulling in slf4j-nop.jar before slf4j-log12.jar and this meant that the log messages were being consumed by slf4j-nop.jar. As the name suggests slf4j-nop.jar does nothing with those log messages and I was looking at a blank screen for another few hours.
Exclusions to the rescue again:
<dependency> <groupId>org.dbunit</groupId> <artifactId>dbunit</artifactId> <version>${org.dbunit.version}</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> </exclusion> </exclusions> </dependency>
Tagged as dbunit, hibernate, jcl, junit, log4j, maven, slf4j, tdd + Categorized as Development, Logging
1 Comments
Trackbacks & Pingbacks
-
Brian Matthew’s Blog » Using P6Spy to debug DbUnit Maven Plugin
[...] my last post I described the motivation for switching to from JCL to SLF4J. I had been debugging some unit tests [...]