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的第三 ...
随机推荐
- 前端vue开发中的跨域问题解决,以及nginx上线部署。(vue devServer与nginx)
前言 最近做的一个项目中使用了vue+springboot的前后端分离模式 在前端开发的的时候,使用vue cli3的devServer来解决跨域问题 上线部署则是用的nginx反向代理至后台服务所开 ...
- Java 添加超链接到Excel文档
超链接即内容链接,通过给特定对象设置超链接,可实现载体与特定网页.文件.邮件.网络等的链接,点击链接载体可打开链接目标,在文档处理中是一种比较常用的功能.本文将介绍通过Java程序给Excel文档添加 ...
- 代理模式-jdk动态代理
IDB package com.bjpowernode.proxy; /** * 代理类和目标类都必须使用同一个接口. */ public interface IDB { int insert(); ...
- Shiro -- (一)简介
简介: Apache Shiro 是一个强大易用的 Java 安全框架,提供了认证.授权.加密和会话管理等功能,对于任何一个应用程序,Shiro 都可以提供全面的安全管理服务.并且相对于其他安全框架, ...
- 【POJ - 3186】Treats for the Cows (区间dp)
Treats for the Cows 先搬中文 Descriptions: 给你n个数字v(1),v(2),...,v(n-1),v(n),每次你可以取出最左端的数字或者取出最右端的数字,一共取n次 ...
- appium+python+unittest+HTMLRunner登录自动化测试报告
环境搭建 python3Java JDK.netFrameworknodejsandroid SDKappiumAppium-Python-Client(pip install Appium-Pyth ...
- opencv —— HoughLines、HoughLinesP 霍夫线变换原理(标准霍夫线变换、多尺度霍夫线变换、累积概率霍夫线变换)及直线检测
霍夫线变换的原理 一条直线在图像二维空间可由两个变量表示,有以下两种情况: ① 在笛卡尔坐标系中:可由参数斜率和截距(k,b)表示. ② 在极坐标系中:可由参数极经和极角(r,θ)表示. 对于霍夫线变 ...
- 消息总线:Spring Cloud Stream
最近在学习Spring Cloud的知识,现将消息总线:Spring Cloud Stream 的相关知识笔记整理如下.[采用 oneNote格式排版]
- Centos7下安装包方式安装MySQL
安装包下载地址:https://cdn.mysql.com//Downloads/MySQL-5.7/mysql-5.7.27-1.el7.x86_64.rpm-bundle.tar 第一步:在 /h ...
- 【python基础语法】数字、布尔值(第1天课堂笔记)
# 导入模块 import keyword # print语句将内容输出到控制台 print("hello world!") # pep8编码规范 # 代码快速格式化快捷键:ctr ...