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.
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:
- 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.
- 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
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 ;-)")
}
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
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))
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
aVM 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>
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")
.
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))
}
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.
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).
Writer
, Monoid
and a Monad
implementation.
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:
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.
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.
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"
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