一、整体介绍

介绍:

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架构【二】的更多相关文章

  1. lombok+slf4j+logback SLF4J和Logback日志框架详解

    maven 包依赖 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lomb ...

  2. SLF4J和Logback日志框架详解

    SLF4J和Logback日志框架详解 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs 本文讲述SLF4J和Logback日志框架.   SLF4J是一套 ...

  3. 日志之slf4j和logback日志系统(二)

    这篇文章我们讲一下,如何使用slf4j和logback组合来搭建一套日志系统. 介绍 如果我们的系统是新系统,也就是之前没有引入其他的日志工具,那么只需要引入,如果之前已经用了common-loggi ...

  4. slf4j 之logback日志之环境安装【一】

    一.maven引用. 传送门:http://www.slf4j.org/manual.html#projectDep <dependency> <groupId>ch.qos. ...

  5. 日志框架(Log4J、SLF4J、Logback)--日志规范与实践

    文章目录 一.Log4j 1.1新建一个Java工程,导入Log4j包,pom文件中对应的配置代码如下: 1.2resources目录下创建log4j.properties文件. 1.3输出日志 1. ...

  6. java日志,(commons-loging 、log4j 、slf4j 、LogBack介绍)

    如果对于commons-loging .log4j .slf4j .LogBack 等都已经非常清楚了,可以忽略本文.几次解决日志冲突问题时对这几个概念的简单总结,希望对这块基础没有理解透的同学能有所 ...

  7. SLF4J - 借助SLF4J, 统一适配所有日志实现为logback日志实现的实践

    一.屏蔽各种日志实现,去掉各种日志实现的实现依赖 二.引入slf4j和各种日志实现的适配器 1.引入slf4j 2.引入各种日志实现的适配器(适配到slf4j) 3.引入logback 引入logba ...

  8. IDEA项目搭建十——使用slf4j和logback进行日志记录

    .简介 java里面日志分为两部分一个门面.一个实现,我们所熟知的SLF4j.Log4j.Log4j2.Logback的日志组件slf4j是门面提供的统一的入口,具体实现由log4j.log4j2.l ...

  9. 【使用篇二】SpringBoot的日志体系及如何开启logback日志(15)

    抄自:https://blog.csdn.net/liujun03/article/details/82684209 Java应用中,日志一般分为以下5个级别(从高到低): ERROR 错误信息 WA ...

随机推荐

  1. java.lang.IllegalArgumentException: Wrong FS ...异常的解决

    配置完Hbase后,启动,JPS发现少了HMaster这个进程.查看了一下日志如下: java.lang.IllegalArgumentException: Wrong FS: hdfs://192. ...

  2. Android开发(22)--seekBar采用handler消息处理操作

    本案例简单实现进度条可走,可拖拽的功能,下面请看源码: 布局文件: <RelativeLayout xmlns:android="http://schemas.android.com/ ...

  3. anadonca环境配置和模块安装

    1.最方便的python环境配置: 下载anaconda即可,自带spyder,集成科学计算的库,自带pip,不用折腾. 想用sublime编写python并运行的话,需要自己配置编译环境,并下载插件 ...

  4. Java自带的性能监测工具用法简介——jstack、jconsole、jinfo、jmap、jdb、jsta、jvisualvm

    JDK内置工具使用 一.javah命令(C Header and Stub File Generator) 二.jps命令(Java Virtual Machine Process Status To ...

  5. 当我们在谈论kmeans(3)

        本系列意在长期连载分享,内容上可能也会有所删改: 因此如果转载,请务必保留源地址,非常感谢! 博客园:http://www.cnblogs.com/data-miner/(暂时公式显示有问题) ...

  6. mysql数据恢复问题

    现象 mysql> drop database zabbix; Query OK, 104 rows affected (0.30 sec)mysql> exitBye[root@mysq ...

  7. 第一百三十节,JavaScript,封装库--连缀

    JavaScript,封装库--连缀 学习要点: 1.连缀介绍 2.改写库对象 本章我们重点来介绍,在调用库的时候,我们需要能够在前台调用的时候可以同时设置多个操作,比如设置CSS,设置innerHT ...

  8. 安卓---android:versionCode和android:versionName 用途

    主要用于升级和自我识别,转自:http://blog.csdn.net/wh_19910525/article/details/8660416 Android的版本可以在androidmainfest ...

  9. C#笔记(一)常量

    常量必须在声明时初始化 常量的值必须能在编译时用于计算.因此,不能用从一个变量中提取的值来初始化常量. 常量总是静态的.但注意,不必(实际上,是不允许)在常量声明中包含修饰符static .

  10. (转载)js 快捷键大全,并有简单使用说明

    摘要: (转载)原文链接: http://www.cnblogs.com/fire-phoenix/archive/2010/09/28/1837295.html Code highlighting ...