场景

一个简单的spring-boot程序,需要用kafka做消息队列,于是在maven中引入kafka依赖,一切看似没问题,在启动时,打印出Warning信息:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/xxx/learning-slf4j-multiple-bindings/WEB-INF/lib/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/xxx/learning-slf4j-multiple-bindings/WEB-INF/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.

原因分析

通过警告消息,可以简单的看出是slf4j绑定发生问题,有多个StaticLoggerBinder.class存在,即slf4j-log4j12logback-classic冲突。

  • 疑惑点1是我并没有手动引入slf4j-log4j12依赖,依赖jar包是被自动引入的,通过maven自带工具分析依赖路径,可以看出是kafka依赖于slf4j-log4j12,自动导入的依赖包。



  • 日志绑定的机制分析

    从日志对象开始探究slf4j的绑定方式。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
···
private final Logger logs = LoggerFactory.getLogger(***.class);

LoggerFactory.getLogger()方法:(下述均只保留关键逻辑代码 )

public static Logger getLogger(Class<?> clazz) {
Logger logger = getLogger(clazz.getName());
···
return logger;
}
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();//看这里
return iLoggerFactory.getLogger(name);//根据名字返回一个Logger实例对象
}

ILoggerFactory是一个接口,归属package org.slf4j;仅存在一个方法为:

public Logger getLogger(String name);

接下来就是看看getILoggerFactory()的真面目:

public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
performInitialization();//看这里
}
}
}
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
case NOP_FALLBACK_INITIALIZATION:
return NOP_FALLBACK_FACTORY;
case FAILED_INITIALIZATION:
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
case ONGOING_INITIALIZATION:
// support re-entrant behavior.
// See also http://jira.qos.ch/browse/SLF4J-97
return SUBST_FACTORY;
}
throw new IllegalStateException("Unreachable code");
}

可以看到performInitialization()是进行初始化的方法:

private final static void performInitialization() {
bind();
···
}

performInitialization()内部调用bind()方法:

private final static void bind() {
try {
Set<URL> staticLoggerBinderPathSet = null;
if (!isAndroid()) {
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();//看这里
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// the next line does the binding
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
fixSubstituteLoggers();
replayEvents();
// release all resources in SUBST_FACTORY
SUBST_FACTORY.clear();
} catch (NoClassDefFoundError ncde) {...
} catch (java.lang.NoSuchMethodError nsme) {...
} catch (Exception e) {...}
}

其中关键在于findPossibleStaticLoggerBinderPathSet()方法,终于到了查找绑定相关的部分内容,可以看到是查找所有的"org/slf4j/impl/StaticLoggerBinder.class"类并加载,同时while循环里,将可能存在的多个StaticLoggerBinder.class路径均加入Set<URL>返回。

private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";

static Set<URL> findPossibleStaticLoggerBinderPathSet() {
Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
try {
ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
Enumeration<URL> paths;
if (loggerFactoryClassLoader == null) {//看这里
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;
}

返回到bind()方法中:

private final static void bind() {
···
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);//看这里
···
}
···
private static void reportMultipleBindingAmbiguity(Set<URL> binderPathSet) {
if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
Util.report("Class path contains multiple SLF4J bindings.");
for (URL path : binderPathSet) {
Util.report("Found binding in [" + path + "]");
}
Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
}
}

这里可以看到reportMultipleBindingAmbiguity()里判断是否发生多重绑定,就是打印文章开头Warning信息的地方。

成功加载StaticLoggerBinder后,在bind()方法中调用其getSingleton()方法得到单例,并修改INITIALIZATION_STATE 状态,至此完成日志框架的绑定。

private final static void bind() {
if (!isAndroid()) {
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
StaticLoggerBinder.getSingleton();//看这里
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
···
}

最后附上slf4j-api-1.7.25.jarlogback-classic-1.2.3.jar的目录结构供参考:

解决方案

分析了原因,那么解决方案自然很简单,就是剔除不需要的依赖包,此处就是在kafka的依赖中剔除slf4j-log4j12maven项目中可以通过exclusions标签来完成。

<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.11</artifactId>
<version>0.10.0.1</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>

本文就简单分析了日志加载绑定的过程,如有遗漏请不吝指出。

log4j与logback包冲突原因及解决,不可忽视的Warning的更多相关文章

  1. Maven类包冲突终极三大解决技巧 mvn dependency:tree

    Maven对于新手来说是<步步惊心>,因为它包罗万象,博大精深,因为当你初来乍到时,你就像一个进入森林的陌生访客一样迷茫. Maven对于老手来说是<真爱配方>,因为它无所不能 ...

  2. Maven中 jar包冲突原理与解决办法

    Maven中jar包冲突是开发过程中比较常见而又令人头疼的问题,我们需要知道 jar包冲突的原理,才能更好的去解决jar包冲突的问题.本文将从jar包冲突的原理和解决两个方面阐述Maven中jar包冲 ...

  3. [转]Maven类包冲突终极三大解决技巧

    举例 A依赖于B及C,而B又依赖于X.Y,而C依赖于X.M,则A除引B及C的依赖包下,还会引入X,Y,M的依赖包(一般情况下了,Maven可通过<scope>等若干种方式控制传递依赖).这 ...

  4. jar包冲突常用的解决方法

    jar包冲突常见的异常为找不到类(java.lang.ClassNotFoundException).找不到具体方法(java.lang.NoSuchMethodError).字段错误( java.l ...

  5. was(websphere application server)中用apache的httpclient时jar包冲突问题的解决

    这个问题可以用was的共享库解决. 具体解决方案如下图所示: 对于有多个jar包冲突时,为每个冲突的jar包都新建一个共享库即可. 我之前的错误操作是以为一个共享库可以添加多个冲突的jar包用分号和逗 ...

  6. 部分APP无法代理抓包的原因及解决方法

    引言 HTTP应用层的抓包已经成为日常工作测试与调试中的重要一环,最近接触新项目突然之间发现之前的抓包手段都不好使了,顿时模块与模块之间的前端与服务之间的交互都变成了不可见,整个人都好像被蒙住了眼睛. ...

  7. tcp粘包问题原因及解决办法

    1.粘包概念及产生原因 1.1粘包概念: TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾. 粘包可能由发送方造成,也可能由接收方造成. ...

  8. [log4j]Slf4j的包冲突

    Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.apache.log4j.Log4jLoggerFa ...

  9. Android手机出现"已安装了存在签名冲突的同名数据包"的原因及解决办法

    http://blog.csdn.net/dyllove98/article/details/8830264 如果你不是开发者:如果你在android上更新一个已经安装过较早版本软件时,安装到最后一步 ...

随机推荐

  1. JS计算十万块钱 分31期 利息万分之五 每个月的还款数

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. laraval开发之QQ登录及QQ报错

    1.composer安装依赖 2.在config/app.php中注册providers并添加Socialite门面 3.在app/Providers/EventServiceProcider.php ...

  3. android OrmLite

    最近在使用ormlite框架进行数据库的操作,下面简单的写个demo来学习下 1.下载jar包 这里使用的是ormlite-core-5.0.jar 和 ormlite-android-5.0.jar ...

  4. ISO14971-2007阅读

    1.什么是风险? 风险的概念,公认的组成有两部分: 损害发生的概率 损害的后果,即损坏的严重性 2.风险管理的适用范围? 适用于医疗器械生命周期所有阶段 不适用于临床判断 不要求具体的质量体系,但14 ...

  5. centors7 elasticsearch6.3安装以及问题记录

    1.安装elasticsearch . 安装系统:centors7 1.下载安装包 官网地址:https://www.elastic.co/downloads/past-releases 2.mac文 ...

  6. mac安装软件提示没有权限

    Mac 安装软件基本是各种爽,自动更新啥. 但是有一种提示没有权限的错误,很不爽,还要sudo管理员权限 有一个修复 /usr/local目录权限的命令 sudo chown -R 'whoami' ...

  7. C#中Equals和= =(等于号)的比较(转)

    一.           值类型的比较 对于值类型来说  两者比较的都是”内容”是否相同,即 值 是否一样,很显然此时两者是划等号的. 例: int i = 9; int j = 9; Console ...

  8. The associated COM server does not support ActiveX Document embedding

    winfrom 通过dsoframer实现读取excel文件报错: System.Reflection.TargetInvocationException: Exception has been th ...

  9. 安装SCOM Gateway Server

    安装SCOM Gateway Server 1.为SCOM Gateway Server申请证书,导入CA证书链2.将安装介质Support Tools下AMD64下的 Microsoft.Enter ...

  10. 性能调优的本质、Spark资源使用原理和调优要点分析

    本课主题 大数据性能调优的本质 Spark 性能调优要点分析 Spark 资源使用原理流程 Spark 资源调优最佳实战 Spark 更高性能的算子 引言 我们谈大数据性能调优,到底在谈什么,它的本质 ...