一、前言
最近升级一个老项目,发面日志没有按照预期的生成。
1、resource下面有logback配置但没有生成日志
检查resource目录下,发现有logback.xml配置,但部署在服务器的项目没有按配置生成日志。于是启动本地tomcat发现日志按logback配置生成了。
检查了下logback.xml配置内容,发现并没有什么不同。看了下在控台输出的日志,发现下面这段
SLF4J: Class path contains multiple SLF4J bindings.
slf4j 包含多个slf4j的绑定,我们知道,slf4j是一个门面 ,市面上有很多日志框架比如:log4j ,common log,logback等。
目前我们用的是logback。
而这里告诉我们:SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
,实际绑定的是Log4jLoggerFactory
。
并且日志也明确说明,有2个jar提供了StaticLoggerBinde r.clas
,所以这里需要把所有引用log4j的地方去掉,
2、去掉Log4j依赖引用
其实控台打印的日志中还有一行提示:SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
这里举了个例说明如何排除log4j冲突jar,如下:
这里直接引的log4j的jar很好去掉,间接引的 需要在用Dependency Analyzer
中搜索,发现zookeep的jar中引用了Log4j,于是添加如下:
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>${zookeeper-version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
重启项目 解决。
3、java是如何加载logback
我们都知道,当我们需要引入logback时,是不是需添加任何配置 来引入logback.xml
文件的,只需要将logback.xml配置文件定义到resources目录即可,那么框架会自动加载这个日志配置文件,并按照配置帮我自己生成日志到指定的目录下,那么它是如何自动加载的。
很显然第一个想到就是通过spi。
在说明如何加载的一个前提是,你需要知道sl4j、log4j、logback之间的关系。
可以看这篇 SLF4J和Logback和Log4j和Logging的区别与联系
这里我还是贴一张图来说明一下:
slf4j是一个门面,而logback、log4j都是这个门面的实现。
所以logback肯定是在sl4j.jar中加载的。
3.1、回顾下我们获取日志对象是如何获取的
//通过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;
}
上面方法会加 //加载org/slf4j/impl/StaticLoggerBinder.class这个类
这个类,那么我们先看下slf4j下有没有这个类:
这个jar下没有这个路径,那么肯定是在slf4j-logback.jar,slf4j-log4j.jar这样的jar包下实现的。
搜索logback相关jar,发现在这个jar下有这个路径类
然后继续,看bind方法后面
如果你的项目中只有logback-classic这一个Jar,没有其它日志框架,那么直接点到这个方法中就到logback方法中,如下:
这个类中静态方法就会执行
看下init方法
autoConfig()中findURLOfDefaultConfigurationFile方法
再继续看autoConfig()方法
后面就不在细说明,可以直接到源码里面看看。
这里整个logback自动注入的过程就完结了。