Typesafe 的 scala-logging 究竟比其他日志框架更高效?

How exactly is Typesafe's scala-logging more performant than other logging frameworks?

Github 页面上显示:

It's performant, because thanks to Scala macros the check-enabled-idiom is applied and the following code is generated:

if (logger.isDebugEnabled) logger.debug(s"Some $expensive message!")

比起 Play 的日志记录,它的性能如何?

在 Play 中,它用自己的调用包装底层记录器,只检查是否在常规代码中启用调试,不涉及宏:

def debug(message: => String)(implicit mc: MarkerContext): Unit = {
  if (isDebugEnabled) {
    mc.marker match {
      case None => logger.debug(message)
      case Some(marker) => logger.debug(marker, message)
    }
  }
}

(源代码为here

检查是否通过宏启用调试如何提高性能?

让我们考虑一个更简单的方法:

def debug(message: => String): Unit = {
  if (logger.isDebugEnabled) {
    logger.debug(message)
  }
}

这里,此方法接受一个别名参数,并仅在启用调试时调用它。在内部,所有按名称的参数都映射到无效函数,因此此方法等效于以下方法:

def debug(message: () => String): Unit = {
  if (logger.isDebugEnabled) {
    logger.debug(message())
  }
}

这意味着 每次您在代码中调用此方法 时都会创建一个 新函数实例。也许编译器有时可以优化它,但绝对不是总是。一般来说,通过名称调用调用方法需要在内部创建函数类的实例,如果捕获任何变量,几乎总是:

logger.debug(s"Something interesting happened: $something. Message: $message")

那么这个函数对象,作为一个闭包,也会包含对这些捕获变量的引用。

与此相比,基于宏的方法将直接重写为条件检查和底层记录器调用:

logger.debug(s"Something interesting happened: $something. Message: $message")
// gets rewritten to
if (logger.logger.isDebugEnabled) {
  logger.logger.debug(s"Something interesting happened: $something. Message: $message")
}

这样,不会创建额外的对象;代码会像您明确写出来一样工作,只是您不必实际编写此样板文件。