日志记录在Scala中
日志记录在Scala中
在Java中,记录日志的标准用法是创建一个静态变量来存储日志对象,并在各个方法中使用它。
在Scala中,看起来惯用的做法是创建一个带有日志成员的Logging特质,并将该特质混入具体的类中。这意味着每次创建一个对象时,都会调用日志框架来获取一个日志对象,并且由于额外的引用,对象会变得更大。
是否有一种替代方案可以在使用“with Logging”时既能方便使用,又能使用每个类的单独日志实例?
编辑:我的问题并不是关于如何在Scala中编写一个日志框架,而是如何在使用现有的日志框架(log4j)时既不产生性能开销(为每个实例获取引用),又不增加代码复杂性。另外,是的,我想使用log4j,只是因为我将使用使用log4j的Java编写的第三方库。
在Scala中,logging(日志记录)是一项常见的任务。然而,有时候在处理日志时会遇到一些问题。本文将探讨出现的问题以及解决方法。
问题的出现是因为对空间开销和对象初始化时间的担忧。为了解决这个问题,可以使用一个logging trait,在该trait中将logger定义为抽象的。具体代码如下:
trait Logging { def logger: Logger def debug(message: String) { logger.debug(message) } def warn(message: String) { logger.warn(message) } }
对于需要尽可能轻量级的类,可以采用以下方法:
object MustBeLightweight { val logger = Logger.getLog(classOf[MustBeLightweight]) } class MustBeLightWeight extends Logging { final def logger = MustBeLightweight.logger }
在这种情况下,JIT甚至可能会内联(debug, warn和logger)方法。
另一种方法是创建一个trait,将其混入到不需要额外字段开销的类中:
trait PerInstanceLog { val logger = Logger.getLog(this.getClass()) }
还可以将日志记录完全放在对象中,而不是类中:
object Foo { object log extends Logging { override val logger = Logger.getLogger(classOf[Foo]) } } class Foo { import Foo.log._ def someMethod() = { warn("xyz") } }
不过,正如Kevin所说,除非必要,否则不要增加复杂性。
以上是解决(logging in scala)问题的原因和解决方法。通过使用logging trait,将logger定义为抽象的,并根据需要选择合适的方法来减少空间开销和对象初始化时间。这些方法可以帮助我们更有效地处理日志记录任务。
问题的出现原因是使用log4j和Scala时,日志消息没有在控制台打印出来。解决方法是不需要在代码中使用“if (logger.isEnabledFor(DEBUG))”这个判断语句,因为在调用debug方法时,它会首先检查是否启用了debug级别的日志。
在Scala中使用log4j可以通过创建一个trait,并在每个实例中创建一个logger来实现。通过一些Scala的魔法和manifests,也许可以将logger更改为静态的(内部对象),但我并不100%确定。个人认为,将logger设为静态是一种过早的优化。
此外,下面的代码将日志消息作为by-name传递,这意味着你的日志调用不需要包裹在“if (log.isDebugEnabled())”中,使用字符串连接创建的复杂日志消息只有在日志级别适当时才会被评估。更多信息请参考以下链接:http://www.naildrivin5.com/scalatour/wiki_pages/TypeDependentClosures
以下是可以在GitHub上找到的相关代码: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!") } } }
在Eclipse中尝试代码时,调试消息没有打印到控制台上。请问是否还需要进行其他设置?
"if (logger.isEnabledFor(DEBUG)) logger.debug(message)" 这个双重检查是不必要的,debug方法的第一件事就是检查是否启用了debug级别的日志。
问题:在Scala中进行日志记录的最佳实践是什么?
原因:根据讨论,目前存在两种主要的日志记录方法:一种是在每个类中创建一个日志记录器实例,另一种是使用trait `Logging`。然而,目前还没有确定哪种方法是最佳的。
解决方法:根据讨论,有几种解决方法可供选择。首先,可以继续使用trait `Logging`的方法,因为它可以简化代码并提供一致的日志记录方式。其次,可以考虑使用slf4j和logback替代log4j,因为它们在Scala中更适用且设计更清晰。最后,还可以使用Java的方法来为每个类创建一个日志记录器实例,并在构造日志记录器时指定类名,以避免在类定义之间复制代码时出现问题。
在Scala中进行日志记录的最佳实践尚未确定,但可以考虑使用trait `Logging`、使用slf4j和logback替代log4j,或者为每个类创建一个日志记录器实例并在构造器中指定类名。