SLF4J的全称是Simple Logging Facade for Java,是当前最流行的日志(包装)框架之一,它不是完成的一套日志框架实现,它主要用作各种日志框架(例如java.util.logging,logback,log4j)的简单外观或抽象,允许最终用户在部署时插入具体的日志框架。可以直接将SLF4J看做其他日志框架的统一API,用SLF4J的好处是,如果后期用户需要切换日志框架的实现,不需要改代码,只需要调整部分jar的依赖即可。
那么你一定很好奇,SLF4J是如何做到统一各个日志框架API的?本文试图给你揭露这一“秘密”。
部分已经流行的日志框架出现的比SLF4J早,比如java.util.logging、log4j等,所以为了制定一套统一的API而直接去这些日志实现框架做“手脚”已经不可能,即下面这一条路走不通:
于是乎,SLF4J想到了加一个适配层,加一个适配层的直接好处是使我们适配老的日志组件成为了可能,而且还能在适配层做些特殊化的处理,注意,这里的适配层并不是统一的一层,而是每个日志实现框架都有一个对应的适配层,这样管理起来更加方便:
如果这样做的话,会有多个适配的Jar出现,而且这些Jar的通用部分(比如统一的API)无法收拢来统一实现,为了抽离公共部分,于是有了SLF4J-API这一层:
这样的话,公共部分和定制化的实现都能更自由的处理和管理,那让我们看看真实的SLF4J的架构图:
我们可以看到,SLF4J-API层的实现就是slf4j-api.jar,而适配层的jar都是slf4j开头,例如Log4j的适配层实现是slf4j-log4j12.jar(注意,上图少了个j)。slf4j-api.jar里面有统一个API,其中有两个重要接口,即日志对象接口org.slf4j.Logger、日志工厂接口org.slf4j.ILoggerFactory,当然,slf4j-api.jar里面不会仅有接口,它还有一些实现类,因为SFL4J还肩负一个重要任务,那就是绑定具体的日志框架实现。
我们接下来看看slf4j-api.jar是如何做到绑定具体的日志框架的,前面提到过,SLF4J-API层需要和适配层联动才能做到这一点,一想到动态加载和绑定,Java体系里比较有名的就是SPI了,SPI的全称是Service Provider Interface,即服务提供者接口,其目的是为了在Java体系中能通过动态绑定接口实现类来支持第三方框架或组件的扩展,JVM中还提供了一个定制化的ClassLoader(即java.util.ServiceLoader)来实现这一点(但其实不太好用,功能也不强大,所以像Dubbo等框架都自己实现),SLF4J(注意,SLF4J的老版本是没有使用SPI的)也使用了SPI。我们一般都如下使用SLF4J(代码片段来自SLF4J官网):
我们在使用SLF4J的时候,并没有显示的去调用他的初始化方法,所以在调用LoggerFactory.getLogger方法时,SLF4J内部会进行状态检查,如果没有初始化过,就会直接进行初始化,其中的日志框架的绑定就是初始化的最重要内容,我们直接通过IDEA的调用关系图来了解这一过程:
上图中的LogFactory.getProvider中会去判断初始化状态,如果没有,那么将同步进行初始化,代码如下:
可见,同一时刻有且只有一个线程能去调用初始化方法,即performInitialization下面的bind方法:
从上面的代码片段来看,我们发现了两个重要方法,即 findServiceProviders 和 findPossibleStaticLoggerBinderPathSet,findServiceProviders是标准的通过SPI来加载日志实现框架的方法,而findPossibleStaticLoggerBinderPathSet方法是为了兜底老版本的实现,需要注意的是,老版本没有SPI的方式,如果将1.7.26版本之后的slf4j-api.jar和老版本的适配层jar一起使用,流程会走到了这里,将匹配到NOPLogger,导致不输出日志也不报错(但启动时会有日志提示)。这里还有一个关键点,即PROVIDER = providersList.get(0)的调用,说明了对于SPI机制,加载的第一个适配层将是SLF4J选定绑定的日志框架实现!我们看下findServiceProviders的实现:
这里的是标准的Java SPI的使用方式,适配层的jar只要实现了SLF4JServiceProvider接口和SPI规范的一些配置即可被slf4j-api.jar发现并加载,例如在Log4j的适配slf4j-log4j12.jar中是这样做的:
如果通过SPI没有发现任何日志框架的实现怎么办(比如1.7.26之前的老版本)?所以在这里仍然保留了老版本的加载方式,即findPossibleStaticLoggerBinderPathSet方法的实现:
这里可以看出,SFL4J尝试去发现所有StaticLoggerBinder类资源文件(注意,这里没有使用ClassLoader.loadClass方法是为了能发现所有的StaticLoggerBinder),StaticLoggerBinder是老版本SLF4J和适配层之间的连接契约,即适配层只要有StaticLoggerBinder这个类,就有机会被slf4j-api.jar来发现,我们看老版本的slf4j-log4j12.jar(例如1.7.25版本)中的StaticLoggerBinder实现(新版本已经不用这种方式,也没有这个类):
可以看出,StaticLoggerBinder是作为单例来使用的,最重要的方法就是getLoggerFactory,用来直接返回包装过Log4j的日志工厂接口ILoggerFactory(之前提到过的slf4j-api.jar的两个最重要的接口之一)的实现类。那么读者会有疑问,在老版本中,如果有多个适配实现,会使用哪一个呢?于是我们得看老版本的LoggerFacotry的bind方法实现方式,这里给出1.7.25版本的bind方法:
其中我们发现了关键的StaticLoggerBinder.getSingleton()调用,所以在老版本中,如果存在多个适配层的实现,那么JVM先加载哪个适配层的StaticLoggerBinder类,那么就相当于SLF4J绑定了哪个适配层。
这里做一个总结,本文简单分析了SLF4J的架构设计,已经新老版本slf4j-api.jar是如何绑定底层的日志框架实现的,希望帮助同学了解内部的一些设计考量和实现。
相关推荐
在给定的标题和描述中,我们看到了两个关键的日志框架——`log4j`和`SLF4J(Simple Logging Facade for Java)`,以及它们之间的桥接器`slf4j-log4j12`。这些组件是Java日志处理的常用工具,让我们详细了解一下它们...
org.slf4j.ILoggerFactory.class org.slf4j.IMarkerFactory.class org.slf4j.Logger.class org.slf4j.LoggerFactory.class org.slf4j.MDC.class org.slf4j.Marker.class org.slf4j.MarkerFactory.class org.slf4j....
1、jcl-over-slf4j-1.7.21.jar 2、jcl-over-slf4j-1.7.21-sources.jar 3、jul-to-slf4j-1.7.21.jar 4、jul-to-slf4j-1.7.21-sources.jar 5、log4j-over-slf4j-1.7.21.jar 6、log4j-over-slf4j-1.7.21-sources....
该压缩包中包含以下内容: 1、jcl-over-slf4j-1.7.21.jar 2、jcl-over-slf4j-1.7.21-sources.jar 3、jul-to-slf4j-1.7.21.jar 4、jul-to-slf4j-1.7.21-sources.jar 5、log4j-over-slf4j-1.7.21.jar 6、log4j-over-...
SLF4J(Simple Logging Facade for Java)是Java中一个重要的日志抽象层,它为各种日志框架,如Log4j、java.util.logging、Logback等提供了一个统一的接口。通过SLF4J,开发者可以在不修改代码的情况下更换底层的...
该压缩包中包含以下内容: 1、jcl-over-slf4j-1.7.21.jar 2、jcl-over-slf4j-1.7.21-sources.jar 3、jul-to-slf4j-1.7.21.jar 4、jul-to-slf4j-1.7.21-sources.jar 5、log4j-over-slf4j-1.7.21.jar 6、log4j-over-...
SLF4J(Simple Logging Facade for Java)是Java中的一种日志抽象层,它提供了一个API,允许开发者在运行时插入所需的日志框架。SLF4J的主要目的是为各种日志框架,如log4j、logback等,提供一个统一的接口,使得...
SLF4J(Simple Logging Facade for Java)是Java中的一种日志抽象层,它提供了一个接口,允许用户在运行时动态地绑定到各种具体的日志框架,如Log4j、Java内置的日志或者Logback等。这个设计使得开发者可以在不修改...
SLF4J(Simple Logging Facade for...`slf4j-api-1.7.12.jar`和`slf4j-log4j12-1.7.12.jar`分别是SLF4J API和SLF4J到Log4j的绑定,它们共同工作,使开发者能够利用Log4j的强大功能,同时保持代码与具体日志系统的分离。
slf4j-api-1.6.0.jar,slf4j-jdk14-1.6.0.jar,slf4j-log4j12-1.6.0-rc0.jar,slf4j-nop-1.6.0.jar,slf4j-simple-1.6.0.jar
Java Slf4j,全称为Simple Logging Facade for Java,是一个为各种日志框架提供一个简单统一的接口,使得最终用户能够在部署时配置他们希望的日志框架。Slf4j允许开发者在部署应用时插入所需的日志实现,无需修改...
描述中提到的"slf4j-api-1.7.26.jar"是SLF4J API的核心库,它包含所有开发者需要使用的接口和类,例如`org.slf4j.Logger`和`org.slf4j.LoggerFactory`。`Logger`接口提供了不同级别的日志记录方法,如`trace()`, `...
SLF4J(Simple Logging Facade for Java)是一个用于Java编程语言的日志门面框架,它提供了一个简单的抽象层,用以使用各种日志框架,例如java.util.logging、logback和log4j。门面模式的好处在于,开发者可以在不同...
SLF4J(Simple Logging Facade for Java)是Java中一个重要的日志接口框架,它为各种日志实现提供了一个抽象层,使得开发者能够在运行时动态地选择具体使用的日志实现,例如Log4j、Java Util Logging (JUL)、Logback...
在使用SLF4J和Log4j12时,你需要注意的一点是,由于Log4j1.2相比Log4j2在某些方面可能较旧,例如性能和功能更新,因此在新项目中,你可能会考虑使用更新的SLF4J绑定器,如slf4j-log4j2,以便利用Log4j2的改进特性。...
1. **slf4j.api-1.6.1.jar**:这是SLF4J的核心API库,包含了所有SLF4J的接口和注解,使得开发者可以在代码中使用SLF4J的API进行日志记录。例如,`org.slf4j.Logger`和`org.slf4j.LoggerFactory`是SLF4J中最常用的类...
标题中的"slf4j-api-1.6.1-slf4j-nop-1.6.1.rar"表明这是一个包含SLF4J API和NOP绑定的日志库的压缩包。描述中提到,解压后会得到两个JAR文件:slf4j-api-1.6.1.jar和slf4j-nop-1.6.1.jar。 **SLF4J API** 1. **...
SLF4J(Simple Logging Facade for Java)是Java中的一种日志门面,它提供了一个简单的接口,允许开发者在不修改代码的情况下切换不同的日志实现。SLF4J的出现是为了统一各种日志框架,如Log4j、java.util.logging、...
赠送jar包:log4j-over-slf4j-1.7.33.jar; 赠送原API文档:log4j-over-slf4j-1.7.33-javadoc.jar; 赠送源代码:log4j-over-slf4j-1.7.33-sources.jar; 赠送Maven依赖信息文件:log4j-over-slf4j-1.7.33.pom; ...