2
votes

There are several questions on this subject but none that I could find relates to the Groovy @Slf4j "AST" annotation. Which on the face of it is not just convenient but gives you conditional execution of stuff depending on log level.

In my book, Groovy in Action, it says:

Injects an Slf4j logger as a static final org.slf4j.Logger into your class and initializes it using org.slf4j.LoggerFactory.getLogger(class) . The LogBack framework uses SLF4J as the underlying logger, so LogBack users should use @Slf4j

And that's what I'm doing. I've got this in my gradle.build

compile 'ch.qos.logback:logback-classic:1.2.1'

... and I've rustled up a simple logback-test.xml.

But each time I run a test class with this annotation, or run the app directly, I'm getting:

SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/D:/apps/... fbb0e/logback-classic-1.2.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/D:/apps/... eca4e/slf4j-log4j12-1.7.6.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 [ch.qos.logback.classic.util.ContextSelectorStaticBinder]

... in particular this is sort of "polluting" my stderr output of my tests.

I tried manually deleting the slf4j-log4j12-1.7.6.jar file ... but it got automatically regenerated next Gradle task.

Jon Skeet is one of the authors of this book. Yes, THE Jon Skeet. Help Jon!

in answer to the two comments (thanks!)
... here is the output

+--- org.codehaus.groovy:groovy-all:2.6.0-alpha-2
+--- net.bytebuddy:byte-buddy:1.6.11
+--- commons-io:commons-io:2.6
+--- org.apache.odftoolkit:simple-odf:0.8.2-incubating
|    +--- org.apache.odftoolkit:odfdom-java:0.8.11-incubating
|    |    +--- org.apache.odftoolkit:taglets:0.8.11-incubating
|    |    +--- xerces:xercesImpl:2.9.1 -> 2.11.0
|    |    |    \--- xml-apis:xml-apis:1.4.01
|    |    +--- xml-apis:xml-apis:1.3.04 -> 1.4.01
|    |    +--- org.apache.jena:jena-core:2.11.2
|    |    |    +--- org.slf4j:slf4j-api:1.7.6 -> 1.7.22
|    |    |    +--- org.apache.jena:jena-iri:1.0.2
|    |    |    |    +--- org.slf4j:slf4j-api:1.7.6 -> 1.7.22
|    |    |    |    \--- log4j:log4j:1.2.17
|    |    |    +--- xerces:xercesImpl:2.11.0 (*)
|    |    |    \--- log4j:log4j:1.2.17
|    |    +--- net.rootdev:java-rdfa:0.4.2
|    |    |    +--- org.apache.jena:jena-iri:0.9.1 -> 1.0.2 (*)
|    |    |    \--- org.slf4j:slf4j-api:1.5.6 -> 1.7.22
|    |    \--- commons-validator:commons-validator:1.5.0
|    |         +--- commons-beanutils:commons-beanutils:1.9.2
|    |         |    +--- commons-logging:commons-logging:1.1.1 -> 1.2
|    |         |    \--- commons-collections:commons-collections:3.2.1 -> 3.2.2
|    |         +--- commons-digester:commons-digester:1.8.1
|    |         +--- commons-logging:commons-logging:1.2
|    |         \--- commons-collections:commons-collections:3.2.2
|    +--- xerces:xercesImpl:2.9.1 -> 2.11.0 (*)
|    \--- xml-apis:xml-apis:1.3.04 -> 1.4.01
+--- org.apache.lucene:lucene-core:6.+ -> 6.6.2
+--- org.apache.lucene:lucene-analyzers-common:6.+ -> 6.6.2
|    \--- org.apache.lucene:lucene-core:6.6.2
+--- org.apache.lucene:lucene-queryparser:6.+ -> 6.6.2
|    +--- org.apache.lucene:lucene-core:6.6.2
|    +--- org.apache.lucene:lucene-queries:6.6.2
|    \--- org.apache.lucene:lucene-sandbox:6.6.2
+--- commons-io:commons-io:2.5 -> 2.6
\--- ch.qos.logback:logback-classic:1.2.1
     +--- ch.qos.logback:logback-core:1.2.1
     \--- org.slf4j:slf4j-api:1.7.22

Clearly, Szymon Stepniak's comment is right. Now I just have to find out how to exclude this use of log4j by the culprit (odftoolkit) package ... and here I find someone wanting to do precisely that.

2
Could you add the output of ./gradlew dependencies --configuration runtime to your question?larsgrefer
One (or many) dependency adds slf4j-log4j12 to your classpath. The solution is to track it down and exclude it in build.gradle. Without knowing your dependencies we can't tell you which one of them adds slf4j-log4j12 to the classpath.Szymon Stepniak
@SzymonStepniak Thanks - see edit. Would you like to turn your comment into an answer? Otherwise I'll make one myself... (or maybe I should delete/mark as a dupe?)mike rodent
@mikerodent Cool, awesome we could help you :)Szymon Stepniak

2 Answers

2
votes

This error message you see in the console has actually nothing to do with @Slf4j annotation used in your code - it just simplifies adding log field to your class. Your problem is caused by multiple org.slf4j.impl.StaticLoggerBinder classes found in your classpath. To fix it you have to firstly check your dependencies tree using:

gradle dependencies --configuration runtime

It will display a full tree of all dependencies. Than you can track down which dependency is adding additional logger to your classpath. As you mentioned in the comment, in your case it is org.apache.odftoolkit:simple-odf:0.8.2-incubating adding log4j dependency.

Knowing that you have to exclude transitive dependencies of group org.slf4j and log4j that come with org.apache.odftoolkit:simple-odf:0.8.2-incubating. Define this dependency in your build.gradle file as follows:

compile ("org.apache.odftoolkit:simple-odf:0.8.2-incubating") {
    exclude group: 'org.slf4j'
    exclude group: 'log4j'
}
2
votes

Just for interest, although Szymon Stepniak's solution is probably the best way to go, there is another solution, which I essentially obtained from this question:

configurations {
    runtime.exclude group: "org.slf4j", module: "slf4j-log4j12"
}

... I suppose this might have the advantage that you don't have to deal with the exclusions for each offending package individually ... ?