intellij - Cómo hacer que el registro funcione en scala unit testing con testng, slf4s y logback
intellij scala tutorial (4)
Soy nuevo en Scala, y no estoy familiarizado con los desarrollos recientes en Java, así que tengo un problema básico que supongo.
Estoy escribiendo un código Scala y lo estoy probando con dispositivos de prueba utilizando ScalaTest y TestNG. El código bajo prueba utiliza slf4s para realizar su registro, respaldado por el registro de retorno.
En mi archivo ''build.sbt'' tengo dependencias para todas las bibliotecas que necesito:
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"
Realizo el registro de esta manera (código simplificado para facilitar la lectura):
class MyClass extends Logging {
def doSomething() {
logger.debug("Hello world")
}
}
cuando ejecuto una prueba que ejerce este código (usando el comando ''sbt test'') no veo el mensaje de depuración, pero veo esto impreso en la consola:
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
Tengo un archivo logback.xml en src / test / resources, y sé que el registro en sí funciona, ya que veo el resultado de la biblioteca de Apache HttpClient (que usa JCL).
¿Me estoy perdiendo de algo? La información que estoy registrando es útil para explorar el comportamiento de mi código con las pruebas, y además parece que esto debería funcionar. Por supuesto, he leído la página en http://www.slf4j.org/codes.html#substituteLogger pero no veo cómo se crea mi registrador antes de que se haya configurado el subsistema de registro.
ACTUALIZACIÓN : Aquí está el contenido de mi 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>
Creo que es porque SBT ejecuta las pruebas en paralelo y algunos códigos de inicialización en Slf4j no son seguros para subprocesos (!). Consulte http://jira.qos.ch/browse/SLF4J-167 ... ¡se informó hace más de 2 años!
Como solución alternativa, inicializo Slf4j cargando el registrador raíz antes de que se ejecuten las pruebas. Para hacerlo solo agrega esto a tu configuración SBT:
testOptions += Setup( cl =>
cl.loadClass("org.slf4j.LoggerFactory").
getMethod("getLogger",cl.loadClass("java.lang.String")).
invoke(null,"ROOT")
)
El problema es que mientras el primer subproceso está inicializando la implementación de registro subyacente (bloqueo), para todos los demás subprocesos concurrentes, se crea SubstituteLoggerFactory . Esta fábrica de registradores sustitutos devuelve un Sustituto Registrador en lugar de la implementación del registrador real. Este problema no se resuelve en SLF4J-167 .
Es menos probable que resuelva este problema en Java, porque a menudo los objetos del registrador se crean como una variable estática, por lo que el LoggerFactory se está inicializando durante la carga de la clase. En Scala no hay modificador estático y los objetos complementarios se inicializan perezosamente. Además, la mayoría de los marcos de prueba en Scala ejecutan pruebas en paralelo.
Para solucionar este problema, puede cambiar el entorno de prueba: como Bruno Bieth sugirió que puede inicializar LoggerFactory antes de que comiencen las pruebas. Puede hacerlo también en el código de prueba en lugar de en la configuración de compilación. También puede configurar la prueba para que se ejecute secuencialmente, pero luego pierde velocidad.
Alternativamente, puede inicializar con entusiasmo un registrador inicializado en un objeto complementario. Feo, pero en la mayoría de los casos garantiza que los objetos Foo creados simultáneamente no se inicialicen con un Sustituto Registrador.
class Foo {
val logger = Foo.singletonLogger
}
object Foo {
val singletonLogger = LoggerFactory.getLogger(getClass)
}
Tuve el mismo problema. Terminé simplemente creando una instancia de un registrador vacío en la definición de clase.
Si aplicara mi método a su código, entonces sería
import com.weiglewilczek.slf4s.{Logger, Logging}
class MyClass with Logging {
val _ = Logger("") // <--Solved problem
def doSomething() {
logger.debug("Hello world")
}
}
Tenga en cuenta que soy muy nuevo en Scala, por lo que no sé todas las implicaciones de lo que acabo de hacer.
slf4s 1.0.7 depende de slf4j 1.6.1 como puede ver [aquí] [1]. Intente utilizar esta versión en lugar de 1.6.3 para sus otras dependencias de slf4j.