You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Log4j2统一日志工具类如何记录调用方而非工具类本身的日志

实现方案

你的需求完全可以实现,Log4j2原生支持日志门面类的栈帧跳过,不需要自己手动解析调用栈。你之前看到的log方法确实存在,只是你记错了参数列表,多了一个Marker占位参数,传null即可。

完整LogUtils实现代码

package my.utils;

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

public final class LogUtils {
    // 核心:当前工具类的全限定类名,Log4j2会自动跳过该类的栈帧,查找真正的调用方
    private static final String FQCN = LogUtils.class.getCanonicalName();

    private LogUtils() {
        throw new AssertionError("工具类不可实例化");
    }

    // --------------- info 级别方法 ---------------
    public static void info(Object msg) {
        log(Level.INFO, null, msg, null);
    }

    public static void info(Object msg, Throwable t) {
        log(Level.INFO, null, msg, t);
    }

    // --------------- debug 级别方法 ---------------
    public static void debug(Object msg) {
        log(Level.DEBUG, null, msg, null);
    }

    public static void debug(Object msg, Throwable t) {
        log(Level.DEBUG, null, msg, t);
    }

    // --------------- warn 级别方法 ---------------
    public static void warn(Object msg) {
        log(Level.WARN, null, msg, null);
    }

    public static void warn(Object msg, Throwable t) {
        log(Level.WARN, null, msg, t);
    }

    // --------------- error 级别方法 ---------------
    public static void error(Object msg) {
        log(Level.ERROR, null, msg, null);
    }

    public static void error(Object msg, Throwable t) {
        log(Level.ERROR, null, msg, t);
    }

    /**
     * 核心日志打印方法
     * @param level 日志级别
     * @param marker 日志标记,无特殊需求传null即可
     * @param msg 日志内容
     * @param t 异常对象,无异常传null即可
     */
    private static void log(Level level, Marker marker, Object msg, Throwable t) {
        // 直接获取调用方的Logger,Log4j2内部已经做了实例缓存,不会重复创建
        StackTraceElement caller = Thread.currentThread().getStackTrace()[3];
        Logger logger = LogManager.getLogger(caller.getClassName());
        logger.log(FQCN, level, marker, msg, t);
    }
}

原理说明

  1. FQCN参数的作用:Log4j2打印栈信息时,会从调用栈中从上往下找,第一个类名不等于FQCN的栈帧,就是真正的日志调用方,因此打印出来的类名、方法名、行号都是业务代码的位置,不会显示LogUtils本身的信息。
  2. Logger缓存问题:你不需要自己维护LOGGERS的Map,Log4j2内部默认会缓存所有创建过的Logger实例,重复调用LogManager.getLogger(同一个类名)会直接返回缓存的对象,不会额外创建实例。
  3. 参数匹配问题:你之前找不到对应方法是因为漏掉了Marker类型的参数,该参数是用来给日志做分类标记的,没有特殊需求直接传null即可匹配到对应的方法签名。

效果验证

只要你的Log4j2配置的输出格式包含类名、方法名、行号占位符(比如%c{1.} %M %L - %msg%n),输出的日志就会完全和你直接在业务类定义Logger打印的效果一致,没有任何差异。

内容的提问来源于stack exchange,提问作者Nakarukatoshi Uzumaki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 04:24:00