0%

spring | 记一次日志排查

这是我在重审项目的日志体系时,发现的问题,感觉非常经典,有必要展开详细说一下。

历史说明

  • 项目最开始用的是 log4j,其配置文件是 log4j.xml
    • 用的方法是使用了一个日志工厂,返回 log4j 对象(下面详细说)
  • log4j 出问题了,项目转向 logback 日志体系,其配置文件是 logback-spring.xml (将原来 log4j 的配置文件删除了)

工厂类获取(最开始使用的方法)

模块中获取 log

1
2
3
4
5
public class HandleChangeService<T> {
private static final Log log = LogFactory.getLog(HandleChangeService.class);

....
}

LogFactory 的核心方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.SimpleMessage;
import org.apache.logging.log4j.spi.ExtendedLogger;
import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;

public class LogFactory {
public static Log getLog(Class<?> clazz) {
return new Log4jWrapper(clazz);
}
}

class Log4jWrapper implements Log {

private static final String FQCN = Log4jWrapper.class.getName();
private ExtendedLoggerWrapper log;

public Log4jWrapper(Class<?> clazz) {
Logger logger = LogManager.getLogger(clazz);
log = new ExtendedLoggerWrapper((ExtendedLogger) logger,
logger.getName(), logger.getMessageFactory());
}

...
}

从这个代码上看,LogFactory 返回的是 log4j 的对象,但是,log4jlogback 的配置文件并不能互通。所以,理论上 log4j 不能按照 logback-spring.xml 的配置输出。

但是,实际情况下,LogFactory 返回的日志对象,就是按照 logback-spring.xml 输出的。

问题解决

经过大规模排查,最终找到问题所在。

首先,在配置文件中,指出

1
2
logging:
config: classpath:logback-spring.xml

当运行 Log4jWrapper 中获取 LogManager.getLogger(clazz) 时,最后发现返回的对象是 ch.qos.logback.classic.LoggerContext[logback]

询问,chatgpt 后,其给出了这样的解释

问题:使用 log4jLogManager.getLogger,最后返回的却是 logback

chatgpt:如果你使用 Log4jLogManager.getLogger 方法,但最终返回的是 Logback 日志记录器,通常是因为项目的类路径中同时存在 Log4jLogback 的依赖,或者因为存在冲突的日志框架依赖。

在这种情况下,Java 类加载器可能会选择其中一个日志框架来处理日志记录,导致混淆。

所以,Log4jWrapper 因为 logback-spring.xml 的配置,导致日志进行了混淆,最后 java 类加载器返回的是 logback 对象。

请我喝杯咖啡吧~