Iniciar sesión en Scala


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

  • simple
  • no desordena el código. Scala es genial por su brevedad. No quiero que la mitad de mi código esté registrando declaraciones
  • el formato de registro se puede cambiar para que se ajuste al resto de los registros de mi empresa y el software de monitoreo
  • soporta niveles de registro (es decir, depuración, seguimiento, error)
  • puede iniciar sesión en el disco, así como en otros destinos (es decir, socket, consola, etc.)
  • configuración mínima, si la hay
  • funciona en contenedores (es decir, servidor web)
  • (opcional, pero agradable de tener) viene ya sea como parte del lenguaje o como un artefacto maven, por lo que no tengo que hackear mis compilaciones para usarlo

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

Gracias por sus respuestas.

 150
Author: George, 2009-06-11

13 answers

Envolturas Slf4j

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

La Fachada de Registro Simple para Java o (SLF4J) sirve como una simple fachada o abstracción para varios marcos de registro, por ejemplo, java.útil.logging, log4j y logback, lo que permite al usuario final conectar el marco de registro deseado en el momento de la implementación.

La capacidad de cambiar la biblioteca de registros subyacente en el momento de la implementación aporta una característica única a toda la familia de registradores slf4j, que debe tener en cuenta:

  1. classpath como aproximación de configuración. La forma en que slf4j sabe qué registro subyacente la biblioteca que está utilizando es cargando una clase con algún nombre. He tenido problemas en los que slf4j no reconoce mi registrador cuando classloader fue personalizado.
  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 usan slf4j.

Scala Logging

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 llamadas de registro potencialmente costosas.

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

Registradores históricos

  • Logula, un envoltorio Log4J escrito por Coda Hale. Solía gustarme este, pero ahora está abandonado.
  • configgy, un java.útil.envoltura de registro que solía ser popular en los primeros días de Scala. Ahora abandonado.
 113
Author: Eugene Yokota,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-04 09:46:41

Con Scala 2.10+ Considere el ScalaLogging por Typesafe. Utiliza macros para ofrecer una API muy limpia

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

Citando de su wiki:

Afortunadamente las macros de Scala se pueden usar para hacernos la vida más fácil: ScalaLogging ofrece la clase Logger con métodos de registro ligeros que se expandirán al modismo anterior. Así que todo lo que tenemos que escribir es:

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

Después de aplicar la macro, el código se han transformado en el idioma descrito anteriormente.

Además, ScalaLogging ofrece el rasgo Logging que proporciona convenientemente una instancia 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 ;-)")
}
 58
Author: fracca,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-12 18:22:03

Usar slf4j y un wrapper es bueno, pero el uso de it's built in interpolation se descompone cuando tiene más de dos valores para interpolar, desde entonces necesita crear una matriz de valores para interpolar.

Una solución más similar a Scala es usar un thunk o cluster para retrasar la concatenación del mensaje de error. Un buen ejemplo de esto es Lift logger

Tronco.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 un llamada por nombre y no será evaluada a menos que isTaceenabled sea true, por lo que no hay costo en la generación de una cadena de mensaje agradable. Esto funciona alrededor del mecanismo de interpolación de slf4j que requiere analizar el mensaje de error. Con este modelo, puede interpolar cualquier número de valores en el mensaje de error.

Si tienes un rasgo separado que mezcla este Log4JLogger en tu clase, entonces puedes 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))
 14
Author: Blair Zajac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-15 06:09:33

No utilice Logula

En realidad he seguido la recomendación de Eugene y lo probé y descubrí que tiene una configuración torpe y está sujeto a errores, que no se arreglan (como este ). No parece estar bien mantenido y no soporta Scala 2.10.

Use slf4s + slf4j-simple

Beneficios clave:

  • Soporta 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 propiedades del sistema, que puede establecer añadiendo algo como -Dorg.slf4j.simplelogger.defaultlog=trace al comando de ejecución o al código duro en su script: System.setProperty("org.slf4j.simplelogger.defaultlog", "trace"). No hay necesidad de administrar archivos de configuración trashy!
  • Encaja muy bien con los IDE. Por ejemplo, para establecer el nivel de registro en "trace" en una configuración de ejecución específica en IDEA, simplemente vaya a Run/Debug Configurations y agregue -Dorg.slf4j.simplelogger.defaultlog=trace a VM options.
  • Fácil instalación: simplemente coloque las dependencias desde la parte inferior de este respuesta

Esto es lo que necesitas 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>
 12
Author: Nikita Volkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-25 19:10:01

Así es como conseguí Scala Logging trabajando 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 un 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ás usando Play, puedes, por supuesto, simplemente import play.api.Logger para escribir mensajes de registro: Logger.debug("Hi").

Vea los documentos para más información.

 9
Author: Matthias Braun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-01 08:10:49

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

Entonces las cosas se ven así:

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

Nos gusta el enfoque hasta ahora.

Aplicació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))  
}
 7
Author: Tristan Juricek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-11 15:50:19

Utilizo SLF4J + Logback classic y lo aplico así:

trait Logging {
  lazy val logger = LoggerFactory.getLogger(getClass)

  implicit def logging2Logger(anything: Logging): Logger = anything.logger
}

Entonces puedes usarlo lo que mejor se ajuste a tu estilo:

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

Pero este enfoque, por supuesto, utiliza una instancia de logger por instancia de clase.

 6
Author: Kristof Jozsa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-21 06:09:44

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

 3
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-11 08:33:05

Writer, Monoid and a Monad implementation.

 3
Author: Tony Morris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-02 08:14:38

Aún no lo he probado, pero Configgy parece prometedor tanto para la configuración como para el registro:

Http://github.com/robey/configgy/tree/master

 3
Author: dberesford,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-20 16:20:11

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, que hace que los casos comunes (cadena básica, formato simple) sean triviales y evita la repetición del formato.

Http://github.com/dln/loglady/

 2
Author: dln,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-17 15:26:58

Me parece muy conveniente usar algún tipo de java logger, sl4j por ejemplo, con simple scala wrapper, lo que me trae dicha 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, una mezcla muy útil de los marcos de registro probados de Java y la sintaxis elegante de scala.

 2
Author: Alex Povar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-23 19:45:01

Formularios rápidos y fáciles.

Scala 2.10 y anteriores:

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

Y construir.sbt:

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

Scala 2.11+ y posterior:

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

Y construir.sbt:

libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0"
 1
Author: xgMz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-21 02:01:08