大家好,我是​陌溪。

相信还有小小伙伴,是用System.out.println来打日志和调试的​


今天来和大家聊聊​​Java​​​日志体系,​​Java​​日志体系可以说是五花八门,眼花缭乱。

导致很多小伙伴因为日志标准库之间复杂的关系而感到烦恼,不知道统一系统的日志标准库需要依赖哪些​​jar​​​包,百度一下所谓的博客,照着人家复制,却无法弄懂原理,甚至还有搞了半天项目因​​jar​​冲突跑不起来的,心态直接爆炸。

Java日志的心路历程_apache

咳咳,淡定淡定,别慌,阿星带你们弄懂其中的原理,只要你静下心,跟着本文来,再给个​一键三连​,你就能随心所欲的更改日志标准库,统一日志输出。

发展史

我们要正确的配置好日志,让​​jar​​相互生效,就要先理清关系,理清关系就得从它的发展史下手。

System.out和System.err

​2001​​​年以前,​​Java​​​是没有日志库的,打印日志全凭​​System.out​​​和​​System.err​​,我人都傻了,十分离谱。

Java日志的心路历程_apache_02

缺点如下:

  • 产生大量的IO操作   同时在生产环境中 无法合理的控制是否需要输出
  • 输出的内容不能保存到文件
  • 只打印在控制台,打印完就过去了,也就是说除非你一直盯着程序跑
  • 无法定制化,且日志粒度不够细

Log4j

此时名为​​Ceki​​​的巨佬站出来,说你这个不好用,我这个好用,接着在​​2001​​​年掏出了​​Log4j​​​,用起来也确实比​​System​​​系列香,​​Log4j​​一度成为业内日志标准。

Java日志的心路历程_apache_03

后来​​Log4j​​​成为​​Apache​​​项目,​​Ceki​​​也加入​​Apache​​​组织(据说​​Apache​​​还曾经建议​​Sun​​​引入​​Log4j​​​到​​Java​​​的标准库中,但​​Sun​​拒绝了)。

JUL(Java Util Logging)

原来​​Sun​​​也有自己的盘算,不就一个日志嘛,我自己也搞一个,​​2002​​​年​​2​​​月​​JDK1.4​​​发布,​​Sun​​​推出了自己的日志标准库​​JUL(Java Util Logging)​​​,其实是照着​​Log4j​​​抄的,而且还没抄好,还是在​​JDK1.5​​以后性能和可用性才有所提升。

因为在​​JUL​​​出来以前,​​Log4j​​​就已经成为一项成熟的技术,使得​​Log4j​​在选择上占据了一定的优势。

JCL(Jakarta Commons Logging)

现在市面上有两款​​Java​​​日志标准库,分别是​​Log4j​​​与​​JUL​​​,此时​​Apache​​组织十分有想法,想统一抽象日志标准接口规范(​就像JDBC​​统一数据库访问层​),让其他日志标准库去实现它的抽象接口,这样你的日志操作都是统一的接口。

Java日志的心路历程_桥接_04

于是​​JUL​​​刚出来不久,​​2002​​​年​​8​​​月​​Apache​​​推出了​​JCL(Jakarta Commons Logging)​​​,也就是日志抽象层,支持运行时动态加载日志组件的实现,当然也提供一个默认实现​​Simple Log​​(​ClassLoader​​中进行查找,如果能找到Log4j​​则默认使用log4j​​实现,如果没有则使用JUL​​ 实现,再没有则使用JCL内部提供的Simple Log​​实现​)。

Java日志的心路历程_jar_05

但是​​JUL​​有三个缺点

  1. 效率较低
  2. 容易引发混乱
  3. 使用了自定义ClassLoader的程序中,使用JCL会引发内存泄露

总之就是问题也挺多~

Slf4j(Simple Logging Facade for Java)

​2006​​​年巨佬​​Ceki​​​(​​Log4j​​​的作者)因为一些原因离开了​​Apache​​​组织,之后​​Ceki​​​觉得​​JCL​​​不好用,自己撸了一套新的日志标准接口规范​​Slf4j(Simple Logging Facade for Java)​​​,也可以称为日志门面,很明显​​Slf4j​​​是对标​​JCL​​​,后面也证明了​​Slf4j​​​比​​JCL​​更优秀。

Java日志的心路历程_apache_06

由于​​Slf4j​​​出来的较晚,光有一个接口,没有实现的日志库也很蛋疼,如​​JUL​​​和​​Log4j​​​都是没有实现​​Slf4j​​​,就算开发者想用​​Slf4j​​也用不了。

Java日志的心路历程_桥接_07

这时候巨佬​​Ceki​​​发话了,​​Sum​​​和​​Apache​​这两个组织不来实现我的接口没关系,我来实现就好了,只有魔法才能打败魔法。

后面巨佬​​Ceki​​​提供了一系列的桥接包来帮助​​Slf4j​​接口与其他日志库建立关系,这种方式称为桥接设计模式。

Java日志的心路历程_apache_08

有了桥接包配合,其他的问题都迎刃而解,我们先看看有那些问题吧~

Java日志的心路历程_桥接_09

从上图可以看出,不同时期的项目使用的日志标准库不一样,我们以​​Slf4j​​​接口作为划分线,考虑两个问题,一个是​​Slf4j​​​之前的项目怎么统一日志标准,另一个是​​Slf4j​​之后的项目怎么统一日志标准。

先来看​​Slf4j​​​之后的项目怎么统一日志标准,项目​​D、E​​​都使用​​Slf4j​​接口,首先在代码层已经统一了,如果要做到日志标准统一也十分简单,直接替换日志标准库与对应的桥接包即可,就如下图所示

Java日志的心路历程_apache_10

好家伙,​​Slf4j​​接口配合桥接包简直无敌了,灵活配置。。

再来看​​Slf4j​​​之前的项目怎么统一日志标准,项目​​A、B、C​​​都使用了不同的日志标准,所以它们的​​API​​​不一样,如果要统一标准,首先就要改动代码,这样侵入太强了,难道就没有办法在不改代码的情况下,让​​A、B、C​​项目统一日志标准吗?

办法当然有,​​Slf4j​​​接口能通过桥接包勾搭上具体的日志标准库,为什么日志标准库不能通过桥接包勾搭​​Slf4j​​接口呢?

Java日志的心路历程_jar_11

阿星想把​​A、B、C​​​项目都统一成​​Log4j​​日志输出,只需要做如下调整

Java日志的心路历程_jar_12

是不是很简单,引入​​Slf4j​​​与相关的桥接包,再引入具体的日志标准库,比如​​Log4j​​​,就完成了​​3​​个项目的统一日志标准,对代码层是零入侵。

Logback

​Ceki​​​巨佬觉得市场上的日志标准库都是间接实现​​Slf4j​​​接口,也就是说每次都需要配合桥接包,因此在​​2006​​​年,​​Ceki​​​巨佬基于​​Slf4j​​​接口撸出了​​Logback​​​日志标准库,做为​​Slf4j​​​接口的默认实现,​​Logback​​也十分给力,在功能完整度和性能上超越了所有已有的日志标准库。

目前​​Java​​日志体系关系图如下

Java日志的心路历程_jar_13

Log4j2

自从​​Logback​​​出来后,可以说​​Slf4j+Logback​​​组合如日中天,很冲击​​JCL+Log4j​​​组合,​​Apache​​​眼看有被​​Logback​​反超的势头。

Java日志的心路历程_apache_14

于​​2012​​​年,​​Apache​​​直接推出新项目​​Log4j2​​(​不兼容Log4j​​),​​Log4j2​​​全面借鉴​​Slf4j+Logback​​(​十分明显的抄袭嫌疑​)。

因为​​Log4j2​​​不仅仅具有​​Logback​​​的所有特性,还做了分离设计,分为​​log4j-api​​​和​​log4j-core​​​,​​log4j-api​​​是日志接口,​​log4j-core​​​是日志标准库,并且​​Apache​​​也为​​Log4j2​​提供了各种桥接包。。。

Java日志的心路历程_jar_15

到目前为止​​Java​​​日志体系被划分为两大阵营,分别是​​Apache​​​阵营和​​Ceki​​j阵营,如下图所示

Java日志的心路历程_apache_16

至于系统中用那套体系,各位自行选择,阿星个人偏向用​​Ceki​​​提供的​​Slf4j​​那套

Slf4j的桥接包介绍

相信大家都对桥接包都有了基本概念,这里阿星罗列下与​​Slf4j​​配合使用的桥接包

​Slf4j​​转向某个日志标准库

  • ​slf4j-jdk14.jar​
  • ​Slf4j​​​到​​JUL​​的桥梁
  • ​slf4j-log4j12.jar​
  • ​Slf4j​​​到​​Log4j​​的桥梁
  • ​log4j-slf4j-impl.jar​
  • ​Slf4j​​​到​​Log4j2​​的桥梁
  • ​slf4j-jcl.jar​
  • ​Slf4j​​​到​​JCL​​的桥梁

某个实际日志框架转向​​Slf4j​

  • ​jul-to-slf4j.jar​
  • ​JUL​​​到​​Slf4j​​的桥梁
  • ​log4j-over-slf4j.jar​
  • ​Log4j​​​到​​Slf4j​​的桥梁
  • ​jcl-over-slf4j.jar​
  • ​JCL​​​到​​Slf4j​​的桥梁

小小实践

从事​​Java​​​开发的伙伴们都清楚,​​Spring​​​框架内部使用​​JCL​​​做日志输出标准,可是项目使用​​Slf4j + Logback​​​做日志输出标准,问题来了,怎样才能让项目内的​​Spring​​保持统一日志输出标准呢?

其实非常简单,只需要引入正确的​​Slf4j​​桥接包,去除无用的日志组件即可。

Java日志的心路历程_apache_17

阿星没骗你们吧,引入​​jcl-over-slf4j.jar​​​或​​jul-to-slf4j.jar​​问题就解决了,十分简单~

小结

本文到此就结束了,以上,阿星通过​​Java​​​日志发展史一步一步的带大家理清​​Java​​日志间的关系,并抛出问题以及解决问题,相信看完后,大家不会再因为日志标准库之间复杂的关系感到烦恼,同时也能​知其所以然​的统一日志标准。

关于我

这里是阿星,一个热爱技术的Java程序猿,公众号  ​「程序猿阿星」​ 里将会定期分享操作系统、计算机网络、Java、分布式、数据库等精品原创文章,2021,与您在 Be Better 的路上共同成长!

非常感谢各位小哥哥小姐姐们能看到这里,原创不易,文章有帮助可以关注、点个赞、分享与评论,都是支持(莫要白嫖)!

Java日志的心路历程_桥接_18

愿你我都能奔赴在各自想去的路上,我们下篇文章见