java日志发展史 log4j slf4j log4j2 jul jcl 日志和各种桥接包的关系
一切事情的发展都是有缘由的,java的日志为啥会发展成现在这个样子,我们来梳理下java日志的发展过程。 我们在项目中经常会遇到jar包冲突,而且看起来包名都差不多,既头大又懵逼,网上随便一搜,各种关系图看的更晕了,都是些啥玩意儿。
索性干脆把日志型痛理清楚,我们现在学习技术,好的地方是技术一般都比较成熟,比较多的坑都被人踩过了,解决方案也比较多,但是不好的地方就是,由于容易使用,追求效率的同时就忽略了技术本身,为什么有这项技术,为了解决什么问题,原理是什么。
言归正传,一件事情很难梳理清楚,那么当你从根上把这件事的来龙去脉梳理清楚了,这件事情也就没有那么难以理解了。
最初是没有我们使用的这些日志技术的,最开始的日志打印方式
System.out System.err
这应该是最早的日志记录方式,不灵活不可配置,要么全部打印要么不要打印,没有日志级别的管理。
log4j
log4j的出现可以追溯到1996年,1996年初,豆粥安全电子市场(E.U.SEMPER)项目决定编写自己的项目跟踪API,这个API就是后来大名鼎鼎的log4j,log4j已经推出就异常火爆,提到log4j就不得不提其主要贡献者,ceki gulcu,后来log4j成了apache基金会的一员,让log4j一度称为了业界的日志标杆,apache据说建议sun把log4j引入到java的jdk中,后来sun拒绝了,并采纳了这个建议,自己出了一个日志库。
jul(java util logging)
sun在2002年2月推出了java 1.4发布,sun竟然推出了自己的日志库java utillogging,其实很多日志思想也都仿照log4j,毕竟log4j已经比较成熟了,显然log4j更加成熟。
JCL(Jakarta Commons Logging)
apache针对刚出来的jul,有搞了一套jcl,打算一统日志江湖,指定日志标准,jcl是日志抽象层,默认有一个Simple log的实现,(就像jdbc一统数据访问层),让日志产品去实现它的抽象,只要你的日志代码依赖JCL接口就可以很方便的在Log4j和JUL之间切换,画个图展示线关系大概是这样的:
但是好景不长,随着JCL的应用,人们发现jcl带来的问题比它解决的问题还要多,适逢乱世,出来解决这个问题的应用问世了。
这个时候Slf4j应运而生。
Slf4j(simple Logging Facade for java)
还是上天提到的ceki大佬(log4j的主要贡献者),由于某些原因离开了apache,他也觉得jcl问题很多,于是在2005年自己撸了一个新东西,也是一套日志接口,也有称之为日志门面,slf4j诞生了,并且剑指jcl,并且后来也证明了,slf4j比jcl要更加优秀。
又一次感受到了大佬的能量,一个人单挑一个团队,还胜的如此漂亮。
但是由于slf4j问世比较晚,而且还只是一套接口,并不是一个可用的日志产品,现有的日志产品虽然不完美但是不用自己去实现,如jul或者log4j,所以对slf4j的推广造成了很大的阻力,这个时候ceki大佬又出现了,他键盘一敲,不就是没有实现吗,我来!
大佬一顿操作撸出了slf4j的桥接包,也就是一种适配器模式
在有了桥接包之后,日志框架关系如下图:
但是由于很多应用依赖了JCL,而没有它的桥接包,这个时候ceki:我又来啦,jcl的桥接包也来了
那好,那我们如果再考虑一下这种场景呢?假设哈,你的Java
应用使用了Spring
的第三方的框架,但是假设Spring
默认用JCL
,并且最终用的JUL
打印的日志,但是你的系统使用了Slf4j
作为日志接口,日志产品使用了Log4j
,那。。。不出意外的话。。。你将有两种日志输出,两种日志的打印方式不统一,到时候解决bug的时候就很恼火,而且配置日志的配置文件还需要两份。
这个时候是乱套了,出来处理问题英雄仍然是ceki大佬,没有什么事是桥接包解决不了的,如果有,那就再来一个
现在日志框架变成了这样。
基于log4j的确定,大佬追求完美的路又往前迈了一步,logback诞生了。
logback
logback完美实现了slf4j于是日志架构变成了下图:
现在有了两个日志接口,三个日志产品。apache可就坐不住了,新的日志产品又诞生了--log4j2.
Log4j2
2012年apache推出了新项目,log4j2,因为log4j2完全不兼容log4j1.x,而且最巧的是log4j2几乎涵盖了logback的全部新特性,log4j2也高了分离式设计,分化成log4j-api和log4j-core,这个log4j-api也是日志接口,log4j-core才是日志产品。
现在我们有了三个日志皆苦,4个日志产品,当然apache也很清楚他们的工作,他们推出了log4j2的桥接包,哈哈。
总结到这可以发现:
1.接口给了无限可能,不写接口没有任何扩展性可言
2.没有什么问题是加一层适配器解决不了的,如果有那就再加一层。
连接了日志的整个框架,在使用的时候我们有什么启发呢
1. 使用日志接口的api而不是日志产品的api,这样也符合依赖倒置原则
2. 依赖选择一个即可
3. 把日志产品依赖设置为optional和runtime scope 其中optional是为了依赖不会被传递,比如别人引用了这个jar,就会被迫使用不想使用的依赖
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>${log4j.version}</version> <optional>true</optional> </dependency>
而scope
设置为runtime
,是可以保证日志的产品的依赖只有在运行时需要,编译时不需要,这样,开发人员就不会在编写代码的过程中使用到日志产品的API
了
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>${log4j.version}</version> <scope>runtime</scope> </dependency>
ok,到这里就结束了,bye~