这不是前两天 Log4j2 出事儿了嘛,那借着这个风,咱顺道盘一盘 Java 的日志系统。
我周末在网上看到一篇文章, 是从历史的角度来看 Java 的日志为啥会发展成现在这个样子。
在此分享给大家,我相信看完之后,你就能从头到尾的明白,原来现在的复杂一切都是有缘由的。
作者:imango https://segmentfault.com/a/1190000021121882
前言最早开始撸码当时就遇到几次日志 jar 包冲突的问题,当时也是很烦躁,毕竟了解的也不多,什么那里 4j ,这里 4j ,还有什么桥接包,而且在我感觉他们的包名都还差不多!!我当时是比较懵逼的,上网搜了下,随便看到一个类似这种桥接过去桥接过来的图我就懵,都是些啥玩意儿
最近项目中突然发现我们自己的工程对于日志的记录也不是很好,很多各种日志的注解大家用的也不一样,有 @Log4j ,也有 @Slf4j 的,也有 @Log4j2 的,索性干脆彻底去了解一下日志系统
由于个人一直觉得现在学习技术是很好的年代,毕竟我们在前人已经踩过的坑,总结过的经验之上来学习,相当于站在了巨人的肩膀上,但同时也是最坏的时代,我们对于新技术可能还是很了解,但是这些新技术到底为什么出现,它们到底解决了什么问题,时代选择了这些技术一定是有原因的,我们不清楚技术发展的历史,就像国外不清楚我们中华民族的历史,所以会曲解我们一样
言归正传,虽然看着日志系统很乱,很难梳理清楚,可能名字都不是很好记,但我们要是从日志系统本身的发展历史去了解一次,可能就会更加明白为什么会出现现在这种情况了
发展历史 System.out和System.err这应该是最早的日志记录方式吧,但是不灵活也不可配置,要么就是全部打印,要么就是全部不打印,没有一个统一的日志级别
Log4j在1996年初, E.U.SEMPER (欧洲安全电子市场)项目决定编写自己的跟踪 API ,最后该 API演变为 Log4j , Log4j 日志软件包一经推出就备受欢迎,当然这里必须要提到一个人,就是 Log4j 的主要贡献者,这个大佬:
Ceki Gülcü
可能应该叫巨佬了。。。后面你就明白了,后来 Log4j 成为了 Apache 基金会项目中的一员,同时 Log4j 的火爆,让 Log4j 一度成为业内日志标杆。(据说 Apache 基金会还曾经建议 Sun引入 Log4j 到 java 的标准库中,但是 sun 拒绝了)
JUL(Java Util Logging)果然 Sun 有自己的考虑,2002年2月 Java1.4 发布, Sun 竟然推出了自己的日志库 Java Util Logging ,其实很多日志的实现思想也都是仿照 Log4j ,毕竟 Log4j 先出来很多年了,已经很成熟了此时,这两个日志工具打架,显然 Log4j 是更胜一筹
它们打架感觉就是互相竞争, Sun 心里可能在想,不就是做个日志工具嘛,谁不会!当然好景不长
JCL(Jakarta Commons Logging)Apache : 玩编程,谁玩的过我!你不让我成为 JDK 标准,我就自己成为日志标准,哼!(完全个人意淫。。。) 于是 JUL 刚出来不久,2002年8月 Apache 又推出了日志接口 Jakarta Commons Logging ,也就是日志抽象层,当然也提供了一个默认实现 Simple Log ,这野心很大,想一统日志抽象(就像以前的 JDBC 一统数据库访问层),让日志产品去实现它的抽象,这样只要你的日志代码依赖的是 JCL 的接口,你就可以很方便的在 Log4j 和 JUL 之间做切换,当时日志领域大概是这样的结构,当然也还是方便理解的,也很优雅
但是好景不长,在使用过程中,虽然现在日志系统在 JCL 的统一下很优雅,很美好,但大家发现了 JCL 还不够好,有些人甚至认为 JCL 造成的问题比解决的问题还多...emmm
Slf4j(Simple Logging Facade for Java)所以大佬粗线, Ceki Gülcü (也就是 Log4j 的作者)由于一些原因离开了 Apache ,之后觉得JCL 不好,于是于2005年自己撸出一个新工程,也就是一套新日志接口(有得也叫日志门面):Slf4j(Simple Logging Facade for Java) ,感觉粗来了么。。。这战争的硝烟,明显这个 Slf4j 是直指 JCL 啊,但是后面确实也证明了 Slf4j 是要比 JCL 在很多地方更优秀
Ceki Gülcü :玩接口,我一个人就是一支军队!
但是由于 Slf4j 出来的较晚,而且还只是一个日志接口,所以之前已经出现的日志产品,如 JUL 和 Log4j 都是没有实现这个接口的,所以尴尬的是光有一个接口,没有实现的产品也是很憋屈啊,就算开发者想用 Slf4j 也是用不了,这时候,大佬发话了 Ceki Gülcü :别急,我早帮你们想好了,要让 Sun 或者 Apache 这两个庞然大物来实现我的接口,太南啦,老铁,但。。。我帮你们实现,不就完了么。。。
于是大佬 Ceki Gülcü 撸出了之前提到的桥接包,也就是这种类似适配器模式
好了,大佬提供了桥接包,于是日志系统现在有了这样的结构
但是其实之前很多 Java 应用应该依赖的 JCL ,所以光有日志产品桥接包,好像还不够 Ceki Gülcü :没问题,不就是不够桥接包么,我写,我来证明Slf4j是最完美的 于是有了 JCL 的桥接包
相当于此时的桥接包就是分了两种场景
-
之前 Java 应用用的日志接口(如 JCL )
-
之前 Java 应用用的日志产品(如 Log4j )
那好,那我们如果再考虑一下这种场景呢?假设哈,你的 Java 应用使用了 Spring 的第三方的框架,但是假设 Spring 默认用 JCL ,并且最终用的 JUL 打印的日志,但是你的系统使用了Slf4j 作为日志接口,日志产品使用了 Log4j ,那。。。不出意外的话。。。你将有两种日志输出,两种日志的打印方式不统一,到时候解决bug的时候就很恼火,而且配置日志的配置文件还需要两份。
所以为了方便统一应用中的所有日志,大佬发话了 Ceki Gülcü :没事,大家都选择用Slf4j统一吧,我来帮大家统一,没有事是桥接包解决不了的,有的话,那就再来个
当然此时这种场景也是符合之前说的两种情况的,因此现在日志系统大体应该是这样的
总结一句话就是:大佬撸完。。。
但好景又不长,大佬毕竟是大佬, Log4j 不就是自己写的么,所以最清楚 Log4j 缺点的人也正是他
Logback由于使用 Slf4j ,需要一次桥接包,也就是之前的日志产品都不是正统的 Slf4j 的实现,因此,2006年,出自 Ceki Gülcü 之手的日志产品 Logback 应运而生,而且大佬还专门写了一篇文章
是不是这太针对了。。。哈哈哈哈,就是这么无情,当然都是他写的,他肯定是最清楚这两者实现的区别
肯定的, Logback 是完美实现了 Slf4j ,于是现在日志系统变成了
ok了,现在咱们有了2个日志接口,3个日志产品,大家也都看起来相安无事。。。但。。。 Slf4j+Logback 的模式,显然很冲击 JCL+Log4j ,并且本身 Logback 确实比 Log4j 性能更优,设计更为合理,所以。。。老东家 Apache 可就坐不住了
Log4j2在2012年, Apache 直接推出新项目,不是 Log4j1.x 升级,而是新项目 Log4j2 ,因为 Log4j2 是完全不兼容 Log4j1.x 的
并且很微妙的, Log4j2 几乎涵盖 Logback 所有的特性(这不是对着干是啥~而且还有抄袭的嫌疑。。。哈哈哈),更甚者的 Log4j2 也搞了分离的设计,分化成 log4j-api 和 log4j-core,这个 log4j-api 也是日志接口, log4j-core 才是日志产品。。。
emmm,我看到这,我都有点崩溃了
现在我们可有了3个日志接口,以及4个日志产品。。。当然 Apache 也知道该做啥,为了让大家可以接入自己的 Log4j2 ,那不就是桥嘛,不就是桥嘛, Apache 也麻溜的推出了它的桥接包,所以。。。唉,所以我都不敢画图了。。。
前方高能!!! 前方高能!!! 前方高能!!!总结到这,我们可以吸取什么经验么?
-
不写接口的坏处
-
没有什么问题是加一个层适配器(接口)解决不了的,如果有,那就再加一层
了解了日志的发展历史,那现在我们再回过头来看看如果,你的系统在选择日志方案的时候,如何抉择呢?毕竟我们3个日志接口,以及4个日志产品
-
显然第一点是使用日志接口的 API 而不是直接使用日志产品的 API 这一条也是必须的,也是符合依赖倒置原则的,我们应该依赖日志的抽象,而不是日志的实现
-
日志产品的依赖只添加一个 当然也这个也是必须的,依赖多个日志产品,只会让自己的应用处理日志显得更复杂,不可统一控制
-
Optional runtime scope Optional jar
org.apache.logging.log4j log4j-core${log4j.version} true
而 scope 设置为 runtime ,是可以保证日志的产品的依赖只有在运行时需要,编译时不需要,这样,开发人员就不会在编写代码的过程中使用到日志产品的 API 了
org.apache.logging.log4j log4j-slf4j-impl${log4j.version} runtime
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)