10

我有一个子类和一个超类。在超类中,我有一个记录一些东西的方法。当我创建子类的实例时,记录器会为超类创建一条日志消息。为什么会这样?

代码示例:

超类.java

import java.util.logging.Level;
import java.util.logging.Logger;

public abstract class SuperClass {
    public void logAndPrintClass(){
        String name = this.getClass().getName();
        System.out.println(name);
        Logger logger = Logger.getLogger(name);
        logger.log(Level.INFO, "Logmessage");
    }
}

子类.java

public class SubClass extends SuperClass {
}

TestLogBubClass.java

public class TestLogBubClass {

    public static void main(String[] args){
        SuperClass obj = new SubClass();
        obj.logAndPrintClass();
    }
}

输出:

SubClass
Mar 15, 2013 6:30:04 PM SuperClass logAndPrintClass
INFO: Logmessage

如您所见,类的名称已正确打印,但在日志消息中显示不正确。

4

4 回答 4

5

这个原因在LogRecord的 JavaDoc 中:

请注意,如果客户端应用程序没有明确指定源方法名称和源类名称,则 LogRecord 类将在首次访问它们时(由于对 getSourceMethodName 或 getSourceClassName 的调用)通过分析调用堆栈自动推断它们。

在这种情况下,调用堆栈以 定义的方法结束SuperClass,因此LogRecord假定它是被调用的类。如果您想看到它的实际效果,请执行以下代码:

public class Example {

    public static class Superclass {
        public void foo() {
            new Exception().printStackTrace();
        }
    }

    public static class Subclass extends Superclass {
        // nothing here
    }

    public static void main(String[] argv)
    throws Exception
    {
        Superclass s = new Subclass();
        s.foo();
    }
}

编辑:我不使用 jul,但希望有一种简单的方法来配置输出(就像所有其他记录器实现提供的一样)。看来您必须实现自己的Formatter类,并使用该java.util.logging.ConsoleHandler.formatter属性指定它。

如果可能,我建议切换到 SLF4J。您可以通过 SLF4J 将所有现有的 jul 代码桥接到一个实际的记录器,让您可以更好地控制其输出(如 Log4J 或 Logback)。

于 2013-03-15T18:31:09.560 回答
1

这段java.util.logging代码很奇怪.. Logging 不使用name,而是试图找出源类本身(然后被记录)。看看private void inferCaller()类中的方法java.util.logging.LogRecord

于 2013-03-15T18:37:19.030 回答
1

@Parsifal 解释了为什么会发生这种情况,我将尝试解释一种解决方法。

如果您想查看调用方法的确切类,我建议使用log4jslf4j。如果您确实需要使用java.util.Logger,请查看jul-to-slf4j

我已经尝试过,对于我来说,具有相同类的输入在我的标准输出中如下所示:

子类

2013 年 3 月 15 日晚上 8:39:44 超级类 logAndPrintClass

信息:日志消息

20:39:44,478 信息子类:12 - 日志消息

在日志文件中只有:

20:39:44,478 信息子类:12 - 日志消息

它可能看起来不太好,但在某些情况下,这可能是合适的解决方法。

于 2013-03-15T18:44:37.573 回答
0

我想我有一个解决方法,即使我无法提供完整的解释。

logAndPrintClass只需一个super调用即可覆盖,如下所示SubClass

class SubClass extends SuperClass {
    protected void logAndPrintClass(){ super.logAndPrintClass(); }
}

我仍然得到相同的输出。

SubClass
Mar 15, 2013 11:22:10 AM SuperClass logAndPrintClass
INFO: Logmessage

然后我简单地将方法体复制到子类中:

class SubClass extends SuperClass {
    protected void logAndPrintClass(){
        String name = this.getClass().getName();
        System.out.println(name);
        Logger logger = Logger.getLogger(name);
        logger.log(Level.INFO, "Logmessage");
    }
}

然后我得到了不同的输出:

SubClass
Mar 15, 2013 11:23:31 AM SubClass logAndPrintClass

我不知道为什么会这样,但我似乎发现的唯一区别是该Logger对象现在是在子类内部而不是在超类内部实例化。

于 2013-03-15T18:28:38.007 回答