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.好,让我们转成流程图
相关推荐
标题中的“【飞天奔月出品】windows版nginx 快速操控神器(启动start,关闭stop,重启restart) 批处理”指的是一个专为Windows操作系统设计的Nginx管理工具,它通过批处理脚本实现了Nginx服务的便捷启动、停止和重启...
奔月生物:2021年半年度报告.PDF
2. **经营业绩**:详细展示2021年上半年的销售收入、净利润、毛利润等关键财务指标,对比去年同期的数据,分析增长或下滑的原因。 3. **财务分析**:深入解析公司的资产负债表、利润表和现金流量表,评估公司的财务...
从文化视角分析,《奔月》揭示了中国传统文化中的某些消极因素。鲁迅将国民性概括为“愚弱”,表现为缺乏是非观念和不敢抗争的懦弱。嫦娥的冷漠、老太太的愚昧、逢蒙的狡诈,都在一定程度上映射了这种国民性。鲁迅借...
2. **公司更名与地址变更**:2019年1月16日,公司名称由“山东奔月生物科技有限公司”变更为“山东奔月生物科技股份有限公司”,同时生产地址也进行了更新,从“山东东营利津刁口政府驻地”迁移到“山东省东营市利津...
HTML5奔月游戏是一款完全基于HTML5技术开发的互动小游戏,玩家可以扮演一只兔子,目标是在游戏中尽可能地收集月饼并安全抵达月球。这款游戏的亮点在于其无需任何额外的插件或软件支持,只需浏览器就能运行,这得益于...
在火箭兔奔月这个游戏源码中,我们可以期待看到以下关键技术和概念: 1. **Canvas绘图**:游戏的主要画面很可能是通过`canvas`元素进行绘制的,包括火箭兔的动画、月球背景和其他游戏元素。开发者会利用JavaScript...
《H5游戏源码解析:奔月游戏》 在当今数字化时代,HTML5(简称H5)技术以其跨平台、轻量级、易部署的特点,成为制作网页游戏的热门选择。"奔月游戏"作为一款H5游戏,其源码为我们提供了一窥H5游戏开发的窗口。本文...
手绘实例:奔月 Photoshop3 图片处理 高级手绘技术技巧
在奔月游戏中,可能使用canvas来绘制角色、背景、轨迹等元素,并通过定时更新画面来实现动画效果。 2. **Web Audio API**:HTML5提供了Web Audio API,用于处理和播放音频。游戏往往需要背景音乐和音效,这个API...
- 打开素材2,使用你喜欢的方法(例如钢笔工具或通道)将女孩抠出并拖入"奔月女孩"文件中,新图层命名为"女孩"。 - 使用自由变换(Ctrl+T)调整女孩的大小和位置。 3. **色彩平衡调整**: - 选择"女孩"图层,通过...
【标题】"小游戏源码-火贱兔奔月.rar" 提供的是一个小型游戏的源代码,名为"火贱兔奔月"。这类源码通常用于教学、学习或游戏开发者的参考,帮助开发者理解游戏的基本架构和编程逻辑。 【描述】"小游戏源码-火贱兔...
【长娥奔月模板下载TIF】是一个与网页设计相关的资源,主要提供了一种以"长娥奔月"为主题的网页模版。这个模版可能是为了庆祝中国传统节日,如中秋节,或者用于讲述中国古老的神话故事。"长娥奔月"是中国文化中的...
《鲁迅的《奔月》:颠覆传统,开创审美新向度》 鲁迅的短篇小说《奔月》是其《故事新编》中的一篇,它颠覆了我们对古代神话的传统认知,尤其对嫦娥这一角色的刻画,使得这篇作品在文学史上占据了独特的地位。鲁迅...
游戏源码分享下载 --- hjby.zipHTML5小游戏【火贱兔奔月--425款经典优秀H5小游戏合集】游戏源码分享下载 --- hjby.zipHTML5小游戏【火贱兔奔月--425款经典优秀H5小游戏合集】游戏源码分享下载 --- hjby.zipHTML5小...
2. **JavaScript编程**: 游戏逻辑和交互功能通常由JavaScript控制。JavaScript用于响应用户的输入(如点击或触摸),更新游戏状态,处理碰撞检测,计算分数,以及控制游戏流程。此外,JavaScript还可以实现计时器...
综上所述,鲁迅的《奔月》不仅是对传统神话的颠覆,更是对现实社会的深刻剖析。通过将传统神圣凡俗化,鲁迅开辟了新的审美向度,体现了他的先锋精神。他的作品不仅是文学艺术的创新,也是对时代问题的敏锐洞察和批判...
在提供的文件列表中,“使用须知.txt”很可能是包含了游戏运行的说明和注意事项,比如如何启动游戏、如何操作兔子移动等。而“132692081374528955”可能是一个压缩的文件或者数据,可能包含了游戏的主要代码、图像...
2. JavaScript文件:主要包含游戏的逻辑代码,包括初始化、事件处理、游戏循环、角色行为、分数计算等核心功能。 3. CSS文件:定义游戏界面的样式,包括背景、按钮、角色样式等,可能包含关键帧动画来实现动态效果。...
初中语文文摘生活且看嫦娥咋奔月