Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to log in Scala *without* a reference to the logger in *every instance*?

I've looked at example of logging in Scala, and it usually looks like this:

import org.slf4j.LoggerFactory

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

This seems independent of the concrete logging framework. While this does the job, it also introduces an extraneous lazy val in every instance that wants to do logging, which might well be every instance of the whole application. This seems much too heavy to me, in particular if you have many "small instances" of some specific type.

Is there a way of putting the logger in the object of the concrete class instead, just by using inheritance? If I have to explicitly declare the logger in the object of the class, and explicitly refer to it from the class/trait, then I have written almost as much code as if I had done no reuse at all.

Expressed in a non-logging specific context, the problem would be:

How do I declare in a trait that the implementing class must have a singleton object of type X, and that this singleton object must be accessible through method def x: X ?

I can't simply define an abstract method, because there could only be a single implementation in the class. I want that logging in a super-class gets me the super-class singleton, and logging in the sub-class gets me the sub-class singleton. Or put more simply, I want logging in Scala to work like traditional logging in Java, using static loggers specific to the class doing the logging. My current knowledge of Scala tells me that this is simply not possible without doing it exactly the same way you do in Java, without much if any benefits from using the "better" Scala.

like image 592
Sebastien Diot Avatar asked Jun 24 '11 09:06

Sebastien Diot


People also ask

Does logger need to be static?

Loggers should be declared to be static and final. It is good programming practice to share a single logger object between all of the instances of a particular class and to use the same logger for the duration of the program.

How do you use logger?

You have to use Logger. getLogger() method. The getLogger() method identifies the name of the Logger and takes string as a parameter. So, if a Logger pre-exists then, that Logger is returned, else a new Logger is created.

What is logger in API?

A Logger object is used to log messages for a specific system or application component. Loggers are normally named, using a hierarchical dot-separated namespace. Logger names can be arbitrary strings, but they should normally be based on the package name or class name of the logged component, such as java.net or javax.


2 Answers

Premature Optimization is the root of all evil

Let's be clear first about one thing: if your trait looks something like this:

trait Logger { lazy val log = Logger.getLogger } 

Then what you have not done is as follows:

  • You have NOT created a logger instance per instance of your type
  • You have neither given yourself a memory nor a performance problem (unless you have)

What you have done is as follows:

  • You have an extra reference in each instance of your type
  • When you access the logger for the first time, you are probably doing some map lookup

Note that, even if you did create a separate logger for each instance of your type (which I frequently do, even if my program contains hundreds of thousands of these, so that I have very fine-grained control over my logging), you almost certainly still will neither have a performance nor a memory problem!


One "solution" is (of course), to make the companion object implement the logger interface:

object MyType extends Logger  class MyType {   import MyType._   log.info("Yay") } 
like image 196
oxbow_lakes Avatar answered Sep 24 '22 05:09

oxbow_lakes


How do I declare in a trait that the implementing class must have a singleton object of type X, and that this singleton object must be accessible through method def x: X ?

Declare a trait that must be implemented by your companion objects.

trait Meta[Base] {
  val logger = LoggerFactory.getLogger(getClass)
}

Create a base trait for your classes, sub-classes have to overwrite the meta method.

trait Base {
  def meta: Meta[Base]
  def logger = meta.logger
}

A class Whatever with a companion object:

object Whatever extends Meta[Base]

class Whatever extends Base {
  def meta = Whatever

  def doSomething = {
    logger.log("oops")
  }
}

In this way you only need to have a reference to the meta object.

We can use the Whatever class like this.

object Sample {
  def main(args: Array[String]) {
    val whatever = new Whatever
    whatever.doSomething
  }
}
like image 41
Stefan De Boey Avatar answered Sep 25 '22 05:09

Stefan De Boey