java下的slf4j
一、导读
| 
 1 
2 
3 
4 
5 
 | 
SLF4J: Class path contains multiple SLF4J bindings.SLF4J: Found binding in [jar:file:/Users/abc/maven-repository/org/slf4j/slf4j-simple/1.7.26/slf4j-simple-1.7.26.jar!/org/slf4j/impl/StaticLoggerBinder.class]SLF4J: Found binding in [jar:file:/Users/abc/maven-repository/org/apache/logging/log4j/log4j-slf4j-impl/2.7/log4j-slf4j-impl-2.7.jar!/org/slf4j/impl/StaticLoggerBinder.class]SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.SLF4J: Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory] | 

也就是说,当有多个日志实现时,SLF4J会在编译期随机选择其中的一个实现。那么,它到底是如何随机选择的呢?下面我们通过源码来分析。
二、源码分析
2.1 LoggerFactory.getLogger(this.getClass())
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
 | 
public static Logger getLogger(Class<?> clazz) {    Logger logger = getLogger(clazz.getName());    if (DETECT_LOGGER_NAME_MISMATCH) {        Class<?> autoComputedCallingClass = Util.getCallingClass();        if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {            Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),                            autoComputedCallingClass.getName()));            Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");        }    }    return logger;} | 
2.2 LoggerFactory.bind()
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
 | 
private final static void bind() {    try {        Set<URL> staticLoggerBinderPathSet = null;        // skip check under android, see also        // http://jira.qos.ch/browse/SLF4J-328        if (!isAndroid()) {            staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); //在classpath下查找有多少个日志实现            reportMultipleBindingAmbiguity(staticLoggerBinderPathSet); //如果有多个日志实现,打印出来        }        // the next line does the binding。classpath下每个日志实现jar都会有org.slf4j.impl.StaticLoggerBinder类,这里会随机加载其中的一个。        StaticLoggerBinder.getSingleton();        INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;        reportActualBinding(staticLoggerBinderPathSet);        fixSubstituteLoggers();        replayEvents();        // release all resources in SUBST_FACTORY        SUBST_FACTORY.clear();    } catch (NoClassDefFoundError ncde) { //如果在classpath下没有找到一个org.slf4j.impl.StaticLoggerBinder        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.contains("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);    }} | 
2.3 LoggerFactory.findPossibleStaticLoggerBinderPathSet()
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
 | 
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) { //用ClassLoader去查找classpath下有多少个org.slf4j.impl.StaticLoggerBinder类            paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);        } else {            paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);        }        while (paths.hasMoreElements()) {            URL path = paths.nextElement();            staticLoggerBinderPathSet.add(path);        }    } catch (IOException ioe) {        Util.report("Error getting resources from path", ioe);    }    return staticLoggerBinderPathSet;} | 
三、具体日志实现
3.1 slf4j-simple

3.2 slf4j-nop
3.3 logback-classic
3.4 log4j-slf4j-impl
四、总结
- 每个日志实现jar都会有org.slf4j.impl.StaticLoggerBinder类的实现;
 - SLF4J会通过ClassLoad扫描当前classpath下有多少个org.slf4j.impl.StaticLoggerBinder类,也就找到了有多少个日志实现(通过这样,我们只需在项目中加入日志实现的jar包,编译时即可自动加载,业务代码无须显式依赖,实现解耦);
 - 如果有多个org.slf4j.impl.StaticLoggerBinder类,SLF4J会在LoggerFactory.bind()里调用StaticLoggerBinder.getSingleton()随机加载一个日志实现jar的StaticLoggerBinder。
 - 发现一个有趣的现象,在本地idea上运行时,是加载pom.xml里声明的第一个日志实现;但是如果打包好后通过java -jar启动时,其加载的日志实现确实是随机的(是在编译打包时随机加载一个日志实现,所以一旦编译打包好后其加载的那个日志实现就会固定不变)。所以,我们在具体使用时一定要通过排除依赖的方式来确定日志实现,不要由于日志实现的不确定性引入难以排查、不必要的坑。
 
- 作者:水岩
 - 出处:http://www.cnblogs.com/waterystone
 - 本博客中未标明转载的文章归作者水岩和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
 
如果您觉得本文对您的学习有所帮助,可通过支付宝(左) 或者 微信(右) 来打赏博主,增加博主的写作动力
java下的slf4j的更多相关文章
- Java下好用的开源库推荐
		
作者:Jack47 转载请保留作者和原文出处 欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 本文想介绍下自己在Java下做开发使用到的一些开源的优秀编程库,会不定 ...
 - Java下Elasticsearh应用指南
		
简介 本文针对在Java下操作elasticsearch给出应用示例,主要涉及创建连接,构建索引以及检索数据3个部分. 环境 1)elasticsearch2.4.4, 2)jdk1.8. 客户端连接 ...
 - Java下利用Jackson进行JSON解析和序列化
		
Java下利用Jackson进行JSON解析和序列化 Java下常见的Json类库有Gson.JSON-lib和Jackson等,Jackson相对来说比较高效,在项目中主要使用Jackson进行 ...
 - maven项目编译漏掉src/main/java下的xml配置文件
		
在整合Spring + Mybatis框架的时候,自动扫描配置都已经配置好了. 配置如下: <?xml version="1.0" encoding="UTF-8& ...
 - maven 编译部署src/main/java下的资源文件
		
maven 编译部署src/main/java下的资源文件 maven默认会把src/main/resources下的所有配置文件以及src/main/java下的所有java文件打包或发布到targ ...
 - Java 下 SSL 通信原理及实例
		
有关SSL的原理和介绍在网上已经有不少,对于Java下使用keytool生成证书,配置SSL通信的教程也非常多.但如果我们不能够亲自动手做一个SSL Sever和SSL Client,可能就永远也不能 ...
 - Java 下实现锁无关数据结构--转载
		
介绍 通常在一个多线程环境下,我们需要共享某些数据,但为了避免竞争条件引致数据出现不一致的情况,某些代码段需要变成原子操作去执行.这时,我们便需要利用各种同步机制如互斥(Mutex)去为这些代码段加锁 ...
 - 关于Objective-c和Java下DES加密保持一致的方式
		
转载自:http://www.cnblogs.com/janken/archive/2012/04/05/2432930.html 最近做了一个移动项目,是有服务器和客户端类型的项目,客户端是要登录才 ...
 - objective-c和java下解析对象类型和数组类型JSON字符串
		
首先讲objective-c如何实现: 这里需要用到2个插件,一个是JSONKit,另一个是Jastor,一共包含6个文件,3个.h头文件和3个.m实现文件.在ARC的工程中如何导入不支持ARC的第三 ...
 
随机推荐
- 杭电-------2047阿牛的eof牛肉串(C语言写)
			
/* 主要看最后一个是否为O,若为O,则倒数第二个不能为O,则为a[n-2]*1*2; 若不为O,则最后一个有两个选择则为a[n-1]*2 */ #include<stdio.h> ] = ...
 - Jenkins自动化构建vue项目然后发布到远程Linux服务器
			
部署Jenkins参照另一篇博客: centos7安装Jenkins及其卸载 一.jenkins相关插件的安装 1.安装Publish Over SSH插件用于SSH连接远程的服务器. 登录 jenk ...
 - uniapp-使用心得
			
<view class="cu-item flex-sub" :class="index==TabCur?'text-orange cur':''" v- ...
 - 给test函数加个装饰器!
			
import timedef timer(func): def deco(*args,**kwargs): start_time=time.time() func(*args,**kwargs) st ...
 - R语言常用的矩阵操作
			
R语言是一门非常方便的数据分析语言,它内置了许多处理矩阵的方法.下面列出一些常用的矩阵操作方法示例. 矩阵的生成 > mat <- matrix(:, ncol = , nrow = , ...
 - go 环境及4开发
			
国内加速 在gopath目录执行 go env -w GOPROXY=direct go env -w GOSUMDB=off go env -w GOPROXY=https://goproxy.io ...
 - Vue中vue-i18n结合vant-ui实现国际化
			
(一)添加依赖模块 在package.json文件中添加vant模块的依赖,如: // package.json { "dependencies": { "vant&qu ...
 - Kemaswill 机器学习 数据挖掘 推荐系统 Python optparser模块简介
			
Python optparser模块简介
 - Wannafly Winter Camp 2020 Day 5F Inversion Pairs - 拉格朗日插值,dp
			
给定 \(n \leq 10^7\),求所有 \(n\) 的全排列的逆序对个数的 \(k \leq 100\) 次方和 Solution \(f[i][j]\) 表示 \(i\) 个元素,逆序对个数为 ...
 - C# compare different Encoding pattern between UTF8 and UTF32 based on Md5
			
using System; using System.Text; using System.IO; using System.Security.Cryptography; static void Ma ...