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); } }
原理说明
- FQCN参数的作用:Log4j2打印栈信息时,会从调用栈中从上往下找,第一个类名不等于FQCN的栈帧,就是真正的日志调用方,因此打印出来的类名、方法名、行号都是业务代码的位置,不会显示LogUtils本身的信息。
- Logger缓存问题:你不需要自己维护
LOGGERS的Map,Log4j2内部默认会缓存所有创建过的Logger实例,重复调用LogManager.getLogger(同一个类名)会直接返回缓存的对象,不会额外创建实例。 - 参数匹配问题:你之前找不到对应方法是因为漏掉了
Marker类型的参数,该参数是用来给日志做分类标记的,没有特殊需求直接传null即可匹配到对应的方法签名。
效果验证
只要你的Log4j2配置的输出格式包含类名、方法名、行号占位符(比如%c{1.} %M %L - %msg%n),输出的日志就会完全和你直接在业务类定义Logger打印的效果一致,没有任何差异。
内容的提问来源于stack exchange,提问作者Nakarukatoshi Uzumaki
相关产品推荐
相关产品推荐

