接口规范:
- commons-logging (Jakarta Commons Logging 、jcl ),它会默认去找log4j和jul(java.util.log包 jdk1.4之后)
- slf4j ( Simple Logging Facade For Java),logback直接实现了这个接口
- jboss-logging
接口实现的框架:
- log4j 一个完整的日志框架,可以直接使用
- logback 实现了 上面的slf4j接口
- jul jdk1.4以后 java.util.log下提供的日志实现
- log4j2
- slf4j-nop
- slf4j-simple
- 别的我也不知道了
使用接口的好处,不要让项目太依赖一个日志框架
官网提供了一张图清晰的描述了这个过程
![1abce20079b3dcfa44db64e1f180a17b.png](https://i-blog.csdnimg.cn/blog_migrate/590eefb36312d0a48c43820a91400b19.jpeg)
slf4j-api的maven地址
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
注意这里是没有具体实现的!
当我们直接使用
private
![2238f5a96ec56316d2feaeb03de3fadf.png](https://i-blog.csdnimg.cn/blog_migrate/33c55ec59c74a294ae0111cbb4cd1437.png)
说明要加入一个具体的实现类,具体怎么加,就看上面那张图就好了。
注意,logback是默认实现slf4j接口的,所以它不需要适配器!(图中只有两层)
比如,我想使用logback,对应的maven
<!-- 这样一个就好了,会自动引入logback-core包,具体的logback包自己上网查下哈 -->
在比如,我想使用log4j,log4j没有实现slf4j接口,所以需要一个适配器
<!-- 适配器 -->
(版本自己注意,我随便网上抄的)
可以看看这个适配器的包做了啥,看看包结构
![9cd00544c34c3c064d4c63f125010e94.png](https://i-blog.csdnimg.cn/blog_migrate/40cd54025ed15d82a47d9e68ad2d9120.jpeg)
实现了`StaticLoggerbinder`这个类哦,注意这里引入多个实现类的时候会报错。
![bed3f54f42f098dda230fb11b8bd1aad.png](https://i-blog.csdnimg.cn/blog_migrate/e62a07642ca32b1541f7d01df37695ed.png)
看下这个适配包做了啥吧,可以自己debug做一下
它的总体思路就是
![90710f5253acb97e9ef36999f643b2cb.png](https://i-blog.csdnimg.cn/blog_migrate/921d2897aabf6e9d3f0d8b4f3c846f47.jpeg)
在Log4jLoggerFactory中,使用log4j中的org.apache.log4j.LogManager获取了log4j的日志对象了。(就是org.slf4j:slf4j-log4j这个包,调用了log4j:log4j这个包的内容了)
包装一下,返回这个适配器,之后我就可以在这个适配器中调用log4j的方法了。
![2ea90a9a167bb4a6b07ad49b50bf7b7b.png](https://i-blog.csdnimg.cn/blog_migrate/a073e9b3540136a103a05365a01e00b0.png)
还有一种桥接包,使用的情况:
我现在的项目使用了某个日志框架,我想使用slf4j,提供反转的在作用。
注意和上面的正向适配过程的区别,上面的是我还没开始做项目,我决定使用slf4j + log4j 来写代码。
但是在旧的项目中,已经存在了大量的使用log4j的代码,这是没有办法改变的。
官网也有一个图。
![15c22cff4fc191333cf1877a27747b1e.png](https://i-blog.csdnimg.cn/blog_migrate/df529b9f84002c728a5401bb40e06e60.jpeg)
举个例子
![0ff4692f1fda6f28ea454e70a9745a17.png](https://i-blog.csdnimg.cn/blog_migrate/608a1e58a165f97e51dc27445204eb98.jpeg)
我首先要做的引入jar包,同时将原来的log4j的包移除。(可以看出来,这个log4j-over-slf4j包相当于欺骗了slf4j,原来的代码中调用了log4j:log4j包中的,现在变成调用了org.slf4j:log4j-over-slf4j中的了 )
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic-->
可以debug看下它究竟干了啥
![c5224e93c9678e433918a853a10c40c7.png](https://i-blog.csdnimg.cn/blog_migrate/b35e686e85ccfa21564c63679897e72b.jpeg)
现在原来log4j的代码都执行到log4j-over-slf4j中了。
秘密就在于这里的new Logger(),这个Logger也是这个包的中 的,它继承了一个类 Catagory
这个类在初始化的时候
![3f5f2e2dfd99d5867dfa07fa5bd24a7b.png](https://i-blog.csdnimg.cn/blog_migrate/59f3d489de5c5a317578ae451d192b90.jpeg)
其实就已经设置好了slf4j的实现对象。slf4jLogger 对象,其实就是logback包里面的了。
这里说下LoggerFactory.getLogger(name)这个原理,这个是slf4j-api.jar里面的方法,它的本质就是引入 import org.slf4j.impl.StaticLoggerBinder;
我们知道实现了slf4j的包都会有这个对象,那么随后
里面这句代码很重要:
StaticLoggerBinder. getSingleton().getLoggerFactory();
这句代码让流程从 slf4j-api包 --> slf4j的实现包 (可能是桥接包或者直接实现的logback包)
return iLoggerFactory.getLogger(name);
得到的对象就是logback包里的啦
![347a67aac9403bf23a2eb3d5acaf574e.png](https://i-blog.csdnimg.cn/blog_migrate/7098ebb51135827ffd9fe9e29b3ee8d0.png)
此后我们在调用的时候,调用其实就是这里面Catagory属性中的上slf4j中的东西了。
这里要注意,我们将原来log4j代码全部交给log4j-over-slf4j执行了,其实就是移交给slf4j-api执行了,这个api要寻找实现类,如果这个时候我们在使用slf4j-log4j12作为实现,那么可以会造成踢皮球的现场导致栈溢出,具体的原因可以看看最下方的参考链接。
比如 log.trace()的时候。
![b1fa8fcd20a27cead47dabd7799582d2.png](https://i-blog.csdnimg.cn/blog_migrate/7e59fb9c4fe6ecd8ae446cb7fbba16ee.jpeg)
![e5f3caeacb285bb49b5f7e37597ad2a1.png](https://i-blog.csdnimg.cn/blog_migrate/7fe0de5d103aefc778d0f0baaa80de3c.png)
![92e848ba0047641dbceb878c42225c7f.png](https://i-blog.csdnimg.cn/blog_migrate/76198bd4b6fd735c91291d792e72ac85.jpeg)
可以看出来,这个logger其实就是logback包中的log了。
总结一下:
- 两类桥接包,一种给新项目使用,一种给旧项目使用。
- 通常使用的接口只有commons-logging 和 slf4j,commons-logging经常和log4j使用,slf4j经常和logback使用。
- 答案都在源码里。
参考链接:
log4j-over-slf4j与slf4j-log4j12共存stack overflow异常分析
SpringBoot日志框架的选择及使用原理_爆发的~小宇宙的博客-CSDN博客_springboot 日志框架