1. Overview
When we use SLF4J in our applications, we sometimes see a warning message about multiple bindings in the classpath printed to the console.
In this tutorial, we’ll try to understand why we see this message and how to resolve it.
2. Understanding the Warning
First, let’s look at a sample warning:
SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:.../slf4j-log4j12-1.7.21.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:.../logback-classic-1.1.7.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
This warning is telling us that SLF4J has found two bindings. One is in slf4j-log4j12-1.7.21.jar and the other in logback-classic-1.1.7.jar.
Now, let’s understand why we see this warning.
The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks. Thus, it allows us to plug in our desired logging framework at deployment time.
To achieve this, SLF4J looks for bindings (a.k.a providers) on the classpath. Bindings are basically implementations of a particular SLF4J class meant to be extended to plug in a specific logging framework.
By design, SLF4J will only bind with one logging framework at a time. Consequently, if more than one binding is present on the classpath, it will emit a warning.
It is worth noting that embedded components such as libraries or frameworks should never declare a dependency on any SLF4J binding. This is because when a library declares a compile-time dependency on an SLF4J binding, it imposes that binding on the end-user. Obviously, this negates SLF4J’s basic purpose. Consequently, they should only depend on the slf4j-api library.
It is also important to note that this is only a warning. If SLF4J finds multiple bindings, it will pick one logging framework from the list and bind with it. As can be seen on the last line of the warning, SLF4J has chosen Log4j by using org.slf4j.impl.Log4jLoggerFactory for the actual binding.
3. Finding the Conflicting JARs
The warning lists the locations of all the bindings it finds. Usually, this is sufficient information to identify the unscrupulous dependency that transitively pulls in an unwanted SLF4J binding into our project.
If it’s not possible to identify the dependency from the warning, we can use the dependency:tree maven goal:
mvn dependency:tree
This will display the dependency tree for the project:
[INFO] +- org.docx4j:docx4j:jar:3.3.5:compile [INFO] | +- org.slf4j:slf4j-log4j12:jar:1.7.21:compile [INFO] | +- log4j:log4j:jar:1.2.17:compile [INFO] +- ch.qos.logback:logback-classic:jar:1.1.7:compile [INFO] +- ch.qos.logback:logback-core:jar:1.1.7:compile
We’re using Logback for logging in our application. Hence, we’ve added the Logback binding, present in the logback-classic JAR, deliberately. But, the docx4j dependency has also pulled in another binding with the slf4j-log4j12 JAR.
4. Resolution
Now that we know the offending dependency, all that we need to do is exclude the slf4j-log4j12 JAR from the docx4j dependency:
<dependency> <groupId>org.docx4j</groupId> <artifactId>docx4j</artifactId> <version>${docx4j.version}</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> </exclusions> </dependency>
Since we’re not going to use Log4j, it might be a good idea to exclude it as well.
5. Conclusion
In this article, we saw how we can resolve the frequently seen warning about multiple bindings emitted by SLF4J.
The source code that accompanies this article is available over on GitHub.