如何使用testng,slf4s和logback在scala单元测试中进行日志记录

时间:2011-10-26 02:58:18

标签: scala testng slf4j logback

我是Scala的新手,并不熟悉Java的最新发展,因此我认为这是一个基本问题。

我正在编写一些Scala代码,并使用ScalaTest和TestNG使用测试夹具对其进行测试。正在测试的代码使用slf4s来执行其日志记录,由logback支持。

在我的'build.sbt'文件中,我对所需的所有库都有依赖关系:

scalaVersion := "2.9.1"

// Add test dependencies on scalatest and testng

libraryDependencies ++= Seq("org.scalatest" %% "scalatest" % "1.6.1" % "test", "org.testng" % "testng" % "6.1.1" % "test")

// Use the slf4j logging facade for logging
libraryDependencies += "org.slf4j" % "slf4j-api" % "1.6.3"

//use the slf4j connectors to implement the JCL logging facade in terms of slf4j (which in turn is implemented in terms of logback)
//confused yet?
libraryDependencies += "org.slf4j" % "jcl-over-slf4j" % "1.6.3"

//use logback for the back-end slf4j logging impl.
libraryDependencies ++= Seq("ch.qos.logback" % "logback-core" % "0.9.30", "ch.qos.logback" % "logback-classic" % "0.9.30")

//use slf4s to expose the slf4j logging facade in scala

libraryDependencies += "com.weiglewilczek.slf4s" %% "slf4s" % "1.0.7"

//Add the dispatch HTTP client dependency

libraryDependencies ++= Seq(
  "net.databinder" %% "dispatch-http" % "0.8.5"
)

//I can't figure out how to use the dispatch HTTP client library, so just use the apache one

libraryDependencies += "org.apache.httpcomponents" % "httpclient" % "4.1.2"

我执行这样的日志记录(为了便于阅读而简化了代码):

class MyClass extends Logging {
   def doSomething() {
      logger.debug("Hello world")
  }
}

当我运行一个运行此代码的测试时(使用'sbt test'命令)我没有看到调试消息,但我确实看到这个打印到控制台:

    SLF4J: The following loggers will not work because they were created
SLF4J: during the default configuration phase of the underlying logging system.
SLF4J: See also http://www.slf4j.org/codes.html#substituteLogger
SLF4J: MyClass

我在src / test / resources中有一个logback.xml文件,我知道日志本身正在工作,因为我看到Apache HttpClient库(使用JCL)的输出。

我错过了什么吗?我正在记录的信息有助于通过测试探索我的代码的行为,此外它似乎应该工作。我当然在http://www.slf4j.org/codes.html#substituteLogger阅读了该页面,但我没有看到在配置日志记录子系统之前如何创建我的记录器。

UPDATE :这是我的logback.xml的内容:

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- encoders are assigned the type
     ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %line --- %msg%n</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

4 个答案:

答案 0 :(得分:19)

我认为这是因为SBT并行运行测试,Slf4j中的一些初始化代码不是线程安全的(!)。 见http://jira.qos.ch/browse/SLF4J-167 ...两年多前就有报道了!

作为一种解决方法,我通过在测试运行之前加载根记录器来初始化Slf4j。为此,只需将其添加到您的SBT设置:

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

答案 1 :(得分:0)

slf4s 1.0.7依赖于slf4j 1.6.1,你可以看到[这里] [1]。尝试使用此版本而不是1.6.3用于其他slf4j依赖项。

答案 2 :(得分:0)

我有同样的问题。结束只是在类定义中实例化一个空记录器。

如果我将我的方法应用于您的代码,那么它将是,

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

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

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

请注意,我对scala非常陌生,所以我不知道我刚才所做的全部含义。

答案 3 :(得分:0)

问题在于,当第一个线程正在初始化基础日志记录实现(阻塞)时,对于所有其他并发线程SubstituteLoggerFactory is created。此替代记录器工厂返回SubstituteLogger而不是实际的记录器实现。此问题未在SLF4J-167中解决。

在Java中遇到此问题的可能性较小,因为通常将记录器对象创建为静态变量,因此在类加载期间正在初始化LoggerFactory。在Scala中没有静态修饰符,并且伴随对象被懒惰地初始化。 此外,Scala中的大多数测试框架并行执行测试。

要解决此问题,您可以更改测试环境:正如Bruno Bieth建议您可以在测试开始之前初始化LoggerFactory。您也可以在测试代码而不是构建设置中执行此操作。您也可以将测试设置为按顺序运行,但随后会失去速度。

或者,您可以急切地初始化在伴随对象中初始化的Logger。很丑,但在大多数情况下确保同时创建的Foo对象不会被SubstituteLogger初始化。

class Foo {
  val logger = Foo.singletonLogger
}

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