【问题标题】:Why does Log4j need the class name?为什么 Log4j 需要类名?
【发布时间】:2014-01-29 21:39:41
【问题描述】:

为了在类中初始化 Log4j,程序员需要提供Class 实例(或代表它的字符串名称)。代码将如下所示:

public class MyClass {
    private static Log logger = Logger.getLogger(MyClass.class);
    [...]
}

Log4j 然后将根据程序员给出的参数提供输出。输出将如下所示:

[INFO] [MyClass:124] Foobar

我的问题:如果 Log4j 知道日志语句的行号是什么(124),那么它为什么需要类名 (我的班级)?在我看来,如果它能够弄清楚前者,它应该能够弄清楚后者。

编辑:这也不是闲置问题;我们大多数人都见过这样的代码,程序员不小心复制并粘贴了另一个类的初始化代码,但忘记在初始化程序中更改类名。这会导致 Log4j 提供令人困惑的输出。

(当然,我的问题并没有说明具体问题,但有助于我了解 Log4j 和可能的 Java 反射如何工作)。

【问题讨论】:

    标签: java log4j


    【解决方案1】:

    要解决“复制和粘贴”问题,请尝试在代码中使用 getClass() 方法,这样当您将记录器声明复制并粘贴到新类时,它会使用自己的类名,如下所示:

    private Logger log = Logger.getLogger(this.getClass());
    

    Log4j 实际上会在类名上调用 .getName() ,因此它有一个用于记录器的字符串名称。事实上,你可以在里面放任何字符串,但惯例是使用类名。

    还有一个用例,您希望在 B 类中使用 A 类的记录器。也许 B 是内部类的子类或一部分,并且您希望日志输出看起来像来自相同的日志名称(班级名称)。例如:

    public class MyClassB extends MyClassA {
        private static Log logger = Logger.getLogger(MyClassB.class);
        [...]
    }
    

    【讨论】:

    • 第一个解决方案在使用静态记录器时不起作用,这是静态类中需要的。您提出的第二点:这不会导致此类记录器中的行号出现问题吗?为什么不使用调用类初始化的默认方法?
    • 1.正确,这不适用于静态类。 2. 这取决于你如何格式化你的日志输出。行号来自源文件名。但包括行号“非常慢”(logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/…)。 3. 是的,你可以这样做。
    【解决方案2】:

    为了在类中初始化 Log4j,程序员需要提供 Class 实例(或代表它的字符串名称)。

    不,使用类名不是必需 - 这只是按照惯例,因此您可以按类和包名配置日志记录。你也可以使用

    private static Log logger = Logger.getLogger("SomeUniqueName");
    

    在记录时检索到的类名和行号很可能是通过当前线程的 StackTrace 检索到的,这是一个性能瓶颈 - 请参阅 http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html

    例如,对于L 模式,它表示

    警告生成调用方位置信息非常缓慢,除非执行速度不是问题,否则应避免。

    暂且不说检索堆栈跟踪的性能,从技术上讲,以下方法也可以在没有显式引用类的情况下检索静态和实例记录器(可能需要一些更复杂的错误处理,尤其是使用数组访问):

    public class ClassLogger {
       public static Logger getLogger() {
          String className = Thread.currentThread().getStackTrace()[2].getClassName();
          return Logger.getLogger(className);
       }
    }
    

    然后可以像这样使用

    public class SomeClass {
       private static Logger sl = ClassLogger.getLogger();
       private Logger il = ClassLogger.getLogger();
    
       ...
    

    这也适用于非调试模式,因为在这种情况下类名也可用(而文件名和行号不可用)。

    【讨论】:

      【解决方案3】:

      一般来说,记录器实例只需要一个名称。

      通常,开发人员为每个类定义一个记录器实例,并使用类名作为记录器名称。为了适应这种常见用途,log4j 添加了一个方便的工厂方法,该方法接受一个 Class 实例来代替 String。

      java.util.logging 不提供这种便捷的方法(这可能是它包含在 JDK 中但使用率低的部分原因)。

      为避免复制粘贴错误,您可以尝试使用Lombok,它会为您提供一个注释,该注释将执行巫术并在编译时注入正确编写的记录器静态实例(并使您的静态代码分析检查器发疯)。

      如果您可以摆脱 Java 语言,Groovy 提供了类似的AST transformation annotations

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-19
        • 2010-10-14
        • 1970-01-01
        • 1970-01-01
        • 2011-01-09
        • 1970-01-01
        相关资源
        最近更新 更多