如何使用 testng、slf4s 和 logback 在 Scala 单元测试中进行日志记录
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7898273/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get logging working in scala unit tests with testng, slf4s, and logback
提问by anelson
I'm new to Scala, and not that familiar with recent developments in Java, so I am having what I assume is a basic problem.
我是 Scala 的新手,对 Java 的最新发展不太熟悉,所以我认为这是一个基本问题。
I'm writing some Scala code, and testing it with test fixtures using ScalaTest and TestNG. The code under test uses slf4s to perform its logging, backed by logback.
我正在编写一些 Scala 代码,并使用 ScalaTest 和 TestNG 使用测试装置对其进行测试。被测代码使用 slf4s 执行其日志记录,由 logback 支持。
In my 'build.sbt' file I have dependencies for all the libraries I need:
在我的“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"
I perform logging like this (code simplified for readability):
我像这样执行日志记录(为了可读性而简化了代码):
class MyClass extends Logging {
def doSomething() {
logger.debug("Hello world")
}
}
when I run a test that exercises this code (using the 'sbt test' command) I do not see the debug message, but I do see this printed to the console:
当我运行执行此代码的测试(使用“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
I have a logback.xml file in src/test/resources, and I know logging itself is working as I see output from the Apache HttpClient library (which uses JCL).
我在 src/test/resources 中有一个 logback.xml 文件,当我看到来自 Apache HttpClient 库(使用 JCL)的输出时,我知道日志记录本身正在工作。
Am I missing something? The information I'm logging is helpful in exploring the behavior of my code with tests, and besides it seems like this should work. I have of course read the page at http://www.slf4j.org/codes.html#substituteLoggerbut I don't see how my logger is getting created before the logging subsystem has been configured.
我错过了什么吗?我记录的信息有助于通过测试探索我的代码的行为,此外,这似乎应该有效。我当然已经阅读了http://www.slf4j.org/codes.html#substituteLogger 上的页面,但是在配置日志子系统之前我没有看到我的记录器是如何创建的。
UPDATE: Here is the contents of my logback.xml:
更新:这是我的 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>
采纳答案by Bruno Bieth
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!
我认为这是因为 SBT 并行运行测试并且 Slf4j 中的一些初始化代码不是线程安全的(!)。参见http://jira.qos.ch/browse/SLF4J-167... 2 多年前就有报道了!
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:
作为一种解决方法,我通过在测试运行之前加载根记录器来初始化 Slf4j。为此,只需将其添加到您的 SBT 设置中:
testOptions += Setup( cl =>
cl.loadClass("org.slf4j.LoggerFactory").
getMethod("getLogger",cl.loadClass("java.lang.String")).
invoke(null,"ROOT")
)
回答by casey
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.
请注意,我对 Scala 很陌生,所以我不知道我刚刚所做的事情的全部含义。
回答by Ohad Bruker
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.
问题是,当第一个线程初始化底层日志实现(阻塞)时,对于所有其他并发线程,创建了 SubstituteLoggerFactory。这个替代记录器工厂返回一个 SubstituteLogger 而不是实际的记录器实现。此问题未在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.
在 Java 中不太可能遇到这个问题,因为通常 logger 对象被创建为静态变量,因此 LoggerFactory 在类加载期间被初始化。在 Scala 中没有静态修饰符,并且伴随对象被延迟初始化。此外,Scala 中的大多数测试框架都并行执行测试。
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.
要解决此问题,您可以更改测试环境:正如 Bruno Bieth 建议的那样,您可以在测试开始之前初始化 LoggerFactory。您也可以在测试代码而不是构建设置中执行此操作。您还可以将测试设置为按顺序运行,但随后会降低速度。
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.
或者,您可以急切地初始化在伴随对象中初始化的 Logger。丑陋,但在大多数情况下确保并发创建的 Foo 对象不会被 SubstituteLogger 初始化。
class Foo {
val logger = Foo.singletonLogger
}
object Foo {
val singletonLogger = LoggerFactory.getLogger(getClass)
}
回答by Heiko Seeberger
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.
slf4s 1.0.7 依赖于 slf4j 1.6.1,如您所见[此处][1]。尝试将此版本而不是 1.6.3 用于其他 slf4j 依赖项。

