本篇文章将会讲述Java常见日志框架的历史和关系,带你一窥究竟。

一、日志框架演进历史

1.1 System.out和System.err

         这应该是最早的日志记录方式吧,但是不灵活也不可配置,要么就是全部打印,要么就是全部不打印,没有一个统一的日志级别

1.2 Log4j(Log for Java)

         在1996年初,E.U.SEMPER(欧洲安全电子市场)项目决定编写自己的跟踪API,最后该API演变为Log4j,Log4j日志软件包一经推出就备受欢迎,当然这里必须要提到一个人,就是Log4j的主要贡献者,这个大佬:Ceki Gülcü。

         可能应该叫巨佬了。。。后面你就明白了,后来Log4j成为了Apache基金会项目中的一员,同时Log4j的火爆,让Log4j一度成为业内日志标杆。(据说Apache基金会还曾经建议Sun引入Log4j到java的标准库中,但是sun拒绝了)

1.3 JUL(Java Util Logging)

         果然Sun有自己的考虑,2002年2月Java1.4发布,Sun竟然推出了自己的日志库Java Util Logging,其实很多日志的实现思想也都是仿照Log4j,毕竟Log4j先出来很多年了,已经很成熟了此时,这两个日志工具打架,显然Log4j是更胜一筹

它们打架感觉就是互相竞争,Sun心里可能在想,不就是做个日志工具嘛,谁不会!当然好景不长。

1.4 JCL(Jakarta Commons Logging)

         Apache: 玩编程,谁玩的过我!你不让我成为JDK标准,我就自己成为日志标准,哼!

         于是JUL刚出来不久,2002年8月Apache又推出了日志接口Jakarta Commons Logging,也就是日志抽象层,当然也提供了一个默认实现Simple Log,这野心很大,想一统日志抽象(就像以前的JDBC一统数据库访问层),让日志产品去实现它的抽象,这样只要你的日志代码依赖的是JCL的接口,你就可以很方便的在Log4j和JUL之间做切换,当时日志领域大概是这样的结构,当然也还是方便理解的,也很优雅

Logger日志不会输出控制台java_slf4j需要什么jar包

但是好景不长,在使用过程中,虽然现在日志系统在JCL的统一下很优雅,很美好,但大家发现了JCL还不够好,有些人甚至认为JCL造成的问题比解决的问题还多。

1.5 Slf4j(Simple Logging Facade forJava)

         所以大佬粗线,Ceki Gülcü(也就是Log4j的作者)由于一些原因离开了Apache,之后觉得JCL不好,于是于2005年自己撸出一个新工程,也就是一套新日志接口(有得也叫日志门面):Slf4j(SimpleLogging Facade for Java),感觉粗来了么。。。这战争的硝烟,明显这个Slf4j是直指JCL啊,但是后面确实也证明了Slf4j是要比JCL在很多地方更优秀。

         但是由于Slf4j出来的较晚,而且还只是一个日志接口,所以之前已经出现的日志产品,如JUL和Log4j都是没有实现这个接口的,所以尴尬的是光有一个接口,没有实现的产品也是很憋屈啊,就算开发者想用Slf4j也是用不了,这时候,大佬发话了

         Ceki Gülcü:别急,我早帮你们想好了,要让Sun或者Apache这两个庞然大物来实现我的接口,太南啦,老铁,但。。。我帮你们实现,不就完了么。。。

         于是大佬Ceki Gülcü撸出了桥接包,也就是这种类似适配器模式:

Logger日志不会输出控制台java_Java_02

         好了,大佬提供了桥接包,于是日志系统现在有了这样的结构

Logger日志不会输出控制台java_slf4j需要什么jar包_03

         但是其实之前很多Java应用应该依赖的JCL,所以光有日志产品桥接包,好像还不够。

         Ceki Gülcü:没问题,不就是不够桥接包么,我写,我来证明Slf4j是最完美的

于是有了JCL的桥接包:

Logger日志不会输出控制台java_Java_04

相当于此时的桥接包就是分了两种场景

(1)之前Java应用用的日志接口(如JCL)

(2)之前Java应用用的日志产品(如Log4j)

         那好,那我们如果再考虑一下这种场景呢?

         假设哈,你的Java应用使用了Spring的第三方的框架,但是假设Spring默认用JCL,并且最终用的JUL打印的日志,但是你的系统使用了Slf4j作为日志接口,日志产品使用了Log4j,那。。。不出意外的话。。。你将有两种日志输出,两种日志的打印方式不统一,到时候解决bug的时候就很恼火,而且配置日志的配置文件还需要两份。

Logger日志不会输出控制台java_slf4j需要什么jar包_05

         所以为了方便统一应用中的所有日志,大佬发话了。

         Ceki Gülcü:没事,大家都选择用Slf4j统一吧,我来帮大家统一,没有事是桥接包解决不了的,有的话,那就再来个。

Logger日志不会输出控制台java_slf4j使用_06

         当然此时这种场景也是符合之前说的两种情况的,因此现在日志系统大体应该是这样的:

Logger日志不会输出控制台java_loggerinfo日志没出来_07

         但好景又不长,大佬毕竟是大佬,Log4j不就是自己写的么,所以最清楚Log4j缺点的人也正是他。

1.6 Logback

         由于使用Slf4j,需要一次桥接包,也就是之前的日志产品都不是正统的Slf4j的实现,因此,2006年,出自CekiGülcü之手的日志产品Logback应运而生,而且大佬还专门写了一篇文章《Reasons to prefer logback over log4j》[ http://logback.qos.ch/reasonsToSwitch.html]

         是不是这太针对了。。。哈哈哈哈,就是这么无情,当然都是他写的,他肯定是最清楚这两者实现的区别。

         肯定的,Logback是完美实现了Slf4j,于是现在日志系统变成了:

Logger日志不会输出控制台java_loggerinfo日志没出来_08

         ok了,现在咱们有了2个日志接口,3个日志产品,大家也都看起来相安无事。。。但。。。Slf4j+Logback的模式,显然很冲击JCL+Log4j,并且本身Logback确实比Log4j性能更优,设计更为合理,所以。。。老东家Apache可就坐不住了

1.7 Log4j2

         在2012年,Apache直接推出新项目,不是Log4j1.x升级,而是新项目Log4j2,因为Log4j2是完全不兼容Log4j1.x的。

         并且很微妙的,Log4j2几乎涵盖Logback所有的特性(这不是对着干是啥~而且还有抄袭的嫌疑。。。哈哈哈),更甚者的Log4j2也搞了分离的设计,分化成log4j-api和log4j-core,这个log4j-api也是日志接口,log4j-core才是日志产品。。。

         现在我们可有了3个日志接口,以及4个日志产品。。。当然Apache也知道该做啥,为了让大家可以接入自己的Log4j2,那不就是桥嘛,不就是桥嘛,Apache也麻溜的推出了它的桥接包,所以。。。

Logger日志不会输出控制台java_loggerinfo日志没出来_09

         以上日志框架,有些是为了解决现有日志框架的不足,有些是功能的扩展,有些是从头到尾重写写的,根据各自出现的先后次序,可以将它们放在同一时间线上:

Logger日志不会输出控制台java_slf4j需要什么jar包_10

二、日志相关Jar包分类

         根据上面的演进日志相关Jar包进行分类,主要分为三类:

2.1 接口类

         接口类:只提供API定义,没有提供具体实现。目的是为应用层提供标准化的使用方式,既所谓的面向接口编程。

~ SLF4J(Simple Logging Façade for Java简单日志门面)

~ JCL(Jakarta Commons Logging)

2.2 实现类

实现类:具体的日志实现类,提供对日志的收集/管理功能。受不同需求/不同历史环境影响,各框架功能上有许多不同,但遵循进化论规律。

~ Log4j(Log For Java)

~ JUL(Java Util Logging)

~ Log4j2

~ Logback

2.3 桥接类

桥接类:多种日志实现框架混用情况下,需要借助桥接类进行日志的转换,最后统一成一种进行输出。

~ slf4j-jdk14

~ slf4j-log4j12

~ log4j-slf4j-impl

~ logback-classic

~ slf4j-jcl

~ jul-to-slf4j

~ log4j-over-slf4j

~ icl-over-slf4j

~ log4j-to-slf4j

三、最佳实践

         了解了日志的发展历史,那现在我们再回过头来看看如果,你的系统在选择日志方案的时候,如何抉择呢?

(1)显然第一点是使用日志接口的API而不是直接使用日志产品的API:这一条也是必须的,也是符合依赖倒置原则的,我们应该依赖日志的抽象,而不是日志的实现

(2)日志产品的依赖只添加一个:当然也这个也是必须的,依赖多个日志产品,只会让自己的应用处理日志显得更复杂,不可统一控制。

(3)把日志产品的依赖设置为Optional和runtime scope。

         其中Optional是为了依赖不会被传递,比如别的人引用你这个jar,就会被迫使用不想用的日志依赖

         而scope设置为runtime,是可以保证日志的产品的依赖只有在运行时需要,编译时不需要,这样,开发人员就不会在编写代码的过程中使用到日志产品的API了

         日志框架怎么选?

(1)commons-loggin、slf4j 只是一种日志抽象门面,不是具体的日志框架。

log4j、logback 是具体的日志实现框架。

(2)在比较关注性能的地方,选择Logback或自己实现高性能Logging API可能更合适。推荐:slf4j + logback.

(3)在已经使用了Log4j的项目中,如果没有发现问题,继续使用可能是更合适的方式:推荐组合为:slf4j+ log4j2.

(4)如果不想有依赖则使用java.util.logging或框架容器已经提供的日志接口。