如何在*每个实例*中没有*引用记录器的情况下登录Scala*?

Seb*_*iot 21 logging singleton scala

我已经看过在Scala中登录的示例,它通常看起来像这样:

import org.slf4j.LoggerFactory

trait Loggable {
  private lazy val logger = LoggerFactory.getLogger(getClass)
  protected def debug(msg: => AnyRef, t: => Throwable = null): Unit =
    {...}
}
Run Code Online (Sandbox Code Playgroud)

这似乎与具体的日志框架无关.虽然这样做了,但它还在每个想要进行日志记录的实例中引入了一个无关的lazy val ,这可能是整个应用程序的每个实例.这对我来说似乎太沉重了,特别是如果你有一些特定类型的"小实例".

有没有办法将记录器放在具体类的对象中,只需使用继承?如果我必须在类的对象中显式声明记录器,并从类/特性显式地引用它,那么我编写的代码几乎和我根本没有重用的代码一样多.

在非日志记录特定上下文中表示,问题是:

如何在特征中声明实现类必须具有X类型的单例对象,并且必须通过方法def x:X访问此单例对象?

我不能简单地定义一个抽象方法,因为在类中只能有一个实现.我希望在超级类中登录让我获得超类单例,并且登录子类会让我获得子类单例.或者更简单地说,我希望登录Scala能像Java中的传统日志一样工作,使用特定于执行日志记录的类的静态记录器.我目前对Scala的了解告诉我,如果不像在Java中那样完全相同,这是不可能的,没有太多使用"更好"的Scala带来的好处.

oxb*_*kes 13

过早优化是万恶之源

让我们首先明确一件事:如果你的特质看起来像这样:

trait Logger { lazy val log = Logger.getLogger }
Run Code Online (Sandbox Code Playgroud)

那么你还没有做的如下:

  • 没有为每个类型的实例创建一个记录器实例
  • 你没有给自己留下记忆或性能问题(除非你有)

已经做了如下:

  • 您在每种类型的实例中都有一个额外的引用
  • 当您第一次访问记录器时,您可能正在进行一些地图查找

请注意,即使您确实为每个类型的实例创建了一个单独的记录器(我经常这样做,即使我的程序包含数十万个这样的记录器,因此我对记录的控制非常精细),当然,既不会有性能也不会有内存问题!


一个"解决方案"是(当然),使伴侣对象实现记录器接口:

object MyType extends Logger

class MyType {
  import MyType._
  log.info("Yay")
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*oey 7

如何在特征中声明实现类必须具有X类型的单例对象,并且必须通过方法def x:X访问此单例对象?

声明必须由伴侣对象实现的特征.

trait Meta[Base] {
  val logger = LoggerFactory.getLogger(getClass)
}
Run Code Online (Sandbox Code Playgroud)

为您的类创建基本特征,子类必须覆盖元方法.

trait Base {
  def meta: Meta[Base]
  def logger = meta.logger
}
Run Code Online (Sandbox Code Playgroud)

与同伴对象无关的类:

object Whatever extends Meta[Base]

class Whatever extends Base {
  def meta = Whatever

  def doSomething = {
    logger.log("oops")
  }
}
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您只需要引用元对象.

我们可以使用像这样的Whatever类.

object Sample {
  def main(args: Array[String]) {
    val whatever = new Whatever
    whatever.doSomething
  }
}
Run Code Online (Sandbox Code Playgroud)