Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

this.getClass usage in Scala traits

Tags:

scala

traits

Can somebody explain why this code works in that way? I wanted to make my own proof of concept Loggable trait. The plan was to instantiate a logger instance in order that inherited classes do not have to do that work. But as I can see this is not what I wanted.

Here is the code:

package hu.jonas.simple

trait Loggable {
  val logger = java.util.logging.Logger.getLogger(this.getClass.getName)

  def whoAmI = {
    logger.info(this.getClass.getName)
  }
}

class Service extends Loggable {
  def answer = {
    whoAmI
  }
}

object Main extends App {
  new Service().answer
}

It produced the following log message:

Jan 25, 2013 2:02:07 PM hu.jonas.simple.Loggable$class whoAmI
INFO: hu.jonas.simple.Service

Why do the two this.getClass.getName differ? And moreover what should I need to write when I instantiate the logger in order to get this:

Jan 25, 2013 2:02:07 PM hu.jonas.simple.Service whoAmI
INFO: hu.jonas.simple.Service
like image 941
Richard Jonas Avatar asked Oct 05 '22 05:10

Richard Jonas


1 Answers

In your code both occurences of getClass.getName return the same value. But the logger, when formatting the output message, is simply using the source class (that it can know by inspecting the stack) instead of the logger's name (which in your case corresponds to getClass.getName). So the logger when outputing the header for the log record is actually using the name of the class where the call to log is done, while the content of the log message is the name runtime class of the instance. So this is like a static vs runtime type issue. If you're wondering where Loggable$class comes from, it just so happens that the method implementations in a trait named T are located in a JVM class named T$class.

And now for an illustration. In the below code I change the log handler so as to erase the information from the source class. This forces the logger to use the logger name instead of the source class name, and you get back the behaviour that you expected.

import java.util.logging._
trait Loggable {
  val logger = Logger.getLogger(this.getClass.getName)

  logger.getParent.getHandlers.foreach{ handler => 
    val formatter = handler.getFormatter
    handler.setFormatter( new java.util.logging.Formatter {
      def format( record: LogRecord ) = {
        record.setSourceClassName( null )
        formatter.format( record )
      }
    })
  }


  def whoAmI = {
    logger.info(this.getClass.getName)
  }
}

class Service extends Loggable {
  def answer = {
    whoAmI
  }
}

object Main extends App {
  new Service().answer
}

Now, to properly fix your issue, there is a property named java.util.logging.SimpleFormatter.format that you can set to change the default log formatting. Use it to have the log use the logger name instead of the source class. See http://docs.oracle.com/javase/7/docs/api/index.html?java/util/logging/SimpleFormatter.html. Note that it requires Java 7.

like image 136
Régis Jean-Gilles Avatar answered Oct 13 '22 07:10

Régis Jean-Gilles