Как заставить работать логи в модульных тестах 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, но не вижу, как создается мой регистратор до того, как настроена подсистема журналирования.
ОБНОВЛЕНИЕ: Вот содержимое моего 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 ответа
Я думаю, это потому, что 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")
)
Проблема заключается в том, что пока первый поток инициализирует базовую реализацию ведения журнала (блокирование), для всех других параллельных потоков создается SubstituteLoggerFactory. Эта фабрика заменяющих логгеров возвращает SubstituteLogger вместо фактической реализации регистратора. Эта проблема не решена в SLF4J-167.
С меньшей вероятностью встретить эту проблему в Java, потому что часто объекты logger создаются как статические переменные, поэтому LoggerFactory инициализируется во время загрузки класса. В Scala отсутствует статический модификатор, и сопутствующие объекты инициализируются лениво. Кроме того, большинство сред тестирования в Scala выполняют тесты параллельно.
Чтобы обойти эту проблему, вы можете изменить среду тестирования: как предложил Бруно Бит, вы можете инициализировать LoggerFactory до начала тестирования. Вы можете сделать это также в тестовом коде, а не в настройках сборки. Вы также можете настроить тест на последовательное выполнение, но тогда вы потеряете скорость.
В качестве альтернативы вы можете легко инициализировать Logger, инициализированный в сопутствующем объекте. Ужасно, но в большинстве случаев гарантирует, что объекты Foo, созданные одновременно, не будут инициализированы с помощью SubstituteLogger.
class Foo {
val logger = Foo.singletonLogger
}
object Foo {
val singletonLogger = LoggerFactory.getLogger(getClass)
}
Я была такая же проблема. Закончилось просто создание пустого регистратора в определении класса.
Если бы я применил свой метод к вашему коду, то это было бы,
import com.weiglewilczek.slf4s.{Logger, Logging}
class MyClass with Logging {
val _ = Logger("") // <--Solved problem
def doSomething() {
logger.debug("Hello world")
}
}
Обратите внимание, что я очень плохо знаком с scala, поэтому я не знаю всех последствий того, что я только что сделал.
slf4s 1.0.7 зависит от slf4j 1.6.1, как вы можете видеть [здесь][1]. Попробуйте использовать эту версию вместо 1.6.3 для других зависимостей slf4j.