SLF4J源码解析-LoggerFactory(一)
slf4j的含义为
Simple logging facade for Java
,其为简单的为java实现的日志打印工具,本文则对其源码进行简单的分析
JAVA调用SLF4J
public class Test{
private static fianl Logger log = LoggerFactory.getLogger(Test.class) ;
public static void main(String[] args){
log.debug();
log.info();
log.error();
log.fatal();
}
}
注意调用slf4j接口用的是
LoggerFactory.getLogger()
方法,与log4j调用的LogManager.getLogger()
有点区别
Logger接口
内部属性概览
final public String ROOR_LOGGER_NAME = "ROOT" ;
public boolean isTraceEnabled();
public void trace(String msg);
....
....
简单的看也就是定义了相应的级别输出方法,比如trace()/info()/error()/debug()等
LoggerFactory内部属性
其为final类型的class。罗列部分属性,具体如下
//代表日志工具的初始化状态
static final int UNINITIALIZED = 0;
static final int ONGOING_INITILIZATION = 1;
static final int FAILED_INITILIZATION = 2;
static final int SUCCESSFUL_INITILIZATION = 3;
static final int NOP_FALLBACK_INITILIZATION = 4;
//返回的均为NOPLogger类,即不打印任何日志信息
static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory();
static NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();
LoggerFactory#getLogger()-获取日志对象
其为静态方法,源码如下
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
继续查看下LoggerFactory#getILoggerFactory()方法
LoggerFactory#getILoggerFactory()-获取真实的日志工厂
源码如下
public static ILoggerFactory getILoggerFactory() {
//初次获取,初始化状态为0
if (INITIALIZATION_STATE == UNINITIALIZED) {
//状态置为1
INITIALIZATION_STATE = ONGOING_INITILIZATION;
//进行初始化
performInitialization();
}
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");
}
继续观察LoggerFactory#performInitialization()方法
LoggerFactory#performInitialization()-初始化日志操作
private final static void performInitialization() {
//检查项目部署的环境即classpath下有无StaticLoggerBinder.class
singleImplementationSanityCheck();
//有则开始绑定
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITILIZATION) {
//对slf4j支持的版本进行确认
versionSanityCheck();
}
}
分别看下LoggerFactory#singleImplementationSanityCheck()方法和LoggerFactory#bind()方法
LoggerFactory#singleImplementationSanityCheck()-特定类存在判断
特定类指的是org/slf4j/impl/StaticLoggerBinder.class
。具体源码如下
private static void singleImplementationSanityCheck() {
try {
//获取类加载器
ClassLoader loggerFactoryClassLoader = LoggerFactory.class
.getClassLoader();
//存放特定类的个数,当无相应的资源返回为空集合,但不为null
Enumeration paths;
if (loggerFactoryClassLoader == null) {
paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
} else {
paths = loggerFactoryClassLoader
.getResources(STATIC_LOGGER_BINDER_PATH);
}
List implementationList = new ArrayList();
//对获取org/slf4j/impl/StaticLoggerBinder.class的资源进行遍历
while (paths.hasMoreElements()) {
URL path = (URL) paths.nextElement();
implementationList.add(path);
}
//打印成功日志
if (implementationList.size() > 1) {
Util.report("Class path contains multiple SLF4J bindings.");
for (int i = 0; i < implementationList.size(); i++) {
Util.report("Found binding in [" + implementationList.get(i) + "]");
}
Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
}
} catch (IOException ioe) {
Util.report("Error getting resources from path", ioe);
}
}
主要任务是判断是否存在
org/slf4j/impl/StaticLoggerBinder.class
,其在为bind()方法作预备调查,此方法目的是打印一些帮助信息。注意此处不建议拥有多个StaticLoggerBinder
类,一般要求只有单个StaticLoggerBinder
类存在,不然则会导致日志输出不了
LoggerFactory#bind()-绑定获取真实的日志处理类
bind()方法的处理逻辑显得就很有意思了
private final static void bind() {
try {
//获取StaticLoggerBinder单例
// the next line does the binding
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITILIZATION;
emitSubstituteLoggerWarning();
} catch (NoClassDefFoundError ncde) {
//对无此类的异常处理
String msg = ncde.getMessage();
//如果错误信息含有org/slf4j/impl/StaticLoggerBinder信息则设置初始化状态为4
if (msg != null && msg.indexOf("org/slf4j/impl/StaticLoggerBinder") != -1) {
INITIALIZATION_STATE = NOP_FALLBACK_INITILIZATION;
Util
.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
Util.report("Defaulting to no-operation (NOP) logger implementation");
Util.report("See " + NO_STATICLOGGERBINDER_URL
+ " for further details.");
} else {
//状态为2,并抛出异常
failedBinding(ncde);
throw ncde;
}
} catch(java.lang.NoSuchMethodError nsme) {
//对StaticLoggerBinder类无getSingleton()方法做处理
String msg = nsme.getMessage();
if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) {
INITIALIZATION_STATE = FAILED_INITILIZATION;
Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
Util.report("Your binding is version 1.5.5 or earlier.");
Util.report("Upgrade your binding to version 1.6.x. or 2.0.x");
}
throw nsme;
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}
bind()方法对
StaticLoggerBinder#getSingleton()
方法做了异常捕获处理,处理逻辑如下:
对不存在StaticLoggerBinder类的
NoClassDefFoundError
异常,如果错误信息含有org/slf4j/impl/StaticLoggerBinder
信息则不抛出异常,但设置状态为NOP_FALLBACK_INITILIZATION(4)
;反之则直接抛出异常,并设置状态为FAILED_INITILIZATION(2)
对存在StaticLoggerBinder类但不存在getSingleton()方法的
NoSuchMethodError
异常,均抛出异常,并设置状态为FAILED_INITILIZATION(2)
LoggerFactory#versionSanityCheck()-日志版本要求验证
源码如下
private final static void versionSanityCheck() {
try {
//此处为1.6
String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
//判断是否与API的版本一致,此处为true
boolean match = false;
for (int i = 0; i < API_COMPATIBILITY_LIST.length; i++) {
if (requested.startsWith(API_COMPATIBILITY_LIST[i])) {
match = true;
}
}
if (!match) {
Util.report("The requested version " + requested
+ " by your slf4j binding is not compatible with "
+ Arrays.asList(API_COMPATIBILITY_LIST).toString());
Util.report("See " + VERSION_MISMATCH + " for further details.");
}
} catch (java.lang.NoSuchFieldError nsfe) {
// given our large user base and SLF4J's commitment to backward
// compatibility, we cannot cry here. Only for implementations
// which willingly declare a REQUESTED_API_VERSION field do we
// emit compatibility warnings.
} catch (Throwable e) {
// we should never reach here
Util.report("Unexpected problem occured during version sanity check", e);
}
}
目前的slf4j版本为1.6
小结
slf4j相当于是一个抽象接口,其会判断classpath下是否存在
StaticLoggerBinder
类,并针对此类进行相应的逻辑处理,于此我们可以判断出,其可以很好的被其他日志API接入,比如logback等
下节内容预告
针对返回状态为
SUCCESSFUL_INITILIZATION
、NOP_FALLBACK_INITILIZATION
、FAILED_INITILIZATION
、ONGOING_INITILIZATION
时,创建的为何种ILoggerFactory,详情见SLF4J源码解析-LoggerFactory(二)
SLF4J源码解析-LoggerFactory(一)的更多相关文章
- SLF4J源码解析-LoggerFactory(二)
承接前文SLF4J源码解析-LoggerFactory(一),本文则主要针对获取ILoggerFactory对象作下简单的分析 LoggerFactory#getILoggerFactory() 源码 ...
- log4j源码解析
前言:本文将在slf4j的基础上解释log4j的应用,阅读本文前可先行阅读SLF4J源码解析-LoggerFactory(二) 前言概要 在前言中提到的slf4j的基础,其主要是通过logback的a ...
- t-io 集群解决方案以及源码解析
t-io 集群解决方案以及源码解析 0x01 概要说明 本博客是基于老谭t-io showcase中的tio-websocket-showcase 示例来实现集群.看showcase 入门还是挺容易的 ...
- 日志那点事儿——slf4j源码剖析
前言: 说到日志,大多人都没空去研究,顶多知道用logger.info或者warn打打消息.那么commons-logging,slf4j,logback,log4j,logging又是什么关系呢?其 ...
- solr&lucene3.6.0源码解析(三)
solr索引操作(包括新增 更新 删除 提交 合并等)相关UML图如下 从上面的类图我们可以发现,其中体现了工厂方法模式及责任链模式的运用 UpdateRequestProcessor相当于责任链模式 ...
- Flume-ng源码解析之Channel组件
如果还没看过Flume-ng源码解析之启动流程,可以点击Flume-ng源码解析之启动流程 查看 1 接口介绍 组件的分析顺序是按照上一篇中启动顺序来分析的,首先是Channel,然后是Sink,最后 ...
- log4j源码解析-文件解析
承接前文log4j源码解析,前文主要介绍了log4j的文件加载方式以及Logger对象创建.本文将在此基础上具体看下log4j是如何解析文件并输出我们所常见的日志格式 附例 文件的加载方式,我们就选举 ...
- Ocelot简易教程(七)之配置文件数据库存储插件源码解析
作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9852711.html 上篇文章给大家分享了如何集成我写的一个Ocelot扩展插件把Ocelot的配置存储 ...
- Spring5源码解析-论Spring DispatcherServlet的生命周期
Spring Web框架架构的主要部分是DispatcherServlet.也就是本文中重点介绍的对象. 在本文的第一部分中,我们将看到基于Spring的DispatcherServlet的主要概念: ...
随机推荐
- SSH中的Invalid action class configuration that references an unknown class named.......
最近用SSH框架做项目的时候页面提交数据到后台,遇到了这个问题,百度了一下,网上的解决办法无非两种: 1.检查struts.xml ,applicationContext.xml的配置是否正确 2. ...
- PHP面向对象 ——多态
接口 接口(interface)是抽象方法和静态常量定义的集合. 接口是一种特殊的抽象类,这种抽象类中只包含抽象方法和静态常量. 接口中没有其它类型的内容 接口的定义 /** * 接口可以认为是特殊的 ...
- Centos 6.5 安装python3.6
废话不多说,直接上步骤 wget https://www.python.org/ftp/python/3.6.2/Python-3.6.2rc1.tgz 进入 https://www.python.o ...
- scroll事件实现监控滚动条并分页显示示例(zepto.js )
今天做了一个类似于手机端京东首页的页面,效果图如下: 刷新页面的时候,标题栏(也就是搜索栏),背景是透明的,当我们往下滑的时候,可以改变标题栏的背景透明度(渐变效果): 当标题栏滑过轮播图后,透明度就 ...
- css动画属性--小球移动
主体只有一个div <body> <div></div> </body> 样式部分(测试:目前的浏览器还是需要加前缀才能兼容) <style> ...
- JavaScript中的设计模式:单例模式
定义 单例模式就是一个类只能实例化一个对象,并且提供了一个访问它的全局访问点. 一般在实现登陆框,或者一个全局的控制器都会用到单例模式.现实中有window对象,线程池,全局缓存等. 简单实现 var ...
- [编辑器]vim常用操作
我是ide的用户,对于vim一只停留在:打开.看.写.关闭基本操作,因为现在更多的接触linux服务器,所以为了提高 效率,用好vim是必备技能!下面罗列一些vim的常用操作,用做备忘(不断更新): ...
- 【PHP】this,self,parent的区别(转)
一. 定义&区别 self: 指向当前类的指针,self是不指向任何已经实例化的对象,一般self使用来指向类中的静态变量. this: 指向当前对象的指针,使用parent来调用父类的构造函 ...
- ES6中的类
前面的话 大多数面向对象的编程语言都支持类和类继承的特性,而JS却不支持这些特性,只能通过其他方法定义并关联多个相似的对象,这种状态一直延续到了ES5.由于类似的库层出不穷,最终还是在ECMAScri ...
- hadoop系列三:mapreduce的使用(一)
转载请在页首明显处注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/7224772.html 一:说明 此为大数据系列的一些博文,有空的话会陆续更新,包含大数据的 ...