登录scala

Itt*_*ayD 30 logging scala

在Java中,日志记录的标准习惯用法是为记录器对象创建一个静态变量,并在各种方法中使用它.

在Scala中,看起来成语是用记录器成员创建Logging特征并在具体类中混合特征.这意味着每次创建对象时,它都会调用日志框架来获取记录器,并且由于附加引用,对象也会变大.

是否有一种替代方案可以在仍然使用每类记录器实例时轻松使用"with Logging"?

编辑:我的问题不是关于如何在Scala中编写日志框架,而是如何使用现有的(log4j)而不会产生性能开销(获取每个实例的引用)或代码复杂性.另外,是的,我想使用log4j,因为我将使用可能使用log4j的Java编写的第三方库.

Kev*_*ght 20

我只是坚持"使用Logging"的方法.干净的设计每次都会获胜 - 如果你得到了样板,那么很有可能你可以在其他领域找到更有用的增益.

请记住,日志记录框架将缓存记录器,因此每个类仍然有一个,即使该类的每个实例碰巧都有(廉价)引用.

如果没有证据证明记录器引用会损害你的堆,这就像过早的优化一样......只是放松并且不用担心,除非探查器告诉你.

在一个不相关的说明中,您可能还想研究使用slf4j和logback而不是log4j.slf4j有一个更干净的设计,更适合惯用的scala.

  • 我有这个特性`trait J2SELogging {protected val log = Logger.getLogger(getClass.getName)}` (3认同)

dav*_*000 12

我通过创建一个特征并使用每个实例而不是每个类的记录器来使用带有Scala的log4j.使用一些Scala魔法和清单,您可以将记录器更改为静态(内部对象),但我不是100%肯定.就个人而言,我同意@KevinWright认为使记录器静态是一个不成熟的优化.

另请注意,下面的代码按名称显示日志消息,这意味着您的记录器调用不需要包含在`if(log.isDebugEnabled()); 除非日志级别合适,否则不会评估通过字符串连接创建的复杂日志消息.有关详细信息,请参阅此链接: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!")
    } 
  }
}
Run Code Online (Sandbox Code Playgroud)


Geo*_*edy 5

如果你是真正关心空间开销和/或在对象初始化额外的时间,一个好的策略可以有一个日志特质留下的记录摘要中


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 PerInstanceLog {
  val logger = Logger.getLog(this.getClass())
}
Run Code Online (Sandbox Code Playgroud)

另一个选择是将日志记录保留在类之外,并将其完全放在对象中,如同


object Foo {
  object log extends Logging {
    override val logger = Logger.getLogger(classOf[Foo])
  } 
}

class Foo {
  import Foo.log._
  def someMethod() = { warn("xyz") }
}
Run Code Online (Sandbox Code Playgroud)

我同意凯文,除非你需要,否则不要增加复杂性.