承接前文SLF4J源码解析-LoggerFactory(一),本文则主要针对获取ILoggerFactory对象作下简单的分析

LoggerFactory#getILoggerFactory()

源码如下

  public static ILoggerFactory getILoggerFactory() {
***
*** switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITILIZATION:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
case NOP_FALLBACK_INITILIZATION:
return NOP_FALLBACK_FACTORY;
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");
}

根据前文的分析,SL4J获取Logger对象需要classpath下拥有StaticLoggerBinder类,本文则选取logback API来作解析

StaticLoggerBinder.getSingleton().getLoggerFactory()-成功初始化状态下的获取ILoggerFactory()

StaticLoggerBinder.getSingleton()-获取StaticLoggerBinder实例

此处我们只需要分析下StaticLoggerBinder的静态初始化块

  static {
SINGLETON.init();
}

紧接着看下init()方法

  void init() {
try {
try {
//此处的defaultLoggerContext为LoggerContext
//此处也会去加载配置文件
new ContextInitializer(defaultLoggerContext).autoConfig();
} catch (JoranException je) {
Util.report("Failed to auto configure default logger context", je);
}
// logback-292
//检查是否defaultLoggerContext注册了StatusListener
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);
}
}

看下ContextInitializer如何加载相应的配置文件

ContextInitializer#autoConfig()

直接看源码

  public void autoConfig() throws JoranException {
//判断系统变量logback.statusListenerClass是否设定,设定了则注册
//StatusListener
StatusListenerConfigHelper.installIfAsked(loggerContext);
//查找相应的配置文件,我们可以简单的看下
URL url = findURLOfDefaultConfigurationFile(true);
if (url != null) {
//加载相应的配置文件
configureByResource(url);
} else {
//没有则创建名为console的ConsoleAppender
BasicConfigurator.configure(loggerContext);
}
}

我们看下ContextInitializer如何查找配置文件

ContextInitializer#findURLOfDefaultConfigurationFile()

直接阅读源码

  public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
//首先查找系统变量logback.configurationFile指定的配置文件
URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
if (url != null) {
return url;
}
//反之则查找classpath下是否存在名为logback.groovy配置文件
url = getResource(GROOVY_AUTOCONFIG_FILE, myClassLoader, updateStatus);
if (url != null) {
return url;
}
//反之则查找classpath下是否存在名为logback-test.xml配置文件
url = getResource(TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus);
if (url != null) {
return url;
}
//最后则查找classpath下是否存在名为logback.xml的配置文件
return getResource(AUTOCONFIG_FILE, myClassLoader, updateStatus);
}

根据源码整理下logback加载配置文件的顺序

  1. 查找系统变量logback.configurationFile指定的配置文件

  2. 上述不存在则查找classpath下是否存在名为logback.groovy配置文件

  3. 上述不存在则查找classpath下是否存在名为logback-test.xml配置文件

  4. 最后查找classpath下是否存在名为logback.xml的配置文件

注意:此处的classpath特指根目录下,web环境下一般指WEB-INF/classes根目录

ContextInitializer#configureByResource()-加载指定的配置文件

具体源码如下

  public void configureByResource(URL url) throws JoranException {
//指定的文件查找不到则抛出异常
if (url == null) {
throw new IllegalArgumentException("URL argument cannot be null");
}
//针对结尾为groovy的配置文件进行解析
if (url.toString().endsWith("groovy")) {
if (EnvUtil.isGroovyAvailable()) {
// avoid directly referring to GafferConfigurator so as to avoid
// loading groovy.lang.GroovyObject . See also http://jira.qos.ch/browse/LBCLASSIC-214
GafferUtil.runGafferConfiguratorOn(loggerContext, this, url);
} else {
StatusManager sm = loggerContext.getStatusManager();
sm.add(new ErrorStatus("Groovy classes are not available on the class path. ABORTING INITIALIZATION.",
loggerContext));
}
}
//对结尾为xml的文件进行解析
if (url.toString().endsWith("xml")) {
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(loggerContext);
configurator.doConfigure(url);
}
}

具体的解析步骤就不讲解了,读者有兴趣可自行去分析

NOPLoggerFactory#NOP_FALLBACK_INITILIZATION状态下生成的ILoggerFactory

此日志工厂主要生成不打印任何日志的Logger对象

小结

通过分析源码我们可以得知其他的API是如何结合SL4J来实现Logger原理的。本文是以logback为例的,具体的逻辑可见本文的详细内容

SLF4J源码解析-LoggerFactory(二)的更多相关文章

  1. SLF4J源码解析-LoggerFactory(一)

    slf4j的含义为Simple logging facade for Java,其为简单的为java实现的日志打印工具,本文则对其源码进行简单的分析 JAVA调用SLF4J public class ...

  2. Cwinux源码解析(二)

    我在我的个人博客上发表了第二篇解析文章.欢迎各位读者批评指正. Cwinux源码解析(二)

  3. # Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析#

    Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析 Volley源码一共40多个类和接口.除去一些工具类的实现,核心代码只有20多个类.所以相对来说分析起来没有那么吃力.但是要想分析透 ...

  4. Spring事务源码解析(二)获取增强

    在上一篇文章@EnableTransactionManagement注解解析中,我们搭建了源码阅读的环境,以及解析了开启Spring事务功能的注解@EnableTransactionManagemen ...

  5. Netty 源码解析(二):Netty 的 Channel

    本文首发于微信公众号[猿灯塔],转载引用请说明出处 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty源码解析(一):开始 当前:Netty 源码解析(二): Netty 的 Channel ...

  6. AOP源码解析之二-创建AOP代理前传,获取AOP信息

    AOP源码解析之二-创建AOP代理前传,获取AOP信息. 上篇文章对AOP的基本概念说清楚了,那么接下来的AOP还剩下两个大的步骤获取定义的AOP信息,生成代理对象扔到beanFactory中. 本篇 ...

  7. 老生常谈系列之Aop--Spring Aop源码解析(二)

    老生常谈系列之Aop--Spring Aop源码解析(二) 前言 上一篇文章老生常谈系列之Aop--Spring Aop源码解析(一)已经介绍完Spring Aop获取advice切面增强方法的逻辑, ...

  8. 【vuejs深入三】vue源码解析之二 htmlParse解析器的实现

    写在前面 一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. 昨天博主分析了一下在vue中,最为基础核心的api,parse函数,它的作用是将vue的模板字符串转换成ast,从而 ...

  9. RxJava2 源码解析(二)

    概述 承接上一篇RxJava2 源码解析(一),本系列我们的目的: 知道源头(Observable)是如何将数据发送出去的.    知道终点(Observer)是如何接收到数据的.    何时将源头和 ...

随机推荐

  1. Python中创建对象的方法

    源引:Python编程实践 示例类: class Point: __slots__=('x','y') def __init__(self,x,y): self.x=x self.y=y def ma ...

  2. hdu_5868:Different Circle Permutation

    似乎是比较基础的一道用到polya定理的题,为了这道题扣了半天组合数学和数论. 等价的题意:可以当成是给正n边形的顶点染色,旋转同构,两种颜色,假设是红蓝,相邻顶点不能同时为蓝. 大概思路:在不考虑旋 ...

  3. xshell常用命令

    常用的命令: suse linux 常用命令 (1) 命令ls--列出文件 ls 显示当前目录文件 ls -la 给出当前目录下所有文件的一个长列表,包括以句点开头的"隐藏"文件 ...

  4. Python爬取百度贴吧数据

    本渣除了工作外,在生活上还是有些爱好,有些东西,一旦染上,就无法自拔,无法上岸,从此走上一条不归路.花鸟鱼虫便是我坚持了数十年的爱好. 本渣还是需要上班,才能支持我的业余爱好.上班时间还是尽量访问外网 ...

  5. (转)JAVA堆栈操作

    java.util.Queue用法(是util中的一个接口) 队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作.进行插入操作的端称为队尾,进行 ...

  6. 【javascript】回调函数

    1. 定义 回调函数,即当条件满足时执行的函数.有三种方法实现调用回调函数 call 1)call 用法:call(thisObj, Obj) 主要区别:call 方法会将函数对象上下文修改为this ...

  7. openstack中使用linux_bridge实现vxlan网络

    openstack环境: 1 版本:ocata 2 系统:ubuntu16.04.2 3 控制节点 1个 + 计算节点 1个 4 控制节点网卡为ens33,ip = 172.171.5.200 ens ...

  8. Vue组件基础用法

    前面的话 组件(Component)是Vue.js最强大的功能之一.组件可以扩展HTML元素,封装可重用的代码.根据项目需求,抽象出一些组件,每个组件里包含了展现.功能和样式.每个页面,根据自己所需, ...

  9. 华为软件开发云CloudIDE功能简测

    华为软件开发云CloudIDE是面向软件开发者的云端开发环境,支持在线编写代码,智能提示,代码提交,代码片段智能搜索等功能. CloudIDE实现了大部分IDE的功能,同时克服了全能IDE的缺点. 一 ...

  10. Xamarin.Forms(二) 返回页面的数据刷新

    这几天在做一个课程表的小程序,遇到了这样一个问题: app打开便是课程表的页面,如果课程表页面没有数据的话需要跳转到同步课表页面模拟登陆教务管理系统获取课表,并显示在课程表页面,这样就需要从同步课表页 ...