什么是slf4j框架?
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.
这个是官方的介绍,意思就是slf4j是一个简单的java日志框架facade,facade是门面的意思,相当于一个服务的接口,具体的日志功能由具体的日志(如java的util包中的logging、logback、log4j)框架提供,使用slf4j可以在部署的时候接上想要使用的日志框架。
从这里可以理解,slf4j不提供任何服务,它只是一个facade,相当于一个通用的接口。
使用slf4j需要用到
- slf4j-api-1.x.xx.jar
从包名可以看出来这个包是和api有关的,仅仅是api肯定是无法工作的。如果我们想要使用slf4j需要依赖另外的jar包,而这些依赖的jar包称为"SLF4J bindings"。官方原文解释SLF4J bindings:
The SLF4J distribution ships with several jar files referred to as "SLF4J bindings", with each binding corresponding to a supported framework.
按照官方介绍的SLF4J bindings有如下几个:
- slf4j-log4j12-1.7.21.jar
slf4j-log4j用于整合log4j,使用非常广泛,使用时需要在依赖里加上log4j的jar包。
- slf4j-jdk14-1.7.21.jar
slf4j-jdk用于整合java.util.logging。只要是jdk1.4以上就OK。
- slf4j-nop-1.7.21.jar
slf4j-nop提供了一个没有任何实现代码的实现。什么意思呢,我截图slf4j包下的一个方法。
可以看出来,方法体里面啥都没有。
- slf4j-simple-1.7.21.jar
slf4j-simple提供了简单的实现,除了info或者更高级别的日志会print外,全部都打印到System.err上。
- slf4j-jcl-1.7.21.jar
slf4j-jcl整合jcl,jcl没听过。。。先无视。
- logback-classic-1.0.13.jar
没听过logback。。。无视
从这里可以知道想要使用slf4j不仅仅是slf4j-api这个包就行了,还需要具体的日志框架。比如我之前做过的项目都用log4j,所以我必须有slf4j-log4j这个包和原本的log4j包。
如何使用slf4j?
我猜大部分人在深入了解一个框架前就会用这个框架了。因为如果都没用过根本不会去深入了解。
用slf4j很简单,下面是我在项目里的用法
public class xxx {
...
...
public static final Logger logger = LoggerFactory.getLogger(xxx.class);
...
...
public void login(){
...
logger.info("ID为{},名为{}的用户登录了服务器!",xx,xx);
...
}
}
在类里面定义一个静态的Logger属性,属性由LoggerFactory的getLogger方法初始化。getLogger方法接收一个class对象,作用是输出时把类名作为日志的前缀。
这里我在用户登录服务器的时候通过logger对象的info方法来输出了一段日志。
需要注意的是slf4j到1.7版本之后才支持可变参数。
如info方法:void info(String var1, Object... var2);
在此之前多参数都是通过数组实现的:void info(String var1, Object[] var2);
从源码分析slf4j
从使用过程来看,slf4j的核心类很明显是Logger类,但是看了源码会发现Logger类只是一个接口。这个不难理解,因为slf4j只是一个 Facade。
浏览一遍Logger接口,会发现里面的方法有规律,根据规律可以把接口的方法分成5大类
- trace
- debug
- info
- warn
- error
从方法名可以看出来这些方法是日志的5个级别,也就是Logger类是一个提供输出不同级别日志的接口。
Logger类的初始化方式是通过LoggerFactory来创建的。从名字一看就知道这里采用了工厂设计模式。Logger可以是不同的实现,但是创建过程我们不过关心,交给工厂来操作就OK了。
通过LoggerFactory的getLogger(Class<?> clazz)方法可以创建一个Logger LoggerFactory.getLogger(xxx.class);
方法内部其实是调用了另外一个重载的方法Logger logger = getLogger(clazz.getName());
也就是使用了类的全限定名来作为参数。
getLogger(String name)方法又调用了getILoggerFactory()方法,这个方法的作用是获得一个Logger工厂。getILoggerFactory方法很关键,它调用了SLF4J bindings的StaticLoggerBinder类。比如我的项目里用了Log4j,这里StaticLoggerBinder会返回一个Log4jLoggerFactory对象。
得到了Log4jLoggerFactory就可以得到Log4j的Logger了吗?也没那么快。因为Log4jLoggerFactory是slf4j-log4j包的类。也就是说,还没有真正调用Log4j。slf4j-log4j相当于只是一个中间件,来协调Log4j和Slf4j。
我们来看看Log4jLoggerFactory的getLogger方法,方法里面调用了LogManager的getLogger方法。LogManager才是log4j的类。从名字可以看出来LogManager是用来管理Logger的类。暂时称它为log管理器吧,log管理器里面也有一个getLogger方法,这里才是真正创建Log4j的Logger的地方。调用了log管理器的getLogger后,Log4jLoggerFactory的getLogger方法就得到了一个Log4j的Logger。
Log4jLoggerFactory得到了Log4j的Logger对象之后会把Logger返回给Slf4j-api嘛?我们看源码,并不是这样。这里使用了一个适配器Log4jLoggerAdapter。看Log4jLoggerAdapter的源码会发现它也实现了Logger接口,所以Log4jLoggerFactory可以直接把适配器返回给Slf4j-api。
Log4jLoggerAdapter是干嘛的呢,适配器嘛,就是为了适应某一个东西,比如这里是为了适配Log4j。也就是说吧Log4j封装,在调用Logger接口的时候能够使用Log4j的实现。
好了下面给大家看一个我自己做的时序图,第一次做,感觉有地方没做好,因为还不熟练,之后的博客会经常给大家画时序图。