日志Jar包冲突有关问题
背景
- 使用淘宝diamond配置工具出现无法配置情况。
- 在分析代码、依赖和确认diamond工作无误仍不得解后,最终由好友指点才定位了问题。
分析
异常栈如下
SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/D:/Workspace/Code/Maven.rep/org/slf4j/slf4j-log4j12/1.5.8/slf4j-log4j12-1.5.8.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/D:/Workspace/Code/Maven.rep/ch/qos/logback/logback-classic/1.0.13/logback-classic-1.0.13.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. Exception in thread "main" java.lang.NoSuchMethodError: org.slf4j.spi.LocationAwareLogger.log(Lorg/slf4j/Marker;Ljava/lang/String;ILjava/lang/String;[Ljava/lang/Object;Ljava/lang/Throwable;)V at org.apache.commons.logging.impl.SLF4JLocationAwareLog.warn(SLF4JLocationAwareLog.java:185) at com.starit.diamond.client.impl.DefaultDiamondSubscriber.getAppName(DefaultDiamondSubscriber.java:1539) at com.starit.diamond.client.impl.DefaultDiamondSubscriber.<init>(DefaultDiamondSubscriber.java:177) at com.starit.diamond.client.impl.DefaultDiamondSubscriber.<init>(DefaultDiamondSubscriber.java:172) at com.starit.diamond.client.impl.DefaultDiamondSubscriber$Builder.build(DefaultDiamondSubscriber.java:168) at com.starit.diamond.client.impl.DiamondClientFactory.getSingletonDiamondSubscriber(DiamondClientFactory.java:41) at com.starit.diamond.manager.impl.DefaultDiamondManager.<init>(DefaultDiamondManager.java:140) at com.starit.diamond.manager.impl.DefaultDiamondManager.<init>(DefaultDiamondManager.java:122) at com.ustcinfo.inm.data.bolt.tedis.DiamondConfigManager.init(DiamondConfigManager.java:73) at com.ustcinfo.inm.data.bolt.tedis.DiamondConfigManager.getInstance(DiamondConfigManager.java:60) at com.ustcinfo.inm.data.bolt.Test4Diamond.main(Test4Diamond.java:20)
繁杂的异常信息,可能让长期处于Deadline边缘的程序猿媛们异常繁杂。但不翻山越岭你怎么能看到彼岸的风景。
我们耐下性子,定位异常发生的位置:
Exception in thread "main" java.lang.NoSuchMethodError: org.slf4j.spi.LocationAwareLogger.log(Lorg/slf4j/Marker;Ljava/lang/String;ILjava/lang/String;[Ljava/lang/Object;Ljava/lang/Throwable;)V
跟踪进log()这个方法
public void warn(Object message) { logger.log(null, FQCN, LocationAwareLogger.WARN_INT, String .valueOf(message), null, null); }
这时好友怀疑是Jar包冲突。
经典场景为工程中依赖的第三方包都需要使用slf4j这个日志包,一旦这些第三方包中有个使用了较低的slf4j版本中的API,倘若slf4j没有向下兼容而直接删除了这个API,而整个工程又只能使用一个版本的slf4j,那么就会导致由于无法找到这个较低版本的API而编译无法通过。
这时我们在跟进log()这个方法定义的位置
通过箭头所指的Link with Editor
定位类路径,注意上图是依据升级版本之后的图,再未升级之前版本为1.5.8,并且发现其中定义的log()方法如下:
public abstract void log(Marker paramMarker, String paramString1, int paramInt, String paramString2, Throwable paramThrowable);
仔细对比才发现,1.5.8即我们正在使用的版本中log方法只有5个参数,而diamond中依赖的日志log是6个参数了。
于是我们很自然的升级了slf4j的版本。
这个版本log有6个参数,而问题也得到了解决。
public void log(Marker marker, String fqcn, int level, String message, Object[] argArray, Throwable t);
总结
很多时候我们都急于寻求答案,却忘记了最重要的提醒。
请仔细聆听出错日志、异常栈的声音。
转载请标明作者和原文链接
ifuteng#gmail.com 2014/7/18