score:22

Accepted answer

I think it's because SBT runs the tests in parallel and some initialization code in Slf4j is not thread safe (!). See http://jira.qos.ch/browse/SLF4J-167 ... it's been reported more than 2 years ago!

As a workaround I initialize Slf4j by loading the root logger before the tests run. To do so just add this to your SBT settings:

testOptions += Setup( cl =>
   cl.loadClass("org.slf4j.LoggerFactory").
     getMethod("getLogger",cl.loadClass("java.lang.String")).
     invoke(null,"ROOT")
)

score:0

slf4s 1.0.7 depends on slf4j 1.6.1 as you can see [here][1]. Try to use this version instead of 1.6.3 for your other slf4j dependencies.

score:1

I had the same issue. Ended up just instantiating an empty logger in the class definition.

If I applied my method to your code then it would be,

import com.weiglewilczek.slf4s.{Logger, Logging}

class MyClass with Logging {
   val _ = Logger("") // <--Solved problem

   def doSomething() {
      logger.debug("Hello world")
  }
}

Note that I am very new to scala so I don't know the full implications of what I have just done.

score:1

The problem is that while the first thread is initializing the underlying logging implementation (blocking), for all other concurrent threads, SubstituteLoggerFactory is created. This substitute logger factory returns a SubstituteLogger instead the actual logger implementation. This issue isn't resolved in SLF4J-167.

It is less likely to meet this issue in Java, because often logger objects are created as a static variable, so the LoggerFactory is being initialized during the class loading. In Scala there is no static modifier and companion objects are initialized lazily. Furthermore, most testing frameworks in Scala execute tests in parallel.

To workaround this issue, you can change the test environment: as Bruno Bieth suggested you can initialize the LoggerFactory before the tests start. You can do that also in the test code rather than the build settings. You can also set the test to run sequentially, but then you lose speed.

Alternatively you can eagerly initialize a Logger initialized in a companion object. Ugly, but in most cases ensures that Foo objects created concurrently won't be initialized with a SubstituteLogger.

class Foo {
  val logger = Foo.singletonLogger
}

object Foo {
  val singletonLogger = LoggerFactory.getLogger(getClass)
}

Related Query

More Query from same tag