Search code examples
scalaplayframeworksbtaether

Multiple SLF4J bindings with Play 2.3.8


I use Play Framework 2.3.8 (for Java) with Scala 2.11.

I get this warning:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/vdanylchuk/.ivy2/cache/org.slf4j/slf4j-simple/jars/slf4j-simple-1.7.7.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/vdanylchuk/.ivy2/cache/ch.qos.logback/logback-classic/jars/logback-classic-1.0.13.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.

This causes real problems at run time in a random way. On some of the deployed hosts, all goes well and the logs are written as configured. On the other hosts, the wrong binding wins, and the logs are written in the wrong location. The deployment process is scripted and exactly the same. So the warning at http://www.slf4j.org/codes.html#multiple_bindings is correct, and this is random, so I need to remove the extra binding.

The fun thing is, they both come from the Play framework. logback-classic (which I actually want to use) comes from the play library, and slf4j-simple comes from the play sbt plugin.

I read a lot of similar questions here and in the mailing lists. Example: How to fix "SLF4J: Class path contains multiple SLF4J bindings" at startup of Play 2.3.x? The common solution is to use some form of exclude rules. None of the suggested solutions worked for me. [Update: in fact, they do - see the solution below.] I would not expect slf4j-simple to come up in the final classpath, but it does. Even though I added excludeAll(ExclusionRule(organization = "org.slf4j")) to every single dependency in my project, except the play framework.

Any ideas on how to get rid of slf4j-simple? Preferably on sbt project level, without manually cleaning up the classpath of the build result.

Update: instructions to reproduce

I have narrowed it down with a small test project. Turns out it is triggered by a combination of play sbt plugin and the aether-deploy plugin that we use. It is sufficient to have this small configuration.
build.sbt:

name := "slf4j-test"
version := "1.0"
scalaVersion := "2.11.5"
lazy val root = (project in file(".")).enablePlugins(PlayJava)


project/plugins.sbt:

resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.8")
addSbtPlugin("no.arktekk.sbt" % "aether-deploy" % "0.13")


project/build.properties:

sbt.version=0.13.7

Then just run "sbt test:compile" or "sbt run" (and query localhost:9000) to see the warning. It works the same with scala 2.10.


Solution

  • My bad. In the end, a simple exclusion in project/plugins.sbt fixed this:

    addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.8" exclude("org.slf4j", "slf4j-simple"))
    

    I tried this earlier, but apparently made a syntax mistake, and thought this was not supported. facepalm