【问题标题】:How to log the caller of a method instead of the method which is calling the Logger如何记录方法的调用者而不是调用 Logger 的方法
【发布时间】:2021-09-10 08:45:38
【问题描述】:

给定一个日志工具类,如何通过该类记录所有内容,而不是为每个类创建一个Logger 对象?

例如,而不是:

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

public class Main {

    private static final Logger LOG = LogManager.getLogger(Main.class);

    public static void main(String[] args) {
        LOG.info("Application started!");
    }
}

我想做这样的事情:

import my.utils.LogUtils;

public class Main {

    public static void main(String[] args) {
        LogUtils.info("Application started!");
    }
}

我的LogUtils 类如下所示:

package my.utils;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
import java.util.Map;

public final class LogUtils {

    private LogUtils() {
        throw new AssertionError("private constructor: " + LogUtils.class.getName());
    }

    private static final Map<Class<?>, Logger> LOGGERS = new HashMap<>();

    static {
        Class<?> current = LogUtils.class;
        LOGGERS.put(current, LogManager.getLogger(current));
    }

    public static void info(Object msg) {
        Logger logger = getFor(getCallerClass());
        // logger.info()... Here's where I am stuck! What I want to log in the stack trace is the *caller* of the "info" method, not the "info" method.
    }

    private static Logger getFor(Class<?> clazz) { return LOGGERS.computeIfAbsent(clazz, key -> LogManager.getLogger(key)); }

    private static Class<?> getCallerClass() {
        try {
            return Class.forName(getCaller(3).getClassName());
        } catch (ClassNotFoundException e) {
            return LogUtils.class;
        }
    }

    // This method should return "main" method name, but it's not being used because I don't know what should I do now
    private static String getCallerMethod() { return getCaller(3).getMethodName(); }

    private static StackTraceElement getCaller(int level) { return Thread.currentThread().getStackTrace()[level]; }
}

我已阅读several log4j2 documentation pages,但我没有发现任何关于我的问题,我还检查了several stack overflow questions,但似乎whatever I try to search 的结果是completely different question

这甚至可能吗?因为我开始怀疑了。表示我试图避免在每个班级使用记录器......否则我不会问这个问题。至少可以创建一个记录自定义堆栈跟踪级别的自定义记录器?

附带说明一下,我的 Maven 依赖项是 the ones given on the log4j2 page

<dependencies>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.14.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.14.1</version>
    </dependency>
</dependencies>

我还必须提到,在一个答案中,有这个电话:

LOG.log(LoggingHelper.class.getCanonicalName(), Level.INFO, message, null);

我在org.apache.logging.log4j.Logger 中找不到这样的方法(类似Javadoc):

Logger#log(String, Level, Object, Throwable);

只是不存在。

【问题讨论】:

    标签: java logging log4j2


    【解决方案1】:

    虽然直接使用 Log4j2 Loggers 是可能的(使用 %M 参数),但这是您试图避免的,IMO,封装日志框架是错误的方式。

    • 如果您希望将日志记录实现与代码分离,请使用slf4j
    • 如果您关心性能,请直接使用log4j2(不过,使用%M 记录调用方方法非常昂贵,如here 所述
    • 如果您关心由于重新创建记录实例而导致的记录 GC 开销(新的 ZGC/Shenandoah GC 不应该这样做),那么 log4j2 会在幕后处理(如 here 所述) ,所以不用担心
    • 还可以考虑将Lombok 用于 Log4j2 记录器实例化。请注意,Lombok/Scala/Kotlin Log4j2 扩展非常支持直接记录器实例化,这可能很好地表明这是正确的方法。

    【讨论】:

    • 这没有回答我提出的问题...为什么“封装”是错误的方法?而且..为什么你认为我封装了我的日志框架?对我来说,它似乎更像是一个集中的代码,而不是一个封装的代码。顺便说一句,a lot of people 似乎对此很感兴趣。我就是想不通为什么是走错路
    • 您直接回答问题是对的,我改写了我的回答。关于封装——好吧,你什么也得不到(记录器初始化是在一个地方或另一个地方发生的单行),同时放弃了一些能力(比如将调用者方法写入日志的能力)。因此,您可以在我的书中看到为什么这可能被认为是“错误的方式”。在您的实用程序类中,您似乎正在尝试缓存记录器实例,这也是由 log4j2 框架在幕后为您完成的,因此充其量是不必要的。
    • 缓存实例的原因只是为了避免重新创建它们,但是,忽略这一点,我只想知道如何防止 log4j2 在堆栈跟踪中显示帮助程序类。但是,正如我所看到的,没有人以任何方式回答,所以我只有“正常”选项,即为每个类创建记录器,至少 afaik。感谢您的意见
    • 正如我所解释的,不需要缓存实例,因为 log4j2 会为您完成。我指出这一点的原因是,如果您放弃此功能,则使用包装器没有任何好处(因为您没有提到您关心将其余代码与 log4j2 分离)。您还可以查看 scala/kotlin/Lombok log4j2 扩展,它们也非常有利于直接实例化记录器。
    • 谢谢推荐,我去看看!
    猜你喜欢
    • 1970-01-01
    • 2022-01-11
    • 1970-01-01
    • 2021-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    相关资源
    最近更新 更多