Ori Popowski Ori Popowski - 6 months ago 31
Scala Question

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

On the Github page it says:


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!")



How is that more performant than, say, Play's logging?

In Play, it wraps the underlying logger with its own calls, and just checks if debug is enabled in regular code, no macro involved:

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)
}
}
}


(Source code is here)

How is checking if debug is enabled via a macro makes it more performant?

Answer Source

Let's consider a simpler method:

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

Here, this method accepts a by-name parameter and invokes it only when debugging is enabled. Internally, all by-name parameters map to nullary functions, so this method is equivalent to the following one:

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

This means that every time you invoke this method in your code a new function instance is created. Maybe the compiler can optimize it sometimes, but definitely not always. In general, invoking methods with by-name calls require creating instances of function classes internally, and if you capture any variables, which is almost always:

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

then this function object, being a closure, would also contain references to these captured variables.

Compared to this, the macro-based method will be literally rewritten into the condition check and underlying logger invocation:

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")
}

This way, no extra objects are created; the code would work as if you have written it out explicitly, except that you don't have to actually write this boilerplate.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download