Alex
Alex

Reputation: 8313

Gradle & SLF4J refusing to exclude transitive dependencies results in IllegalStateException

I am getting this error when running unit tests on my project:

Caused by: java.lang.IllegalStateException: Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError. See also http://www.slf4j.org/codes.html#log4jDelegationLoop for more details.
    at org.slf4j.impl.Log4jLoggerFactory.<clinit>(Log4jLoggerFactory.java:54)
    ... 29 more

I need SLF4J, but not the clash that the following dependencies is setting up:

dependencies {
    compile("org.springframework:spring-core:$springVersion")
    compile("org.springframework:spring-jdbc:$springVersion")
//    compile("org.springframework:spring-orm:$springVersion"
    compile("org.springframework:spring-tx:$springVersion")
    compile("org.springframework.data:spring-data-jpa:1.10.1.RELEASE") {
        exclude module: "slf4j-api"
        exclude module: "slf4j-over-slf4j"
        exclude module: "jcl-over-slf4j"
    }
    // tag::jetty[]
    compile("org.springframework.boot:spring-boot-starter-web:1.3.5.RELEASE") {
        exclude module: "spring-boot-starter-tomcat"
        exclude module: "slf4j-api"
        exclude module: "slf4j-over-slf4j"
        exclude module: "jcl-over-slf4j"
    }
    compile("org.springframework.boot:spring-boot-starter-jetty")
    // end::jetty[]
    // tag::actuator[]
    compile("org.springframework.boot:spring-boot-starter-actuator")
    // end::actuator[]
    testCompile("junit:junit")
    compile(group: 'org.hibernate.javax.persistence', name: 'hibernate-jpa-2.1-api', version: '1.0.0.Final')
    compile("org.apache.velocity:velocity:1.7")
    compile(group: 'org.slf4j', name: 'slf4j-api', version: '1.7.21')
    compile('org.slf4j:log4j-over-slf4j:1.7.21')
    compile(group: 'org.projectlombok', name: 'lombok', version: '1.16.8')
    testCompile("org.springframework.boot:spring-boot-starter-test")
    testCompile(group: 'junit', name: 'junit', version: '4.11')
}

I can stop the error via:

configurations {
  all*.exclude group: "org.slf4j"

}

but then I get ClassNotFoundExceptions at runtime because slf4j is needed (and its adapters).

Any ideas? I just want to exclude slf4j from Spring's dependencies. I have seen plenty of threads on this issue but not resolve it -- are my exclusions correct?

Upvotes: 4

Views: 8772

Answers (2)

Alex
Alex

Reputation: 8313

I just excluded spring-boot-starter-logging:

configurations {
    all*.exclude module: 'spring-boot-starter-logging'
}

My dependencies now look like this:

dependencies {
    compile "org.springframework:spring-core:$springVersion"
    compile "org.springframework:spring-jdbc:$springVersion"
    compile "org.springframework:spring-orm:$springVersion"
    compile "org.springframework:spring-tx:$springVersion"
    compile "org.springframework.data:spring-data-jpa:1.10.1.RELEASE"
    compile("org.springframework.boot:spring-boot-starter-web") {
        exclude module: "spring-boot-starter-tomcat"
    }
    compile("org.springframework.boot:spring-boot-starter-jetty")
    compile group: 'org.hibernate.javax.persistence', name: 'hibernate-jpa-2.1-api', version: '1.0.0.Final'
    compile "org.apache.velocity:velocity:1.7"
    compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.21'
    compile group: 'org.slf4j', name: 'slf4j-log4j12', version: '1.7.21'
    compile group: 'org.projectlombok', name: 'lombok', version: '1.16.8'
    testCompile("junit:junit")
    testCompile "org.springframework.boot:spring-boot-starter-test"
    testCompile group: 'junit', name: 'junit', version: '4.11'
}

Upvotes: 12

Vampire
Vampire

Reputation: 38724

Your problem is, that you have log4j-over-slf4j - which redirects all log4j logging to slf4j - and slf4j-log4j12 - which outputs all slf4j logging to log4j - in your classpath. This means you would have an endless loop which is wrong of course.

So you have to decide what you want. Do you want to redirect all log4j logging to slf4j and then direct all slf4j logging to some other logging framework (or slf4j-simple e. g. for logging to System.out or no binding to suppress the logging) or do you want to direct all slf4j logging to log4j, then it doesn't make sense to redirect log4j logging to slf4j in the first place.

How you correctly resolve your problem heavily depends on the desired result.
You can also find out what pulls in a dependency using the gradle task dependencyInsight.

Upvotes: 2

Related Questions