Как получить журнал, работающий в scala модульных тестах с testng, slf4s и 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
У меня есть файл logback.xml в src/test/resources, и я знаю, что сам журнал работает, поскольку я вижу вывод из библиотеки 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>
Ответы
Ответ 1
Я думаю, это потому, что SBT запускает тесты параллельно, а некоторый код инициализации в Slf4j не является потокобезопасным (!).
См. http://jira.qos.ch/browse/SLF4J-167... было сообщено более 2 лет назад!
В качестве обходного пути я инициализирую Slf4j загрузкой корневого регистратора до запуска тестов. Для этого просто добавьте это в свои настройки SBT:
testOptions += Setup( cl =>
cl.loadClass("org.slf4j.LoggerFactory").
getMethod("getLogger",cl.loadClass("java.lang.String")).
invoke(null,"ROOT")
)
Ответ 2
slf4s 1.0.7 зависит от slf4j 1.6.1, как вы можете видеть [здесь] [1]. Попробуйте использовать эту версию вместо 1.6.3 для других зависимостей slf4j.
Ответ 3
У меня была такая же проблема. Закончилось просто создание пустого регистратора в определении класса.
Если бы я применил свой метод к вашему коду, это было бы,
import com.weiglewilczek.slf4s.{Logger, Logging}
class MyClass with Logging {
val _ = Logger("") // <--Solved problem
def doSomething() {
logger.debug("Hello world")
}
}
Обратите внимание, что я очень новичок в scala, поэтому не знаю, какие последствия я только что сделал.
Ответ 4
Проблема заключается в том, что, хотя первый поток инициализирует базовую реализацию (блокировку) ведения журнала, для всех других параллельных потоков, SubstituteLoggerFactory создается, Этот замещающий logger factory возвращает SubstituteLogger вместо фактической реализации регистратора. Эта проблема не решена в SLF4J-167.
Менее вероятно, чтобы эта проблема возникла на Java, потому что часто объекты журнала создаются как статическая переменная, поэтому LoggerFactory инициализируется во время загрузки класса. В Scala нет статического модификатора, а объекты сопутствующих объектов инициализируются лениво.
Кроме того, большинство тестовых фреймворков в Scala выполняют тесты параллельно.
Чтобы решить эту проблему, вы можете изменить тестовую среду: поскольку Бруно Биет предложил вам инициализировать LoggerFactory до начала тестирования. Вы можете сделать это также в тестовом коде, а не в настройках сборки. Вы также можете установить, что тест запускается последовательно, но затем вы теряете скорость.
В качестве альтернативы вы можете с нетерпением инициализировать Logger, инициализированный в сопутствующем объекте. Ужасно, но в большинстве случаев гарантирует, что объекты Foo, созданные одновременно, не будут инициализированы с помощью SubstituteLogger.
class Foo {
val logger = Foo.singletonLogger
}
object Foo {
val singletonLogger = LoggerFactory.getLogger(getClass)
}