今天启动tomcat服务失败,碰到异常情况如下
java.lang.NoSuchMethodError: org.slf4j.spi.LocationAwareLogger.log(Lorg/slf4j/Marker;Ljava/lang/String;ILjava/lang/String;Ljava/lang/Throwable;)V
??? at org.apache.commons.logging.impl.SLF4JLocationAwareLog.info(SLF4JLocationAwareLog.java:159)
?
?
Commons-log + log4j 这黄金搭档一直以来都让我们很省心,很好的完成了日志的需求。但是随着技术的变更和对性能的追求,slf4j 和 logback 这对后起之秀的到来好像打破了原本很平静的日志系统,频繁的出现包冲突...?
?? ? ? 和平的日子不在了,让我们一起来看看究竟发生了什么...
?
?
?
?
?
首先看看这些个包,特别是slf4j引入后就引入了一大堆包之后就有点懵了。
?
?
?
?
很清晰的可以看到jcl-over-slf4j 重写了 commons-logging...
?
?
?
?
?
其实就这么简单,往往看了代码之后才发现错误是这么显而易见。。。
?
?
?
?
?
?
1.测试类
?
?
class="java" name="code">package com.taobao.wuzhong.log; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * DESC: * * Copyright: Copyright 2011 m.taobao.com * * @author wuzhong@taobao.com * @time 2011-4-6 下午03:42:11 * @version 1.0 **/ public class LogTest { // Logback tries to find a file called logback.groovy in the classpath. // If no such file is found, logback tries to find a file called // logback-test.xml in the classpath. // If no such file is found, it checks for the file logback.xml in the // classpath.. // If neither file is found, logback configures itself automatically using // the BasicConfigurator which will cause logging output to be directed to // the console. @Test public void test() { //commons-logging的方式获取 Log log = LogFactory.getLog(LogTest.class); //slf4j直接的方式获取,推荐用这个 Logger log2 = LoggerFactory.getLogger(LogTest.class); log.debug("eeeeee {} {} {}"); log2.debug("{} {} {}", new String[] { "a", "b", "c" }); } }
?
?
?
?
logFactory.getLog 会调用内部静态变量 Slf4jLogFactory.getInstance方法,如下:
?
?
?
?public Log getInstance(String name) throws LogConfigurationException {
?
Log instance = null; // protect against concurrent access of loggerMap synchronized (this) { instance = (Log) loggerMap.get(name); if (instance == null) { Logger logger = LoggerFactory.getLogger(name); //slf4j的方式,代理过去了 if(logger instanceof LocationAwareLogger) { instance = new SLF4JLocationAwareLog((LocationAwareLogger) logger); //包装了一层,做适配 } else { instance = new SLF4JLog(logger); } loggerMap.put(name, instance); } } return (instance);?
?
?
?
loggerFactory 会调用getILoggerFactory().getlOgger()
?
LoggerFactory.java public static ILoggerFactory getILoggerFactory() { if (INITIALIZATION_STATE == UNINITIALIZED) { INITIALIZATION_STATE = ONGOING_INITILIZATION; performInitialization(); } switch (INITIALIZATION_STATE) { case SUCCESSFUL_INITILIZATION: return getSingleton().getLoggerFactory(); case FAILED_INITILIZATION: throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG); case ONGOING_INITILIZATION: // support re-entrant behavior. // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106 return TEMP_FACTORY; } throw new IllegalStateException("Unreachable code"); } private final static void performInitialization() { bind(); versionSanityCheck(); singleImplementationSanityCheck(); }
?
?
static { SINGLETON.init(); } /** * Package access for testing purposes. */ void init() { try { try { new ContextInitializer(defaultLoggerContext).autoConfig(); } catch (JoranException je) { Util.reportFailure("Failed to auto configure default logger context", je); } StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext); contextSelectorBinder.init(defaultLoggerContext, KEY); initialized = true; } catch (Throwable t) { // we should never get here Util.reportFailure("Failed to instantiate [" + LoggerContext.class.getName() + "]", t); } }
?
?
获取配置信息初始化
?
?
autoConfig …. public URL findURLOfDefaultConfigurationFile(boolean updateStatus) { ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this); URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus); if (url != null) { return url; } url = Loader.getResource(TEST_AUTOCONFIG_FILE, myClassLoader); if (updateStatus) { statusOnResourceSearch(TEST_AUTOCONFIG_FILE, myClassLoader, url); } if (url != null) { return url; } url = Loader.getResource(AUTOCONFIG_FILE, myClassLoader); if (updateStatus) { statusOnResourceSearch(AUTOCONFIG_FILE, myClassLoader, url); } return url; } public void autoConfig() throws JoranException { StatusListenerConfigHelper.installIfAsked(loggerContext); URL url = findURLOfDefaultConfigurationFile(true); if (url != null) { configureByResource(url); } else { BasicConfigurator.configure(loggerContext); } }
?
?
?
最后画张流程图总结下,^_^
?
?
?
?
?
?
总结: log框架应该很好的诠释了 facade , adapter , 实现上还是比较简单的,很好的做到了接口和实现的分离,对今后的代码组织有一定的启发