Scala特质中的this.getClass用法

5

有人能解释一下为什么这段代码会以这种方式工作吗?我想制作自己的概念证明Loggable trait。计划是实例化一个记录器实例,以便继承类不必做这项工作。但我看到这不是我想要的。

这是代码:

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
}

它产生了以下日志信息:
Jan 25, 2013 2:02:07 PM hu.jonas.simple.Loggable$class whoAmI
INFO: hu.jonas.simple.Service

为什么两个 this.getClass.getName 不同?此外,当我实例化记录器时,我需要编写什么才能获得这个结果:
Jan 25, 2013 2:02:07 PM hu.jonas.simple.Service whoAmI
INFO: hu.jonas.simple.Service
2个回答

4
在您的代码中,getClass.getName 的两个出现次数返回相同的值。但是,当格式化输出消息时,记录器仅使用源类(它可以通过检查堆栈来知道)而不是记录器的名称(在您的情况下对应于getClass.getName)。因此,记录器在输出日志记录的标题时实际上使用调用log的类的名称,而日志消息的内容是实例的运行时类的名称。因此,这就像是一个静态与运行时类型的问题。如果您想知道Loggable$class来自何处,那么恰好在名为T的特征中的方法实现位于名为T$class的JVM类中。
现在来看一个示例。在下面的代码中,我更改了日志处理程序,以便删除源类的信息。这会强制记录器使用记录器名称而不是源类名称,并且您将获得预期的行为。
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
}

现在,为了正确地修复您的问题,有一个名为java.util.logging.SimpleFormatter.format的属性,您可以设置它来更改默认日志格式。使用它使日志使用记录器名称而不是源类。请参见http://docs.oracle.com/javase/7/docs/api/index.html?java/util/logging/SimpleFormatter.html。请注意,它需要Java 7。

0
你在看的是调用记录器日志的方法,而不一定是日志记录器的类。在你的情况下,那是 Loggable.whoAmI。被实例化的类是 hu.jonas.simple.Service 类型,但由于方法在 Loggable 上,所以这就是显示的内容。这些信息通常是通过查看堆栈跟踪来检索的。
在下面的示例中,您可以通过自己获取堆栈跟踪来获得相同的结果:
trait Loggable { 
  val logger = java.util.logging.Logger.getLogger(this.getClass.getName)

  def whoAmI = {
    val stackTrace = Thread.currentThread.getStackTrace();
    logger.info("Class: %s, Method: %s".format(stackTrace(1).getClassName, 
      stackTrace(1).getMethodName))
    logger.info(this.getClass.getName)
  }
}

这将产生输出:

Jan 25, 2013 9:39:28 AM $line99.$read$$iw$$iw$Loggable$class whoAmI
INFO: Class: $line99.$read$$iw$$iw$Loggable$class, Method: whoAmI

Jan 25, 2013 9:39:28 AM $line99.$read$$iw$$iw$Loggable$class whoAmI
INFO: $line100.$read$$iw$$iw$Service

你可以看到这与Logger显示的一致。如果你继续向下遍历堆栈,你会发现你可以通过stackTrace(3)获取调用层次结构:

Jan 25, 2013 9:43:58 AM $line107.$read$$iw$$iw$Loggable$class whoAmI
INFO: Class: $line108.$read$$iw$$iw$Service, Method: answer

我不确定如何使用java.util.Logger来更改它,但正如其他答案中指定的那样 - 也许使用格式化程序可以获得所需的输出。否则,您可以尝试另一个日志引擎或根据上述信息自己创建。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接