logging - mvc - spring framework pdf español
iniciar sesión en scala (8)
En Java, la expresión estándar para el registro es crear una variable estática para un objeto logger y usar eso en los diversos métodos.
En Scala, parece que el modismo es crear un rasgo de Logging con un miembro logger y mezclar el rasgo en clases concretas. Esto significa que cada vez que se crea un objeto llama al marco de registro para obtener un registrador y también el objeto es más grande debido a la referencia adicional.
¿Existe alguna alternativa que permita la facilidad de uso de "con el registro" mientras se sigue utilizando una instancia del registrador por clase?
EDITAR: Mi pregunta no es sobre cómo se puede escribir un marco de trabajo de registro en Scala, sino cómo usar uno existente (log4j) sin incurrir en una sobrecarga de rendimiento (obtener una referencia para cada instancia) o complejidad del código. Además, sí, quiero usar log4j, simplemente porque usaré bibliotecas de terceros escritas en Java que probablemente usen log4j.
A veces, el registro en el nivel del paquete es la respuesta correcta. Scala lo hace más fácil que java porque los objetos se pueden definir directamente en un paquete. Si definiste un Log como este:
package example
object Log extends au.com.langdale.util.PackageLogger
Este registro está disponible en todas partes en el ejemplo del paquete. Para obtener un registro más detallado, puede dispersar definiciones similares alrededor de la jerarquía del paquete. O puede definir todos los registradores de paquetes juntos de esta manera:
package example {
import au.com.langdale.util.PackageLogger
object Log extends PackageLogger
package frobber {
object Log extends PackageLogger
package undulater {
object Log extends PackageLogger
}
}
}
La clase PackageLogger podría definirse de la siguiente manera (suponiendo SLF4J):
package au.com.langdale.util
import org.slf4j.LoggerFactory
class PackageLogger {
val name = { val c = getClass.getName; c.substring(0, c.lastIndexOf(''.'')) }
val inner = LoggerFactory.getLogger(name)
// various convenient logging methods follow....
def apply( mesg: => Any ) = inner.info(mesg.toString)
def info( mesg: String ) = inner.info(mesg)
def warn( mesg: String ) = inner.warn(mesg)
def error( mesg: String ) = inner.error(mesg)
}
Aquí hay un truco rápido (que en realidad no he estado usando, honesto; @)
object LogLevel extends Enumeration {
val Error = Value(" ERROR ")
val Warning = Value(" WARNING ")
val Info = Value(" INFO ")
val Debug = Value(" DEBUG ")
}
trait Identity {
val id: String
}
trait Logging extends Identity {
import LogLevel._
abstract class LogWriter {
protected val writer: Actor
protected val tstampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ")
def tstamp = tstampFormat.format(new Date)
def log(id: String, logLevel: LogLevel.Value, msg: String) {
writer ! (tstamp + id + logLevel + msg)
}
}
object NullLogWriter extends LogWriter {
protected val writer = actor{loop {react{case msg: String =>}}}
}
object ConsoleWriter extends LogWriter {
protected val writer = actor{loop {react {case msg: String => Console.out.println(msg); Console.flush case _ =>}}}
}
class FileWriter(val file: File) extends LogWriter {
require(file != null)
require(file.canWrite)
protected val writer = actor{loop {react {case msg: String => destFile.println(msg); destFile.flush case _ =>}}}
private val destFile = {
try {new PrintStream(new FileOutputStream(file))}
catch {case e => ConsoleWriter.log("FileWriter", LogLevel.Error, "Unable to create FileWriter for file " + file +
" exception was: " + e); Console.out}
}
}
protected var logWriter: LogWriter = ConsoleWriter
protected var logLevel = Info
def setLoggingLevel(level: LogLevel.Value) {logLevel = level}
def setLogWriter(lw: LogWriter) {if (lw != null) logWriter = lw}
def logError(msg: => String) {if (logLevel <= Error) logWriter.log(id, Error, msg)}
def logWarning(msg: => String) {if (logLevel <= Warning) logWriter.log(id, Warning, msg)}
def logInfo(msg: => String) {if (logLevel <= Info) logWriter.log(id, Info, msg)}
def logDebug(msg: => String) {if (logLevel <= Debug) logWriter.log(id, Debug, msg)}
}
Espero que sea de alguna utilidad.
La API de registro de tipo de seguridad ( https://github.com/typesafehub/scalalogging ) tiene un rasgo para agregar un logger val a una clase, pero su uso es opcional. Inicializa la variable usando getClass getName, que la mitad del tiempo no tendrá valor porque con frecuencia su nombre de clase real será gobbledygook.
Por lo tanto, si no desea que el rasgo agregue la variable extra a cada instancia, ciertamente no necesita usarlo y simplemente puede poner el logger val en su objeto complementario y hacer una importación en la clase de los miembros del objeto acompañante para que pueda no es necesario que lo califique.
Me limitaría al enfoque "con registro". El diseño limpio gana todo el tiempo: si obtienes el texto estándar, es probable que puedas obtener ganancias mucho más útiles en otras áreas.
Tenga en cuenta que el marco de trabajo registrará registradores de caché, por lo que todavía tiene uno por clase, incluso si cada instancia de esa clase tiene una referencia (de bajo costo).
Sin pruebas de que las referencias del registrador estén dañando su montón, esto huele mucho a una optimización prematura ... Solo relájese y no se preocupe, a menos que un generador de perfiles le diga lo contrario.
En una nota no relacionada, es posible que también desee examinar el uso de slf4j y logback en lugar de log4j. slf4j tiene un diseño más limpio que se adapta mejor a la scala idiomática.
Si realmente está preocupado por la sobrecarga de espacio y / o el tiempo extra en los inicializadores de objetos, una buena estrategia puede ser tener un rasgo de registro que deje el resumen del registrador como en
trait Logging {
def logger: Logger
def debug(message: String) { logger.debug(message) }
def warn(message: String) { logger.warn(message) }
}
Para las clases que necesitan ser lo más livianas posible, entonces puedes hacer
object MustBeLightweight {
val logger = Logger.getLog(classOf[MustBeLightweight])
}
class MustBeLightWeight extends Logging {
final def logger = MustBeLightweight.logger
}
El JIT podría incluso warn
debug
línea y el logger
en este caso.
También puede tener un rasgo para mezclar en las clases donde la sobrecarga de un campo adicional no es un problema
trait PerInstanceLog {
val logger = Logger.getLog(this.getClass())
}
Otra opción es dejar el registro fuera de la clase y ponerlo completamente en un objeto como en
object Foo {
object log extends Logging {
override val logger = Logger.getLogger(classOf[Foo])
}
}
class Foo {
import Foo.log._
def someMethod() = { warn("xyz") }
}
Aunque estoy de acuerdo con Kevin, no agregues la complejidad a menos que la necesites.
Una forma es extender el registrador al objeto complementario:
object A extends LoggerSupport
class A {
import A._
log("hi")
}
trait LoggerSupport{
val logger = LoggerFactory.getLogger(this.getClass)
def log(msg : String)= logger.log(msg)
}
//classes of the logging framework
trait Logger{
def log(msg : String) : Unit
}
object LoggerFactory{
def getLogger(classOfLogger : Class[_]) : Logger = ...
}
Alternativamente, puede almacenar en caché las instancias del registrador:
import collection.mutable.Map
object LoggerCache{
var cache : Map[Class[_], Logger] = Map()
def logger(c : Class[_]) = cache.getOrElseUpdate(c, LoggerFactory.getLogger(c))
}
trait LoggerSupport{
def log(msg : String) = LoggerCache.logger(this.getClass).log(msg)
}
class A extends LoggerSupport{
log("hi")
}
Esto es más fácil de usar pero tendrá un peor rendimiento. El rendimiento será realmente malo cuando vaya a descartar la mayoría de los mensajes de registro (debido a la configuración de nivel de registro).
Utilicé log4j con Scala creando un rasgo y teniendo el registrador por instancias no por clase. Con algunos conjuros y magia de Scala, es posible que puedas cambiar el registrador para que sea estático (objeto interno), pero no estoy 100% seguro. Personalmente, estoy de acuerdo con @KevinWright que hacer que el registrador sea estático es una optimización prematura.
También tenga en cuenta que el código siguiente tiene los mensajes de registro como nombre-por-nombre, lo que significa que sus llamadas al registrador no necesitan ser incluidas en `if (log.isDebugEnabled ()); los mensajes de registro complejos creados mediante la concatenación de cadenas no se evaluarán a menos que el nivel de registro sea el adecuado. Consulte este enlace para obtener más información: http://www.naildrivin5.com/scalatour/wiki_pages/TypeDependentClosures
http://github.com/davetron5000/shorty/blob/master/src/main/scala/shorty/Logs.scala
package shorty
import org.apache.log4j._
trait Logs {
private[this] val logger = Logger.getLogger(getClass().getName());
import org.apache.log4j.Level._
def debug(message: => String) = if (logger.isEnabledFor(DEBUG)) logger.debug(message)
def debug(message: => String, ex:Throwable) = if (logger.isEnabledFor(DEBUG)) logger.debug(message,ex)
def debugValue[T](valueName: String, value: => T):T = {
val result:T = value
debug(valueName + " == " + result.toString)
result
}
def info(message: => String) = if (logger.isEnabledFor(INFO)) logger.info(message)
def info(message: => String, ex:Throwable) = if (logger.isEnabledFor(INFO)) logger.info(message,ex)
def warn(message: => String) = if (logger.isEnabledFor(WARN)) logger.warn(message)
def warn(message: => String, ex:Throwable) = if (logger.isEnabledFor(WARN)) logger.warn(message,ex)
def error(ex:Throwable) = if (logger.isEnabledFor(ERROR)) logger.error(ex.toString,ex)
def error(message: => String) = if (logger.isEnabledFor(ERROR)) logger.error(message)
def error(message: => String, ex:Throwable) = if (logger.isEnabledFor(ERROR)) logger.error(message,ex)
def fatal(ex:Throwable) = if (logger.isEnabledFor(FATAL)) logger.fatal(ex.toString,ex)
def fatal(message: => String) = if (logger.isEnabledFor(FATAL)) logger.fatal(message)
def fatal(message: => String, ex:Throwable) = if (logger.isEnabledFor(FATAL)) logger.fatal(message,ex)
}
class Foo extends SomeBaseClass with Logs {
def doit(s:Option[String]) = {
debug("Got param " + s)
s match {
case Some(string) => info(string)
case None => error("Expected something!")
}
}
}
object Log {
def log(message: String) = {
.....
}
}
¿No?