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")
}
这样,不会创建额外的对象;代码会像您明确写出来一样工作,只是您不必实际编写此样板文件。
在 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")
}
这样,不会创建额外的对象;代码会像您明确写出来一样工作,只是您不必实际编写此样板文件。