最近接手一个厂商移交的项目,发现后管子系统不打印日志。
项目使用的logback
本地断点调试发现logback-classic?jar冲突导致?打出的war中没有?相关的jar
解决方法:
去除pom?文件中多余的?logback-classic?应用,只保留最新版本的。?重新打包环境后,日志可正常输出。
java是如何加载logback
我们都知道,当我们需要引入logback时,是不是需添加任何配置 来引入logback.xml文件的,只需要将logback.xml配置文件定义到resources目录即可,那么框架会自动加载这个日志配置文件,并按照配置帮我自己生成日志到指定的目录下,那么它是如何自动加载的。
很显然第一个想到就是通过spi。
在说明如何加载的一个前提是,你需要知道sl4j、log4j、logback之间的关系。
可以看这篇 SLF4J和Logback和Log4j和Logging的区别与联系
这里我还是贴一张图来说明一下:
slf4j是一个门面,而logback、log4j都是这个门面的实现。
所以logback肯定是在sl4j.jar中加载的。
3.1、回顾下我们获取日志对象是如何获取的
上面方法会加 //加载org/slf4j/impl/StaticLoggerBinder.class这个类这个类,那么我们先看下slf4j下有没有这个类:
//通过LoggerFactory获取一个logger对象
final static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
//通过LoggerFactory获取一个logger对象,那么我们看下这个方法如下:
它果然是在slf4j这个门面中定义的。
public static Logger getLogger(Class<?> clazz) {
? ?//看下是如何获取logger 的
? ?Logger logger = getLogger(clazz.getName());
? ?if (DETECT_LOGGER_NAME_MISMATCH) {
? ? ? ?Class<?> autoComputedCallingClass = Util.getCallingClass();
? ? ? ?if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
? ? ? ? ? ?Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
? ? ? ? ? ? ? ? ? ? ? ? ? ?autoComputedCallingClass.getName()));
? ? ? ? ? ?Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
? ? ? ?}
? ?}
? ?return logger;
}
?
getLogger
public static Logger getLogger(String name) {
? ? //看下这个方法
? ? ILoggerFactory iLoggerFactory = getILoggerFactory();
? ? return iLoggerFactory.getLogger(name);
}
?
public static ILoggerFactory getILoggerFactory() {
? ? ?if (INITIALIZATION_STATE == UNINITIALIZED) {
? ? ? ? ?synchronized (LoggerFactory.class) {
? ? ? ? ? ? ?if (INITIALIZATION_STATE == UNINITIALIZED) {
? ? ? ? ? ? ? ? ?INITIALIZATION_STATE = ONGOING_INITIALIZATION;
? ? ? ? ? ? ? ? ?//看这个方法
? ? ? ? ? ? ? ? ?performInitialization();
? ? ? ? ? ? ?}
? ? ? ? ?}
? ? ?}
? ? ?......
} ? ??
?
查看performInitialization 的bind方法
? private final static void performInitialization() {
? ? ?//绑定
? ? ?bind();
? ? ?if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
? ? ? ? ?versionSanityCheck();
? ? ?}
?}
private final static void bind() {
? ? ....
? ? staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
? ? .....
}
?
?private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
static Set<URL> findPossibleStaticLoggerBinderPathSet() {
? ? ?// use Set instead of list in order to deal with bug #138
? ? ?// LinkedHashSet appropriate here because it preserves insertion order
? ? ?// during iteration
? ? ?Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
? ? ?try {
? ? ? ? ?ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
? ? ? ? ?Enumeration<URL> paths;
? ? ? ? ?if (loggerFactoryClassLoader == null) {
? ? ? ? ?//加载org/slf4j/impl/StaticLoggerBinder.class这个类
? ? ? ? ? ? ?paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
? ? ? ? ?} else {
? ? ? ? ? ? ?paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
? ? ? ? ?}
? ? ? ? ?while (paths.hasMoreElements()) {
? ? ? ? ? ? ?URL path = paths.nextElement();
? ? ? ? ? ? ?staticLoggerBinderPathSet.add(path);
? ? ? ? ?}
? ? ?} catch (IOException ioe) {
? ? ? ? ?Util.report("Error getting resources from path", ioe);
? ? ?}
? ? ?return staticLoggerBinderPathSet;
?}
这个jar下没有这个路径,那么肯定是在slf4j-logback.jar,slf4j-log4j.jar这样的jar包下实现的。
搜索logback相关jar,发现在这个jar下有这个路径类
然后继续,看bind方法后面
如果你的项目中只有logback-classic这一个Jar,没有其它日志框架,那么直接点到这个方法中就到logback方法中,如下:
这个类中静态方法就会执行
看下init方法
autoConfig()中findURLOfDefaultConfigurationFile方法
再继续看autoConfig()方法
后面就不在细说明,可以直接到源码里面看看。
这里整个logback自动注入的过程就完结了
?
具体排查过程待补充
最终原因?jar包冲突?
表现?war包中没有?logback-classic.jar?这个文件,? 解决冲突后 ,logback-classic.jar?出现了?问题解决。