如何打印调用的根调用 class 名称?

how to print the root invoke class name of the invoke?

在log4j2中,有2个classes:

Common.java

public class Common {
    protected static Logger logger = LogManager.getLogger("mts_logger");

    public static void sayHi(String hi){
        logger.info(hi);
    }
}

Demo1.java:

public class Demo1 {
    @Test
    public void test1(){
        Common.sayHi("hello");
    }
}

打印的日志是:

2021-04-09 12:10:27.652  INFO   -utils.Common.sayHi(Common.java:14) mts_logger  Common.java - world

log4j2.xml 模式是:

<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level  -%l %c - %msg%n" />

我们可以看到日志只打印了Common.java,没有打印Demo1的class名字,我只是希望日志能显示调用的原始class名字方法“sayHi”,在这个例子中,class 名称是 Demo1。 如何配置 log4j2 然后它也可以显示“原始”调用class名称“Demo1”?

总之,如何在日志中打印出Demo1?

像这样实现Common.java

package com.mypackage;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.StackLocatorUtil;

class Common {
    protected static Logger logger = LogManager.getLogger("mts_logger");

    public static void sayHi(String msg){
        //using API available in log4j get the caller location
        //use Fully qualified name of the Common class.
        StackTraceElement ste = StackLocatorUtil.calcLocation("com.mypackage.Common"); 
        

        logger.info(ste.toString() + ": " + msg);
    }
}

根据您当前的模式,它将像这样记录:

2021-04-09 11:08:37.230 INFO   -Common.sayHi(Test.java:20) -sayHi mts_logger - Demo1.test1(Test.java:26): hello

您可以删除 -%l 以避免打印 -Common.sayHi(Test.java:20) -sayHi

虽然您已经接受了 Onkar 的回答,但这并不是最好的方法。根据您的操作,有两种更好的方法。

  1. 正在创建记录器“包装器”。 Log4j 实际上在几个地方自己做了这个,例如,将 SLF4J 绑定到 Log4J 的 Log4jLogger。如果您查看 class,您会看到它声明了一个名为 FQCN 的变量。这被设置为包装器的名称 class,然后传递给所有 Logger 方法。 Log4j 将使用它来查找调用该 class 的堆栈帧。这将导致您继续使用现有模式,但具有正确的 class 名称。这类似于 Onkar 的回答,但它使用了一种接受 FQCN 的日志记录方法,因此您不必自己格式化堆栈跟踪元素。

  2. 你只需要为这件事包括调用者。为此,请使用 Log4j 2.13.0 中添加的 LogBuilder。为此你应该这样做:

    package com.mypackage;
    
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.apache.logging.log4j.util.StackLocatorUtil;
    
    public class Common {
        protected static Logger logger = LogManager.getLogger("mts_logger");
    
        public static void sayHi(String msg) {          
            StackTraceElement ste = StackLocatorUtil.getStackTraceElement(3); 
            logger.atInfo().withLocation(ste).log(msg);
        }
    }
    

与第一个选项一样,这也会导致 Log4j 在将这些信息包含在您的模式中时包含正确的 class、行和方法信息。如果您使用 Java 11,则可以使用 StackWalker 而不是 Log4j 的 StackLocatorUtil。

传递给 getStackTraceElement 的数字是相关堆栈帧需要上升的级别数。值为 1 将产生 StackLocatorUtil 的框架。值为 2 将 return 调用 SayHi 中 StackLocatorUtil 的堆栈帧,值为 3 应该是 sayHi 的调用者。此方法比搜索 FQCN 快得多,但这是您不知道堆栈帧数时的唯一选择。