1.logback这货是怎么启动呢?
就系统启动的时候,会自动读取配置文件,以便后续代码执行这操行,那必定是在 static block中执行的
static{
do logic;
}
并且这个类要被加载,static block也好,static变量也好 才会执行
关于static block 参见我曾在百度知道的一个回帖
http://zhidao.baidu.com/question/294516388
顺着藤,摸着瓜,果不其然 logback-classic-1.1.13.jar中的 org.slf4j.impl.StaticLoggerBinder类
/** * The unique instance of this class. */ private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder(); private static Object KEY = new Object(); static { SINGLETON.init(); }
我们来看看 init()方法
void init() { try { try { new ContextInitializer(defaultLoggerContext).autoConfig(); } catch (JoranException je) { Util.report("Failed to auto configure default logger context", je); } // logback-292 if(!StatusUtil.contextHasStatusListener(defaultLoggerContext)) { StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext); } contextSelectorBinder.init(defaultLoggerContext, KEY); initialized = true; } catch (Throwable t) { // we should never get here Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t); } }
跟踪代码, 发现
step1. 加载配置文件
关于加载配置文件的顺序, 可以参看 我的这个帖子 logback1.1.13配置文件加载顺序
step2. 构造 contextSelector
我们来看看 ch.qos.logback.classic.util.ContextSelectorStaticBinder.init(LoggerContext, Object) 代码
public void init(LoggerContext defaultLoggerContext, Object key) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { if(this.key == null) { this.key = key; } else if (this.key != key) { throw new IllegalAccessException("Only certain classes can access this method."); } String contextSelectorStr = OptionHelper .getSystemProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR); if (contextSelectorStr == null) { contextSelector = new DefaultContextSelector(defaultLoggerContext); } else if (contextSelectorStr.equals("JNDI")) { // if jndi is specified, let's use the appropriate class contextSelector = new ContextJNDISelector(defaultLoggerContext); } else { contextSelector = dynamicalContextSelector(defaultLoggerContext, contextSelectorStr); } }
转换成流程图是 :
2.好,那么问题来了,代码中使用 log.debug(叉叉叉),log.info(叉叉叉),log.warn(叉叉叉),log.info(叉叉叉)等等会发生什么事情呢? 为毛这里的log 是slf4j的log 会调用到logback的配置呢?
好,带着问题,我们再来走两步
瞧瞧这个简单的 HelloWorldTest 测试类
public class HelloWorldTest{ /** The Constant log. */ private static final Logger log = LoggerFactory.getLogger(HelloWorldTest.class); @Test public void testHelloWorldTest(){ log.error("hello world"); } }
我们上面的问题,其实就发生在 LoggerFactory.getLogger(HelloWorldTest.class)
通过跟踪代码,我们发现 初始化 perform Initialization会调用
org.slf4j.LoggerFactory.findPossibleStaticLoggerBinderPathSet() 来查找项目中的 "org/slf4j/impl/StaticLoggerBinder.class" 类
private 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) { paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH); } else { paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH); } while (paths.hasMoreElements()) { URL path = (URL) paths.nextElement(); staticLoggerBinderPathSet.add(path); } } catch (IOException ioe) { Util.report("Error getting resources from path", ioe); } return staticLoggerBinderPathSet; }
当然咯,从代码中可见, 如果项目中 有多个 org/slf4j/impl/StaticLoggerBinder.class 类 会提示 "Class path contains multiple SLF4J bindings."
private static void reportMultipleBindingAmbiguity(Set<URL> staticLoggerBinderPathSet) { if (isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) { Util.report("Class path contains multiple SLF4J bindings."); Iterator<URL> iterator = staticLoggerBinderPathSet.iterator(); while (iterator.hasNext()) { URL path = (URL) iterator.next(); Util.report("Found binding in [" + path + "]"); } Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation."); } }
而 org.slf4j.LoggerFactory.bind()方法中的 StaticLoggerBinder.getSingleton();
首先会加载StaticLoggerBinder类,从而调用本文开头写的static block方法,从而调用 init方法
private final static void bind() { try { Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); reportMultipleBindingAmbiguity(staticLoggerBinderPathSet); // the next line does the binding StaticLoggerBinder.getSingleton(); INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION; reportActualBinding(staticLoggerBinderPathSet); fixSubstitutedLoggers();
而这些操作都执行完了,会调用 StaticLoggerBinder.getSingleton().getLoggerFactory() 方法返回
public static ILoggerFactory getILoggerFactory() { 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://bugzilla.slf4j.org/show_bug.cgi?id=106 return TEMP_FACTORY; } throw new IllegalStateException("Unreachable code"); }
我们再来看看 org.slf4j.impl.StaticLoggerBinder.getLoggerFactory() 方法
public ILoggerFactory getLoggerFactory() { if (!initialized) { return defaultLoggerContext; } if (contextSelectorBinder.getContextSelector() == null) { throw new IllegalStateException( "contextSelector cannot be null. See also " + NULL_CS_URL); } return contextSelectorBinder.getContextSelector().getLoggerContext(); }
好,这么一转,又回到了我们之前写的 contextSeletor部分了
从而基本上,我们可以确定,
一般情况下, 会使用ch.qos.logback.classic.selector.DefaultContextSelector,使用的是 默认的 ch.qos.logback.classic.LoggerContext
3.如果重复调用 Logger log = LoggerFactory.getLogger(HelloWorldTest.class) 会怎么样呢?
改造下代码
public class HelloWorldTest{ /** * TestHelloWorldTest. * * @throws InterruptedException */ @Test public void testHelloWorldTest() throws InterruptedException{ Logger log = LoggerFactory.getLogger(HelloWorldTest.class); log.error("hello world"); } }
从代码中发现, 如果第二次调用
public static ILoggerFactory getILoggerFactory() { if (INITIALIZATION_STATE == UNINITIALIZED) { INITIALIZATION_STATE = ONGOING_INITIALIZATION; performInitialization(); } switch (INITIALIZATION_STATE) { case SUCCESSFUL_INITIALIZATION: return StaticLoggerBinder.getSingleton().getLoggerFactory();
此处直接返回 StaticLoggerBinder.getSingleton().getLoggerFactory();
由于StaticLoggerBinder 是单例模式的设计,此处不会重复初始化log context
而从LoggerFactory 到log 的过程中,我们发现 log 有 map(ConcurrentHashMap) cache 机制,第一次会通过logger name得到log 会设置到map cache中,第二次,相同的logger name 会直接从map cache 中返回
public final Logger getLogger(final String name) { if (name == null) { throw new IllegalArgumentException("name argument cannot be null"); } // if we are asking for the root logger, then let us return it without // wasting time if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) { return root; } int i = 0; Logger logger = root; // check if the desired logger exists, if it does, return it // without further ado. Logger childLogger = (Logger) loggerCache.get(name); // if we have the child, then let us return it without wasting time if (childLogger != null) { return childLogger; }
4.好,让我们转成流程图
相关推荐
NULL 博文链接:https://feitianbenyue.iteye.com/blog/1828929
奔月生物:2021年半年度报告.PDF
鲁迅《奔月》作品分析.pdf
奔月生物:2021年半年度报告.rar
奔月生物:2019年半年度报告.PDF
插件描述:纯HTML5兔子奔月吃月饼游戏,可分享到朋友圈. 参考示例:http://www.jq22.com/jquery-info6154
火箭兔奔月HTML5游戏源码,运行需要服务器环境,已经反复测试,放心使用。
HTML5兔子奔月吃月饼游戏源码
手绘实例:奔月 Photoshop3 图片处理 高级手绘技术技巧
长娥奔月模板下载TIF
析鲁迅《奔月》.docx
初中语文文摘生活且看嫦娥咋奔月
jfreechart-1.0.14-javadocs chm格式 因为带有目录索引,所以打成rar 包上传 希望大家喜欢 --create by 飞天奔月
HTML5兔子奔月吃月饼游戏源码.zip
[鲁迅]析鲁迅《奔月》.docx
小游戏源码-火贱兔奔月.rar
Photoshop合成奔月女孩梦幻艺术照片效果
专题嫦娥二号成功发射开创奔月新纪元
HTML5实现兔子中秋奔月吃月饼游戏源码.zip