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的第三 ...
随机推荐
- Gridview的stretchMode等属性详解
<GridView android:id="@+id/grid"android:layout_width="fill_parent"android:lay ...
- 利用VS Code在Azure上构建部署静态页面
0x00 前言 前一段时间,我找到了Jendrik Illner的个人网站.除了那里的精彩文章,网站的主题也吸引了我的注意力,而且我发现该网站的主题采用了Hugo的Academic主题. 然后,我认为 ...
- Java的引用类型的内存分析
一. jdk的内存:jdk的bin目录常见命令 1. javac.exe:编译java源代码的,生成java字节码文件(*.class) 2. java.exe:启动一个jvm,来运行指定class字 ...
- fancybox图片灯箱功能
fancybox图片灯箱功能 在页面中引入几个文件 <script src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery ...
- 基于arduino的气象站
bmp180的简介: • 压力范围:~1100hPa(海拔 米~- 米) • 电源电压:.8V~.6V(VDDA), .62V~.6V(VDDD) • 尺寸:.6mmx3.8x0.93mm • 低功耗 ...
- LeetCode 57. Insert Interval 插入区间 (C++/Java)
题目: Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if nec ...
- DNS | named.run文件很大的处理方法
在查看/var/named/data目录中发现named.run文件260G+ 总用量 267G -rw-r--r-- 1 named named 1.3K 9月 2 21:27 named.run ...
- cf912D
题意简述:往n*m的网格中放k条鱼,一个网格最多放一条鱼,然后用一个r*r的网随机去捞鱼,问怎么怎么放鱼能使得捞鱼的期望最大,输出这个期望 题解:肯定优先往中间放,这里k不大,因此有别的简单方法,否则 ...
- MySQL 8 InnoDB 集群管理
使用 dba.checkInstanceConfiguration() 在添加实例到集群中前,使用该方法检查实例配置是否满足InnoDB 集群要求. 使用 dba.configureLocalInst ...
- 为实践javaweb项目,搭建了相应环境
为实践javaweb项目,搭建了相应环境,现总结一下. JDK与JRE的安装与配置 前提准备: 1.我们下载的JDK安装包里面既包含JDK又包含JRE: 2.要确认你的电脑里面没有JDK和JRE的残留 ...