slf4j 之logback日志之sl4j架构【二】
一、整体介绍
介绍:
The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired logging framework at deployment time.
工作模式:
sl4j-api结构图
根据入口代码来分析。
Logger logger = LoggerFactory.getLogger(logbackTest.class);
我们可以知道初始化,是从这个方法开始的。
首先入口代码非常简单
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
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");
}
上面基本上就是整个调用流程的代码了。第5行通过factory模式得到了一个日志工厂,第6行根据名字获取日志logger。
第3个函数是主要的逻辑函数,根据INITIALIZATION_STATE 不同状态,做不同的处理。
当INITIALIZATION_STATE ==0时,初始化日志框架【9行】
当INITIALIZATION_STATE ==1时,返回的TEMP_FACTORY,我们尅看下定义【15行】
static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory();
SubstituteLoggerFactory是什么尼,很简单的结构,是由sl4j实现的一个。。,看一下文档描述:
SubstituteLoggerFactory 简单实现了ILoggerFactory接口,并总是返回一个单例的 NOPLogger实例。
作用:
* It used as a temporary substitute for the real ILoggerFactory during its
* auto-configuration which may re-enter LoggerFactory to obtain logger
* instances. See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
当INITIALIZATION_STATE ==2时,返回的失败信息【17行】,其中我们可以看下各种状态枚举定义,可以知道2对应的状态是日志初始化失败
static final int ONGOING_INITIALIZATION = 1;
static final int FAILED_INITIALIZATION = 2;
static final int SUCCESSFUL_INITIALIZATION = 3;
static final int NOP_FALLBACK_INITIALIZATION = 4;
当INITIALIZATION_STATE ==3时,返回的失败信息【19行】,显然是日志初始化成功,获取日志单例信息。
当INITIALIZATION_STATE ==4时,返回的失败信息【21行】,我们看见返回会的是NOP_FALLBACK_FACTORY(没有找到桥接jar)对象,看下定义
static NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();
/**
* NOPLoggerFactory is an trivial implementation of {@link
* ILoggerFactory} which always returns the unique instance of
* NOPLogger.
*
* @author Ceki Gülcü
*/
public class NOPLoggerFactory implements ILoggerFactory { public NOPLoggerFactory() {
// nothing to do
} public Logger getLogger(String name) {
return NOPLogger.NOP_LOGGER;
} }
根据定义和和注释,我们我们可以大胆猜测,4状态就是什么都不做,NO Operator Logger Factory.仅仅是一个空实现。
官网描述:SINCE 1.6.0 If no binding is found on the class path, then SLF4J will default to a no-operation implementation.
二、核心代码分析
重点分析这个方法performInitialization();
private final static void performInitialization() {
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
versionSanityCheck();
}
}
private final static void bind() {
try {
Set staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
// the next line does the binding
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
emitSubstituteLoggerWarning();
} catch (NoClassDefFoundError ncde) {
String msg = ncde.getMessage();
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
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 {
failedBinding(ncde);
throw ncde;
}
} catch (java.lang.NoSuchMethodError nsme) {
String msg = nsme.getMessage();
if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -) {
INITIALIZATION_STATE = FAILED_INITIALIZATION;
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.");
}
throw nsme;
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}
首先进入bind方法,
bind方法第9行,findPossibleStaticLoggerBinderPathSet(),根据名字我们知道这个函数的功能是查找可能的StaticLoggerBinder路径集合。
private static Set findPossibleStaticLoggerBinderPathSet() {
// use Set instead of list in order to deal with bug #138
// LinkedHashSet appropriate here because it preserves insertion order during iteration
Set staticLoggerBinderPathSet = new LinkedHashSet();
try {
ClassLoader loggerFactoryClassLoader = LoggerFactory.class
.getClassLoader();
Enumeration 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;
}
我们可以看到都是在加载StaticLoggerBinder.class类
private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
那么StaticLoggerBinder.class是干嘛的尼?传送门:http://skyao.github.io/2014/07/21/slfj4-binding/,后续会对StaticLoggerBinder仔细分析。
bind方法第10行,假设找到多个StaticLoggerBinder.class,就提示输出信息。
bind方法第12行,很关键的一行代码,和第10行关联起来了。获取一个StaticLoggerBinder单例.
private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
public static StaticLoggerBinder getSingleton() {
return SINGLETON;
}
static {
SINGLETON.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);
}
}
可以看出其实就是一个单例,并初始化了。初始化的具体详情,我们在后续会分析。这里我们只需要知道得到了一个StaticLoggerBinder单例对象即可。
bind方法14,15,16行主要是改变状态,显示警告信息等。
由此我们知道bind方法核心功能是加载了StaticLoggerBinder.class,并初始化了单例对象。
我们回到上面getILoggerFactory方法。
getILoggerFactory方法19行代码:StaticLoggerBinder.getSingleton().getLoggerFactory();
即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();
}
在第二行判断initialized状态在init()方法第20行知道,在初始化完成后,即为true。所以getLoggerFactory方法会直接到第6行。
private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder.getSingleton();
public static ContextSelectorStaticBinder getSingleton() {
return singleton;
}
static ContextSelectorStaticBinder singleton = new ContextSelectorStaticBinder();
第6行获取到一个ContextSelectorStaticBinder的实例。并且获取getContextSelector方法
public ContextSelector getContextSelector() {
return contextSelector;
}
ContextSelector contextSelector;
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);
}
}
我们查询代码知道getContextSelector获取的对象是在init()中方法赋值的。
在StaticLoggerBinder类中init()方法第19行代码,进行了ContextSelectorStaticBinder的实例化。
contextSelectorBinder.getContextSelector()根据代码知道,其实就是LoggerContext包装对象。
第10代码,contextSelectorBinder.getContextSelector().getLoggerContext();得到一个LoggerContext对象。
即,我们的getILoggerFactory的方法,得到了一个LoggerContext对象。
在getLogger方法第二行代码,传递name获取Logger对象。
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 = ;
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;
} // if the desired logger does not exist, them create all the loggers
// in between as well (if they don't already exist)
String childName;
while (true) {
int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
if (h == -) {
childName = name;
} else {
childName = name.substring(, h);
}
// move i left of the last point
i = h + ;
synchronized (logger) {
childLogger = logger.getChildByName(childName);
if (childLogger == null) {
childLogger = logger.createChildByName(childName);
loggerCache.put(childName, childLogger);
incSize();
}
}
logger = childLogger;
if (h == -) {
return childLogger;
}
}
}
至此,整个流程就走完了。
下一章,我们会对整个sl4j架构进行分析。分析代码的优缺点。
-------------------------------------------------------------------------华丽分割线,下面是StaticLoggerBinder代码分析-------------------------------
在我们的源码目录中,并没有这个类
那么他在哪里尼?这就是实现接口反转和适配很重要的一个环节了,还记得我们开始第一幅图么
,
sl4j-api会调用
看下类类定StaticLoggerBinder.class:binding一个实际的ILoggerFactory的实例。
* The binding of {@link LoggerFactory} class with an actual instance of
* {@link ILoggerFactory} is performed using information returned by this class.
来吧,一言不合就看源码:
public class StaticLoggerBinder implements LoggerFactoryBinder { /**
* Declare the version of the SLF4J API this implementation is compiled
* against. The value of this field is usually modified with each release.
*/
// to avoid constant folding by the compiler, this field must *not* be final
public static String REQUESTED_API_VERSION = "1.6"; // !final final static String NULL_CS_URL = CoreConstants.CODES_URL + "#null_CS"; /**
* The unique instance of this class.
*/
private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder(); private static Object KEY = new Object(); static {
SINGLETON.init();
} private boolean initialized = false;
private LoggerContext defaultLoggerContext = new LoggerContext();
private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder
.getSingleton(); private StaticLoggerBinder() {
defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
} public static StaticLoggerBinder getSingleton() {
return SINGLETON;
} /**
* Package access for testing purposes.
*/
static void reset() {
SINGLETON = new StaticLoggerBinder();
SINGLETON.init();
} /**
* Package access for testing purposes.
*/
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);
}
} 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();
} public String getLoggerFactoryClassStr() {
return contextSelectorBinder.getClass().getName();
} }
先看下类图:
slf4j 之logback日志之sl4j架构【二】的更多相关文章
- lombok+slf4j+logback SLF4J和Logback日志框架详解
maven 包依赖 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lomb ...
- SLF4J和Logback日志框架详解
SLF4J和Logback日志框架详解 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs 本文讲述SLF4J和Logback日志框架. SLF4J是一套 ...
- 日志之slf4j和logback日志系统(二)
这篇文章我们讲一下,如何使用slf4j和logback组合来搭建一套日志系统. 介绍 如果我们的系统是新系统,也就是之前没有引入其他的日志工具,那么只需要引入,如果之前已经用了common-loggi ...
- slf4j 之logback日志之环境安装【一】
一.maven引用. 传送门:http://www.slf4j.org/manual.html#projectDep <dependency> <groupId>ch.qos. ...
- 日志框架(Log4J、SLF4J、Logback)--日志规范与实践
文章目录 一.Log4j 1.1新建一个Java工程,导入Log4j包,pom文件中对应的配置代码如下: 1.2resources目录下创建log4j.properties文件. 1.3输出日志 1. ...
- java日志,(commons-loging 、log4j 、slf4j 、LogBack介绍)
如果对于commons-loging .log4j .slf4j .LogBack 等都已经非常清楚了,可以忽略本文.几次解决日志冲突问题时对这几个概念的简单总结,希望对这块基础没有理解透的同学能有所 ...
- SLF4J - 借助SLF4J, 统一适配所有日志实现为logback日志实现的实践
一.屏蔽各种日志实现,去掉各种日志实现的实现依赖 二.引入slf4j和各种日志实现的适配器 1.引入slf4j 2.引入各种日志实现的适配器(适配到slf4j) 3.引入logback 引入logba ...
- IDEA项目搭建十——使用slf4j和logback进行日志记录
.简介 java里面日志分为两部分一个门面.一个实现,我们所熟知的SLF4j.Log4j.Log4j2.Logback的日志组件slf4j是门面提供的统一的入口,具体实现由log4j.log4j2.l ...
- 【使用篇二】SpringBoot的日志体系及如何开启logback日志(15)
抄自:https://blog.csdn.net/liujun03/article/details/82684209 Java应用中,日志一般分为以下5个级别(从高到低): ERROR 错误信息 WA ...
随机推荐
- Android call setting 源码分析 (上)
Android 的 call setting 是用来设定与 simcard 相关的一些内容的应用程序,如网络,PIN等等,算是AP层.这里就选择其中一个项从源代码读下去直到底层,看看大概的结构和流程. ...
- Android消息推送之各种方案的对比
C2DM/GCM: 优点:免费,搭建方便 缺点:依赖谷歌服务器:免费服务有上限:依赖谷歌服务包:需要2.2+版本的安卓系统才支持:对网络状况有一定要求,接收不可靠.(也是基于XMPP协议) XMPP( ...
- 一口一口吃掉Hexo(一)
如果你想得到更好的阅读效果,请访问我的个人网站 ,版权所有,未经许可不得转载! 这里是我的个人博客网站,点击这里你可以到我的首页瞧瞧.我之前使用的是第三方的博客平台--博客园,点击这里可以看到我的博客 ...
- 结构-行为-样式-Css笔记
0.常见的行级元素和块级元素: 行级元素:div,table,form ,ul,ol,p,h1-h6,hr; 块级元素:a,select,input,textarea,img,label,br,i, ...
- JDK1.8源码阅读系列之一:ArrayList
本篇随笔主要描述的是我阅读 ArrayList 源码期间的对于 ArrayList 的一些实现上的个人理解,有不对的地方,请指出- 先来看一下 ArrayList 的继承图: 由图可以看出,Array ...
- Python random模块(获取随机数)常用方法和使用例子
random.randomrandom.random()用于生成一个0到1的随机符点数: 0 <= n < 1.0 random.uniformrandom.uniform(a, b),用 ...
- lzo压缩格式文件查看
使用lzop命令解压并查看 :lzop -cd xxx.lzo |more 附压缩命令:lzop xxx.log (生成xxx.log.lzo) 其它参数: # lzop -v test # 创建te ...
- Selenium也是一个用于Web应用程序测试的工具
Selenium也是一个用于Web应用程序测试的工具.Selenium测试直接运行在浏览器中,就像真正的用户在操作一样.支持的浏览器包括IE.Mozilla Firefox.Mozilla Suite ...
- json字符串参数
jsp部分 json字符串的属性应该都是实体类的属性 function saveCashier(){ layer.closeAll(); var Reapply = document.g ...
- centos 编译安装nginx
这里选用的是nginx-1.10.1稳定版,其基础依赖库有gcc.gcc-c++.pcre.zlib和openssl. pcre.zlib和openssl这三个依赖库在安装nginx时无需编译安装,下 ...