logging scala

logging - Iniciando sesión en Scala



(13)

envoltorios slf4j

La mayoría de las bibliotecas de registro de Scala han sido algunos envoltorios alrededor de un marco de registro de Java (slf4j, log4j, etc.), pero a partir de marzo de 2015, las bibliotecas de registro supervivientes son todas slf4j. Estas bibliotecas de registro proporcionan algún tipo de objeto de log al que puede llamar info(...) , debug(...) , etc. No soy un gran admirador de slf4j, pero ahora parece ser el marco de registro predominante . Aquí está la descripción de SLF4J :

Simple Logging Facade para Java o (SLF4J) sirve como una simple fachada o abstracción para varios marcos de registro, por ejemplo, java.util.logging, log4j y logback, lo que permite al usuario final conectar el marco de trabajo de registro deseado en el momento del despliegue.

La capacidad de cambiar la biblioteca de registro subyacente en el momento del despliegue proporciona una característica única a toda la familia de registradores slf4j, que debe tener en cuenta:

  1. classpath como enfoque de configuración . La forma en que slf4j sabe qué biblioteca de registro subyacente está utilizando es cargando una clase por algún nombre. He tenido problemas en los que slf4j no reconoció mi registrador cuando se personalizó el cargador de clases.
  2. Debido a que la fachada simple intenta ser el denominador común, se limita solo a las llamadas de registro reales. En otras palabras, la configuración no se puede hacer a través del código.

En un proyecto grande, en realidad podría ser conveniente poder controlar el comportamiento de registro de dependencias transitivas si todos usaran slf4j.

Registro de Scala

Scala Logging está escrito por Heiko Seeberger como sucesor de su slf4s . Utiliza macro para expandir las llamadas a la expresión if para evitar una llamada de registro potencialmente costosa.

Scala Logging es una biblioteca de registro conveniente y de rendimiento que abarca bibliotecas de registro como SLF4J y potencialmente otras.

Registradores históricos

  • Logula , un contenedor de Log4J escrito por Coda Hale. Solía ​​gustarme este, pero ahora está abandonado.
  • configgy , un contenedor java.util.logging que solía ser popular en los primeros días de Scala. Ahora abandonado.

¿Cuál es una buena forma de iniciar sesión en una aplicación Scala? Algo que es consistente con la filosofía del lenguaje, no satura el código, es de bajo mantenimiento y discreto. Aquí hay una lista de requisitos básicos:

  • sencillo
  • no satura el código Scala es genial por su brevedad. No quiero que la mitad de mi código sea una declaración de registro
  • el formato de registro se puede cambiar para adaptarse al resto de mis registros de empresa y software de supervisión
  • admite niveles de registro (es decir, depuración, rastreo, error)
  • puede iniciar sesión en el disco, así como otros destinos (es decir, socket, consola, etc.)
  • configuración mínima, si hay alguna
  • funciona en contenedores (es decir, servidor web)
  • (opcional, pero agradable de tener) viene como parte del lenguaje o como un artefacto experto, así que no tengo que hackear mis compilaciones para usarlo

Sé que puedo usar las soluciones de registro de Java existentes, pero fallan en al menos dos de las anteriores, a saber, desorden y configuración.

Gracias por tus respuestas.


No use Logula

De hecho, he seguido la recomendación de Eugene y lo intenté y descubrí que tiene una configuración torpe y está sujeto a errores que no se corrigen (como este ). No parece estar bien mantenido y no es compatible con Scala 2.10 .

Utilice slf4s + slf4j-simple

Beneficios clave:

  • Admite la última versión de Scala 2.10 (hasta la fecha es M7)
  • La configuración es versátil pero no podría ser más simple. Se hace con las propiedades del sistema , que puede establecer añadiendo algo así como -Dorg.slf4j.simplelogger.defaultlog=trace al comando de ejecución o hardcode en su script: System.setProperty("org.slf4j.simplelogger.defaultlog", "trace") . ¡No es necesario administrar los archivos de configuración basura!
  • Encaja muy bien con IDEs. Por ejemplo, para establecer el nivel de registro en "rastreo" en una configuración de ejecución específica en IDEA, vaya a Run/Debug Configurations y agregue -Dorg.slf4j.simplelogger.defaultlog=trace a las VM options .
  • Fácil configuración: solo ingrese las dependencias desde la parte inferior de esta respuesta

Esto es lo que necesita para ejecutarlo con Maven:

<dependency> <groupId>com.weiglewilczek.slf4s</groupId> <artifactId>slf4s_2.9.1</artifactId> <version>1.0.7</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.6.6</version> </dependency>


Así es como conseguí que Scala Logging funcionara para mí:

Pon esto en tu build.sbt :

libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.7.2", libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3"

Luego, después de hacer una sbt update , esto imprime un mensaje de registro amigable:

import com.typesafe.scalalogging._ object MyApp extends App with LazyLogging { logger.info("Hello there") }

Si está usando Play, puede simplemente import play.api.Logger para escribir mensajes de registro: Logger.debug("Hi") .

Ver los docs para más información.


Con Scala 2.10+ Considere ScalaLogging by Typesafe. Utiliza macros para entregar una API muy limpia

https://github.com/typesafehub/scala-logging

Citando de su wiki:

Afortunadamente, las macros de Scala se pueden usar para facilitar nuestras vidas: ScalaLogging ofrece a la clase Logger con métodos de registro livianos que se ampliarán al idioma anterior. Entonces todo lo que tenemos que escribir es:

logger.debug(s"Some ${expensiveExpression} message!")

Después de que se haya aplicado la macro, el código se habrá transformado en la expresión idiomática descrita anteriormente.

Además, ScalaLogging ofrece el Logging características que proporciona de manera conveniente una instancia de Logger inicializada con el nombre de la clase mezclada en:

import com.typesafe.scalalogging.slf4j.LazyLogging class MyClass extends LazyLogging { logger.debug("This is very convenient ;-)") }


Debería echar un vistazo a la biblioteca scalax: http://scalax.scalaforge.org/ En esta biblioteca, hay un rasgo de registro, usando sl4j como back-end. Al usar este rasgo, puede iniciar sesión con bastante facilidad (solo use el campo de registro en la clase que hereda el rasgo).


Después de usar slf4s y Logula por un tiempo, escribí loglady , un rasgo de registro simple que envuelve slf4j.

Ofrece una API similar a la de la biblioteca de registro de Python, lo que hace que los casos comunes (cadena básica, formateo simple) sean triviales y evite dar formato a la repetición.

http://github.com/dln/loglady/


Encuentro muy conveniente el uso de algún tipo de logger java, sl4j por ejemplo, con scala wrapper simple, que me trae esa sintaxis

val #! = new Logger(..) // somewhere deep in dsl.logging. object User with dsl.logging { #! ! "info message" #! dbg "debug message" #! trace "var a=true" }

En mi opinión, es una combinación muy útil de marcos de registro de Java comprobados y la sintaxis de Scala.


Formas rápidas y fáciles.

Scala 2.10 y anterior:

import com.typesafe.scalalogging.slf4j.Logger import org.slf4j.LoggerFactory val logger = Logger(LoggerFactory.getLogger("TheLoggerName")) logger.debug("Useful message....")

Y build.sbt:

libraryDependencies += "com.typesafe" %% "scalalogging-slf4j" % "1.1.0"

Scala 2.11+ y más nuevo:

import import com.typesafe.scalalogging.Logger import org.slf4j.LoggerFactory val logger = Logger(LoggerFactory.getLogger("TheLoggerName")) logger.debug("Useful message....")

Y build.sbt:

libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0"


Saqué un poco de trabajo del rasgo de Logging de scalax y creé un rasgo que también integraba una biblioteca MessageFormat-based .

Entonces algo así se ve así:

class Foo extends Loggable { info( "Dude, I''m an {0} with {1,number,#}", "Log message", 1234 ) }

Nos gusta el enfoque hasta ahora.

Implementación:

trait Loggable { val logger:Logger = Logging.getLogger(this) def checkFormat(msg:String, refs:Seq[Any]):String = if (refs.size > 0) msgfmtSeq(msg, refs) else msg def trace(msg:String, refs:Any*) = logger trace checkFormat(msg, refs) def trace(t:Throwable, msg:String, refs:Any*) = logger trace (checkFormat(msg, refs), t) def info(msg:String, refs:Any*) = logger info checkFormat(msg, refs) def info(t:Throwable, msg:String, refs:Any*) = logger info (checkFormat(msg, refs), t) def warn(msg:String, refs:Any*) = logger warn checkFormat(msg, refs) def warn(t:Throwable, msg:String, refs:Any*) = logger warn (checkFormat(msg, refs), t) def critical(msg:String, refs:Any*) = logger error checkFormat(msg, refs) def critical(t:Throwable, msg:String, refs:Any*) = logger error (checkFormat(msg, refs), t) } /** * Note: implementation taken from scalax.logging API */ object Logging { def loggerNameForClass(className: String) = { if (className endsWith "$") className.substring(0, className.length - 1) else className } def getLogger(logging: AnyRef) = LoggerFactory.getLogger(loggerNameForClass(logging.getClass.getName)) }



Usar slf4j y una envoltura es bueno, pero el uso de la interpolación construida se rompe cuando tienes más de dos valores para interpolar, ya que entonces necesitas crear una matriz de valores para interpolar.

Una solución más similar a Scala es usar un procesador o clúster para retrasar la concatenación del mensaje de error. Un buen ejemplo de esto es el registrador de Lift

Log.scala Slf4jLog.scala

Que se ve así:

class Log4JLogger(val logger: Logger) extends LiftLogger { override def trace(msg: => AnyRef) = if (isTraceEnabled) logger.trace(msg) }

Tenga en cuenta que msg es una llamada por nombre y no se evaluará a menos que isTraceEnabled sea verdadero, por lo que no hay costo en la generación de una buena cadena de mensajes. Esto funciona alrededor del mecanismo de interpolación del slf4j que requiere analizar el mensaje de error. Con este modelo, puede interpolar cualquier cantidad de valores en el mensaje de error.

Si tiene un rasgo separado que mezcla este Log4JLogger en su clase, entonces puede hacer

trace("The foobar from " + a + " doesn''t match the foobar from " + b + " and you should reset the baz from " + c")

en lugar de

info("The foobar from {0} doesn''t match the foobar from {1} and you should reset the baz from {c}, Array(a, b, c))


Uso el SLF4J + Logback classic y lo utilizo así:

trait Logging { lazy val logger = LoggerFactory.getLogger(getClass) implicit def logging2Logger(anything: Logging): Logger = anything.logger }

Entonces puede usarlo lo que mejor se adapte a su estilo:

class X with Logging { logger.debug("foo") debug("bar") }

pero este enfoque, por supuesto, usa una instancia de registrador por instancia de clase.


Writer , Monoid y una implementación de Monad .