Logging Dependencies in Spring

This article deals with the choices that Spring makes and the options that developers have for logging in applications built with Spring. This is timed to coincide with the imminent release of Spring 3.0 not because we have changed anything much (although we are being more careful with dependency metadata now), but so that you can make an informed decision about how to implement and configure logging in your application. First we look briefly at what the mandatory dependencies are in Spring, and then go on to discuss in more detail how to set your application up to use some examples of common logging libraries. As an example I'll show the dependency configuration using Maven Central style artifact naming conventions.
Spring Dependencies and Depending on Spring
Although Spring provides integration and support for a huge range of enterprise and other external tools, it intentionally keeps its mandatory dependencies to an absolute minimum: you shouldn't have to locate and download (even automatically) a large number of jar libraries in order to use Spring for simple use cases. For basic dependency injection there is only one mandatory external dependency,and that is for logging (see below for a more detailed description of logging options). If you are using Maven for dependency management you don't even need to supply the logging dependency explicitly. For example, to create an application context and use dependency injection to configure an application, your Maven dependencies will look like this:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.0.0.RELEASE</version>
<scope>runtime</scope>
</dependency>
</dependencies>
That's it. Note the scope can be declared as runtime if you don't need to compile against Spring APIs, which is typically the case for basic dependency injection use cases.
We used the Maven Central naming conventions in the example above, so that works with Maven Central or the SpringSource S3 Maven repository. To use the S3 Maven repository (e.g. for milestones or developer snapshots), you need to specify the repository location in your Maven configuration. For full releases:
<repository>
<id>com.springsource.repository.maven.release</id>
<url>http://maven.springframework.org/release/</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>
For milestones:
<repository>
<id>com.springsource.repository.maven.milestone</id>
<url>http://maven.springframework.org/milestone/</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>
And for snapshots:
<repository>
<id>com.springsource.repository.maven.snapshot</id>
<url>http://maven.springframework.org/snapshot/</url>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
To use the SpringSource EBR you would need to use a different naming convention for the dependencies. The names are usually easy to guess, e.g. in this case it is:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.context</artifactId>
<version>3.0.0.RELEASE</version>
<scope>runtime</scope>
</dependency>
</dependencies>
You also need to declare the location of the repository explicitly (only the URL is important):
<repository>
<id>com.springsource.repository.bundles.release</id>
<url>http://repository.springsource.com/maven/bundles/release/</url>
</repository>
</repositories>
If you are managing your dependencies by hand, the URL in the repository declaration above is not browseable, but there is a user interface at
that can be used to search for and download dependencies. It also has handy snippets of Maven and Ivy configuration that you can copy and paste if you are using those tools.
If you prefer to use Ivy to manage dependencies then there are similar names and configuration options there (refer to the documentation of your dependency management system, or look at some sample code – Spring itself uses Ivy to manage dependencies when it is building).
Logging
Logging is a very important dependency for Spring because a) it is the only mandatory external dependency, b) everyone likes to see some output from the tools they are using, and c) Spring integrates with lots of other tools all of which have also made a choice of logging dependency. One of the goals of an application developer is often to have unified logging configured in a central place for the whole application, including all external components. This is more difficult than it might have been since there are so many choices of logging framework.
The mandatory logging dependency in Spring is the Jakarta Commons Logging API (JCL). We compile against JCL and we also make JCL Log objects visible for classes that extend the Spring Framework. It's important to users that all versions of Spring use the same logging library: migration is easy because backwards compatibility is preserved even with applications that extend Spring.The way we do this is to make one of the modules in Spring depend explicitly on commons-logging (the canonical implementation of JCL), and then make all the other modules depend on that at compile time. If you are using Maven for example, and wondering where you picked up the dependency on commons-logging, then it is from Spring and specifically from the central module calledspring-core.
The nice thing about commons-logging is that you don't need anything else to make your application work. It has a runtime discovery algorithm that looks for other logging frameworks in well known places on the classpath and uses one that it thinks is appropriate (or you can tell it which one if you need to). If nothing else is available you get pretty nice looking logs just from the JDK (java.util.logging or JUL for short). You should find that your Spring application works and logs happily to the console out of the box in most situations, and that's important.
Unfortunately, the worst thing about commons-logging, and what has made it unpopular with new tools, is also the runtime discovery algorithm. If we could turn back the clock and start Spring now as a new project it would use a different logging dependency. Probably the first choice would be the Simple Logging Facade for Java (SLF4J), which is also used by a lot of other tools that people use with Spring inside their applications.
To switch off commons-logging is easy: just make sure it isn't on the classpath at runtime. In Maven terms you exclude the dependency, and because of the way that the Spring dependencies are declared, you only have to do that once.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.0.0.RELEASE</version>
<scope>runtime</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
Now this application is probably broken because there is no implementation of the JCL API on the classpath, so to fix it a new one has to be provided. In the next section we show you how to provide an alternative implementation of JCL using SLF4J as an example.
Using SLF4J
SLF4J is a cleaner dependency and more efficient at runtime than commons-logging because it uses compile-time bindings instead of runtime discovery of the other logging frameworks it integrates. This also means that you have to be more explicit about what you want to happen at runtime, and declare it or configure it accordingly. SLF4J provides bindings to many common logging frameworks, so you can usually choose one that you already use, and bind to that for configuration and management.
SLF4J provides bindings to many common logging frameworks,including JCL, and it also does the reverse: bridges between other logging frameworks and itself. So to use SLF4J with Spring you need to replace the commons-logging dependency with the SLF4J-JCL bridge. Once you have done that then logging calls from within Spring will be translated into logging calls to the SLF4J API, so if other libraries in your application use that API, then you have a single place to configure and manage logging.
A common choice might be to bridge Spring to SLF4J, and then provide explicit binding from SLF4J to Log4J. You need to supply 4 dependencies (and exclude the existing commons-logging): the bridge, theSLF4J API, the binding to Log4J, and the Log4J implementation itself. In Maven you would do that like this
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.0.0.RELEASE</version>
<scope>runtime</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.5.8</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.8</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.8</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
<scope>runtime</scope>
</dependency>
</dependencies>
That might seem like a lot of dependencies just to get some logging. Well it is, but it
A more common choice amongst SLF4J users, which uses fewer step sand generates fewer dependencies, is to bind directly to Logback. This removes the extra binding step because Logback implements SLF4J directly, so you only need to depend on two libaries not four (jcl-over-slf4j and logback). If you do that you might also need to exclude the slf4j-api dependency from other external dependencies (not Spring), because you only want one version of that API on the classpath.
Using Log4J
Many people use
To make Log4j work with the default JCL dependency (commons-logging) all you need to do is put Log4j on the classpath, and provide it with a configuration file(log4j.properties or log4j.xml in the root of the classpath). So for Maven users this is your dependency declaration:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.0.0.RELEASE</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
<scope>runtime</scope>
</dependency>
</dependencies>
And here's a sample log4j.properties for logging to the console:
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L – %m%n
log4j.category.org.springframework.beans.factory=DEBUG
Runtime Containers with Native JCL
Many people run their Spring applications in a container that itself provides an implementation of JCL. IBM Websphere Application Server (WAS) is the archetype. This often causes problems, and unfortunately there is no silver bullet solution; simply excluding commons-logging from your application is not enough inmost situations.
To be clear about this: the problems reported are usually not with JCL per se, or even with commons-logging: rather they are to do with binding commons-logging to another framework (often Log4J). This can fail because commons-logging changed the way they do the runtime discovery in between the older versions (1.0) found in some containers and the modern versions that most people use now (1.1). Spring does not use any unusual parts of the JCL API, so nothing breaks there, but as soon as Spring or your application tries to do any logging you can find that the bindings to Log4J are not working.
In such cases with WAS the easiest thing to do is to invert the classloader hierarchy (IBM calls it "parent last") so that the application controls the JCL dependency, not the container. That option isn't always open, but there are plenty of other suggestions in the public domain for alternative approaches, and your mileage may vary depending on the exact version and feature set of the container.
Similar Posts
- Obtaining Spring 3 Artifacts with Maven
- Bundlor Version Expansion and Property Substitution
- Using Bundlor in Eclipse
- Maven Artifacts
- Spring Framework Maven Artifacts











Jens Göring says:
Added on December 4th, 2009 at 8:48 amI like to add that you often need to exclude commons-logging from additional dependencies (non-spring, other frameworks), if they use it. One way to get around this is explained here: http://day-to-day-stuff.blogspot.com/2007/10/announcement-version-99-does-not-exist.html
And a minor correction: SLF is called Simple Logging Facade, not Framework
Dave Syer (blog author) says:
Added on December 4th, 2009 at 9:02 amThanks for the link. And I corrected the full name of SLF4J.
AndreyRybin says:
Added on December 4th, 2009 at 10:03 amWhy not to switch to SLF4J?
Spring3.0 is new _major_ version with many changes.
Why not SLF4J too?
Dave Syer (blog author) says:
Added on December 4th, 2009 at 10:12 amIn a word (actually 2 words): "backwards compatibility". Read the second paragraph in the section entitled "Logging" again, and see if it is unclear. If it is I can expand a bit in the user guide.
Also this JIRA has some interesting discussion: SPR-5327.
Ceki Gulcu says:
Added on December 4th, 2009 at 11:08 amThank you for this post. I believe that the artifact name for the jcl replacement is jcl-over-slf4j and not jcl-slf4j.
Dave Syer (blog author) says:
Added on December 4th, 2009 at 11:11 amThanks. I'll correct that in the user guide as well.
Tony Dalbrekt says:
Added on December 4th, 2009 at 1:46 pmA simple way to exclude commons-logging from your package is to add a dependency of the latest version with scope provided and then add the jcl-over-slf4j with default scope (compile). This will prevent any version of commons-logging to be bundled with your final artifact.
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.5.8</version>
</dependency>
Dave Syer (blog author) says:
Added on December 4th, 2009 at 2:19 pmThanks for the additional suggestion. There's always more than one way, as they say, to skin a cat.
I updated your comment and added back the XML that the blog machine helpfully stripped out when you posted. Hope that's OK.
Rusty Wright says:
Added on December 4th, 2009 at 2:30 pmGreat tip Tony; I've been using the 99.0-does-not-exist hack for a while now and it's nice to not need that.
Robert Glover says:
Added on December 4th, 2009 at 4:42 pmBecause of a need to develop locally in Tomcat but later deploy to IBM Websphere 6.1, I wrote a 31 page Power Point Presentation named "HOW_TO_CONFIGURE_commons-logging-for-artifactory_with_slf4j.pptx". Unfortunately there is no way to attached it to this comment.
What I basically did was devise an "Artifactory port" of the concepts of the solution explained at the link in the comment before this one and at the following two links:
http://stubbisms.wordpress.com/2008/07/27/death-to-commons-logging-long-live-slf4j/
http://www.slf4j.org/legacy.html
The gist of the solution was to create within "Artifactory" a “dummy” commons-logging jar file to replace the “real” commons-logging jar file. The dummy JCL jar file contains the same "MANIFEST-MF" file as the real commons-logging jar file and one dummy java class. When doing a local maven build, the "dummy" version of the JCL jar gets copied from Artifactory into the local maven repository on the local hard drive.
The changes in the Pom file of the Spring Webapp were to add the following:
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.9</version>
</dependency>
With this solution JCL works okay in IBM Websphere 6.1, and there is no need to change the "parent last" setting. In the past, changing that setting didn't work for me, which is why I took the time to devise this solution.
TomS says:
Added on December 10th, 2009 at 2:42 pmWhen I upgraded my logging from Spring 2.5 to 3.0 it appears that log4j.xml is read and configured when log4j.debug is on, but even though I turned off all the INFO level messages from org.springframework these all now appear in 3.0 where as they did not in 2.5.
Ryan says:
Added on December 12th, 2009 at 12:25 amI've recently started using logback as an alternative to log4j. It has native support for slf4j, and the config/api is very similar to log4j as an added bonus.
Dave Syer (blog author) says:
Added on December 17th, 2009 at 10:47 am@Robert: I edited your post because Wordpress ate your XML formatting. I had to gues what the original said, but it looks to me like the same set of dependencies as in the main article, unless I missed something. You just replaced the exlcusion with an empty jar (which is the "version 99" approach).
@Tom: someone on the forum reported the same issue and I think it turned out that log4j wasn't being used because of another logging library that was pulled in by Hibernate (or another external library). Check your classpath carefully.
Rusty Wright says:
Added on December 17th, 2009 at 2:53 pmAs an addendum to what Dave suggested to Tom, if you're using maven, you can see where a dependency is coming from by doing "mvn dependency:tree". The rule of thumb with maven is that if you're explicitly using some package/jar/dependency, then you should explicitly list it in your maven dependencies; don't just let arrive in your project as a side effect of it being pulled in by something else.
Robert Glover says:
Added on December 17th, 2009 at 3:40 pmI immediately stopped using my own (inferior) solution in favor of Tony Dalbrekt's far better solution as soon as I saw his post. In fact, just today I added the following to a maven parent pom I was modifying:
<!–
See the blog entry at the following address for
an explanation how/why slf4j is being used instead of
commons-logging:
http://blog.springsource.com/2009/12/04/logging-dependencies-in-spring/
–>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>