If you have multiple logging implementations on the classpath, one will be picked in a non-deterministic ("random") way. You may see logging like this.
15:04:56.264 [ERROR] [system.err] SLF4J: Class path contains multiple SLF4J bindings.
15:04:56.265 [ERROR] [system.err] SLF4J: Found binding in [jar:file:/home/max/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-log4j12/1.7.6/6953717b9850aeb26d1b8375ca07dbd9c50eca4e/slf4j-log4j12-1.7.6.jar!/org/slf4j/impl/StaticLoggerBinder.class]
15:04:56.265 [ERROR] [system.err] SLF4J: Found binding in [jar:file:/home/max/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.2.3/7c4f3c474fb2c041d8028740440937705ebb473a/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
15:04:56.265 [ERROR] [system.err] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
15:04:56.276 [ERROR] [system.err] SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
15:04:56.286 [ERROR] [system.err] Exception in thread "main" java.lang.StackOverflowError
15:04:56.288 [ERROR] [system.err] at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936)
15:04:56.288 [ERROR] [system.err] at org.slf4j.impl.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:58)
15:04:56.288 [ERROR] [system.err] at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:358)
15:04:56.288 [ERROR] [system.err] at org.apache.log4j.Category.<init>(Category.java:57)
15:04:56.288 [ERROR] [system.err] at org.apache.log4j.Logger.<init>(Logger.java:37)
15:04:56.288 [ERROR] [system.err] at org.apache.log4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:43)
15:04:56.288 [ERROR] [system.err] at org.apache.log4j.LogManager.getLogger(LogManager.java:45)
15:04:56.288 [ERROR] [system.err] at org.slf4j.impl.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:66)
This document describes how you can fix a problem like this.
./gradlew clean syncRuntimeDependencies
This will put all the JARs into ./build/deps/runtime/
and sibling folders for other configurations (like "test").
grep -l StaticLoggerBinder ./build/deps/runtime/*.jar
Example output:
./build/deps/runtime/logback-classic-1.2.3.jar
./build/deps/runtime/slf4j-log4j12-1.7.6.jar
So, we have two! That's the problem. We want there to be only one!
Decide which one you want to keep and filter the other one out. Put this in your build.gradle
:
configurations.all {
// We don't want to pull in this, ever. Dropwizard uses ch.qos.logback:logback-core
exclude group: 'org.slf4j', module: 'slf4j-log4j12'
}
Now clean, sync and do the grep again. You should have only one logging implementation.
Example output:
./build/deps/runtime/logback-classic-1.2.3.jar
PROFIT.
The task
syncRuntimeDependencies
is not standard Gradle. It is defined by the following code (in build.gradle):I usually add it to all my projects, as trouble-shooting tool for class path related problems.