вторник, 1 ноября 2011 г.

Spring logging under maven

As we all know, Spring framework uses Commons Logging (sometimes called JCL - Jakarta Commons Logging) for its output. And I bet you are a successful developer who knows how cool SLF4J is. Can you spot the problem already?

OK, I'll ease your agony. How are we going to ask Spring to pollute our log files with its log output, provided we use SLF4J as a facade and, say, Log4J as implementation?

I assume you use maven 2 as build tool. The solution should work for Spring 2 and 3.

So here is the plan. First we use a nice library jcl-over-slf4j, which  implements the public API of JCL but using SLF4J underneath. Then we use another nice library slf4j-log4j12 to tell SLF4J to use Log4j as its underlying logging framework. Apart from these two dependencies we obviously need two more: SLF4J itself (slf4j-api) and Log4J itself (log4j). So we end up with following dependencies config in maven pom:

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  ...
  <dependencies>
    ...
      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
          <version>1.6.3</version>
      </dependency>
      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-log4j12</artifactId>
          <version>1.6.3</version>
          <scope>runtime</scope>
      </dependency>
      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>jcl-over-slf4j</artifactId>
          <version>1.6.3</version>
          <scope>runtime</scope>
      </dependency>
      <dependency>
          <groupId>log4j</groupId>
          <artifactId>log4j</artifactId>
          <version>1.2.16</version>
          <scope>runtime</scope>
      </dependency>
  </dependencies>
</project>

But before celebrating the victory we have another thing left to solve. Removing commons-logging from classpath. Algorithm is following:
  1. make sure commons-logging is not directly specified in your pom as dependency
  2. run mvn dependency:tree and search for commons-logging in the output
  3. if not found goto 6
  4. for the higher-level dependency which brings commons-logging add the following configuration in pom:
    ...
      <dependencies>
          <dependency>
            <groupId>...</groupId>
            <artifactId>...</artifactId>
            <version>...</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
          </dependency>
      </dependencies>
    ...
    
  5. goto 2
  6. get some champagne and start celebrating

Now you can comfortably use SLF4J with Spring logging enabled.

Useful links:
http://blog.springsource.com/2009/12/04/logging-dependencies-in-spring/
http://stackoverflow.com/questions/3387441/how-do-i-configure-spring-and-slf4j-so-that-i-can-get-logging