Как заставить работать логи в юнит-тестах scala с testng, slf4s и logback - PullRequest
19 голосов
/ 26 октября 2011

Я новичок в 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, но я не вижу, как создается мой регистратор до того, как настроена подсистема журналирования.

ОБНОВЛЕНИЕ : Вот содержимое моего 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 ]

19 голосов
/ 23 августа 2012

Я думаю, это потому, что 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")
)
0 голосов
/ 15 июля 2017

Проблема заключается в том, что в то время как первый поток инициализирует базовую реализацию ведения журнала (блокирование), для всех других одновременных потоков SubstituteLoggerFactory создается . Эта фабрика заменителей логеров возвращает SubstituteLogger вместо фактической реализации логгера. Эта проблема не решена в SLF4J-167 .

Меньше вероятность встретить эту проблему в Java, потому что часто объекты логгера создаются как статическая переменная, поэтому LoggerFactory инициализируется во время загрузки класса. В Scala нет статического модификатора, а сопутствующие объекты инициализируются лениво. Кроме того, большинство сред тестирования в Scala выполняют тесты параллельно.

Чтобы обойти эту проблему, вы можете изменить среду тестирования: как предложил Бруно Бит, вы можете инициализировать LoggerFactory до начала тестирования. Вы можете сделать это также в тестовом коде, а не в настройках сборки. Вы также можете настроить запуск теста последовательно, но тогда вы потеряете скорость.

В качестве альтернативы вы можете легко инициализировать Logger, инициализированный в сопутствующем объекте. Уродливо, но в большинстве случаев гарантирует, что объекты Foo, созданные одновременно, не будут инициализированы с помощью SubstituteLogger.

class Foo {
  val logger = Foo.singletonLogger
}

object Foo {
  val singletonLogger = LoggerFactory.getLogger(getClass)
}
0 голосов
/ 16 декабря 2014

У меня была такая же проблема.Закончилось только создание экземпляра пустого регистратора в определении класса.

Если бы я применил свой метод к вашему коду, то это было бы,

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

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

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

Обратите внимание, что я очень плохо знаком сСкала, так что я не знаю всех последствий того, что я только что сделал.

0 голосов
/ 22 декабря 2011

slf4s 1.0.7 зависит от slf4j 1.6.1, как вы можете видеть [здесь] [1].Попробуйте использовать эту версию вместо 1.6.3 для других зависимостей slf4j.

...