日志相关框架(扫盲)

常用日志框架

Java开发的同学对log4j、log4j2、Logging、、logback、commons-logging、slf4j等等,对这几个日志相关的技术不陌生吧,为什么有这么多日志技术,它们都是什么区别和联系呢?相信大多数人搞不清楚它们的关系,下面我将一一介绍一下,以后大家再也不用傻傻分不清楚了。

Java日志体系发展历程

在实际开发过程中,Java的日志框架体系之所以会给编程人员带来一种混乱的感觉,根本原因是Java日志框架没有统一的使用标准。

  1. Log4,在最初的上古时代,程序员们并没有现成的日志框架,Java开发者们只能使用原始的System.out.println(), System.err.println()或者e.printStackTrace()。通过把debug日志写到StdOut流,错误日志写到ErrOut流,来实现应用程序运行状态的记录。这样有一个很大的问题,那就是无法定制化,并且日志的输出粒度不够细。于是1999年,Ceki Gülcü创建了Log4j项目,并几乎成为了Java日志框架的实际标准。
  2. JUL,Log4j作为Apache基金会的一员,Apache希望将Log4j引入jdk,不过被sun公司拒绝了。随后,sun模仿Log4j,在jdk1.4中实现了JUL即java.util.logging。Java Logging 的介绍可以看官方文档
  3. JCL,后来为了实现将日志接口与实现解耦,Apache推出了JCL即Apache Commons Logging。JCL只定义了一套日志接口,具体实现由Log4j或JUL来完成。JCL基于动态绑定来实现日志的记录,在使用时只需要用JCL定义的接口来编写代码即可,程序真正运行时会检查classpath中的具体实现,因此可以自由选择是有log4j还是JUL来实现日志功能。 commons-logging 就是日志的门面接口,它也是 apache 最早提供的日志门面接口,用户可以根据喜好选择不同的日志实现框架,而不必改动日志定义,这就是日志门面的好处,符合面对接口抽象编程。commons-logging官网地址
  4. Slf4j&Logback,后来,Java日志大佬Ceki Gülcü离开了Apache自己单干,先后创建了Slf4j和Logback两个项目。Slf4j是一个日志门面,只提供接口,可以支持Logback、JUL、log4j等实现;Logback作为大佬自己提供的具体实现,相较于log4j有更快的执行速度和更完善的功能。Slf4j 的官方地址Logback 的官方地址
  5. log4j2,最后,Apache为了避免Slf4j和Logback,慢慢取代JCL和Log4j ,推出了自己的反击之作log4j2。log4j2不兼容log4j,性能获得了很大的提升。于是,我们就看到了如此多的日志框架并存的局面。Log4j2 的官方地址

日志门面、桥接器与日志实现

门面模式:

比直接使用具体日志框架更合理的选择是使用日志门面接口日志门面接口提供了一套独立于具体日志框架实现的API,应用程序通过使用这些独立的API就能够实现与具体日志框架的解耦,这跟JDBC是类似的。最早的日志门面接口是commons-logging,但目前最受欢迎的是SLF4J

SLF4J(Simple logging Facade for Java)

意思为简单日志门面,它是把不同的日志系统的实现进行了具体的抽象化,只提供了统一的日志使用接口,使用时只需要按照其提供的接口方法进行调用即可,由于它只是一个接口,并不是一个具体的可以直接单独使用的日志框架,所以最终日志的格式、记录级别、输出方式等都要通过接口绑定的具体的日志系统来实现,这些具体的日志系统就有log4j2、log4j、logback、java.util.logging等,它们才实现了具体的日志系统的功能。日志门面在使用时,可以动态或者静态的指定具体的日志框架实现,实现了接口和实现的解耦,使得用户可以灵活的选择日志的具体实现框架。另一种就是具体的日志系统,它提供了具体的日志接口实现,应用程序通过具体实现执行日志打印的功能。


那为什么要将日志的体系搞这么复杂呢,简单一个实现不就好了吗?为什么分成门面与实现?要解答这个问题,需要回顾一下门面模式。门面模式,其核心为:外部与一个子系统的通信,必须通过一个统一的外观对象进行,使得子系统更易于使用

 

现在,我们可以回到最初的问题了,日志体系为什么要使用SLF4J作为门面呢?

原因:使用slf4j后有利于根据自己实际的需求更换具体的日志系统,比如,之前使用的具体的日志系统为log4j,想更换为logback时,只需要删除log4j相关的jar,然后加入logback相关的jar和日志配置文件即可,而不需要改动具体的日志输出方法,试想如果没有采用这种方式,当你的系统中日志输出有成千上万条时,你要更换日志系统将是多么庞大的一项工程。如果你开发的是一个面向公众使用的组件或公共服务模块,那么一定要使用slf4j的这种形式,这有利于别人在调用你的模块时保持和他系统中使用统一的日志输出

sl4j与具体日志框架的结合

Java日志体系当前大体可以分为三个部分:日志门面接口桥接器日志框架具体实现。门面接口与日志实现上面已经介绍过了。

那么什么是桥接器呢?所谓“桥接器”,不过就是对某套API的伪实现,这种实现并不是直接去完成API所声明的功能,而是去调用有类似功能的别的API。这样就完成了从“某套API”到“别的API”的转调。如果同时存在A-to-B.jar和B-to-A.jar这两个桥接器,那么可以想象当应用程序开始调用A或者B的API时,会发生什么事。这就是最开始引出的那个stack overflow异常的基本原理,递归调用太深导致栈溢出,问题源头指向日志接口

下图来自SL4J官网文档:

可以看到slf4j与具体日志框架结合的方案有很多种。当然,每种方案的最上层(绿色的应用层)都是统一的,它们向下都是直接调用slf4j提供的API(浅蓝色的抽象API层),依赖slf4j-api.jar。然后slf4j API向下再怎么做就非常自由了,几乎可以使用所有的具体日志框架。注意图中的第二层是浅蓝色的,看左下角的图例可知这代表抽象日志API,也就是说它们不是具体实现。如果像左边第一种方案那样下层没有跟任何具体日志框架实现相结合,那么日志是无法输出来的( 这里不确定是否可能会默认输出到标准输出 )。

图中第三层明显就不如第一、二层那么整齐划一了,因为这里已经开始涉及到了具体的日志框架。

首先看第三层中间的两个湖蓝色块,这是适配层,也就是桥接器。左边的slf4j-log4j2.jar桥接器看名字就知道是slf4j到log4j的桥接器,同样,右边的slf4j-jdk14.jar就是slf4j到Java原生日志实现的桥接器了。它们的下一层分别是对应的日志框架实现,log4j的实现代码是log4j.jar,而jul实现代码已经包含在了JVM runtime中,不需要单独的jar包。

再看第三层其余的三个深蓝色块。它们三个也是具体的日志框架实现,但是却不需要桥接器,因为它们本身就已经直接实现了slf4j API。slf4j-simple.jar和slf4j-nop.jar这两个不用多说,看名字就知道一个是slf4j的简单实现,一个是slf4j的空实现,平时用处也不大。而logback之所以也实现了slf4j API,是因为logback和slf4j出自同一人之手。

第三层所有的灰色jar包都带有红框,这表示它们都直接实现了slf4j API,只是湖蓝色的桥接器对slf4j API的实现并不是直接输出日志,而是转去调用别的日志框架声明的API。


日志框架总结

commons-logging、slf4j 只是一种日志抽象门面,不是具体的日志框架。 log4j、logback等是具体的日志实现框架。 一般首选强烈推荐使用 slf4j + logback。当然也可以使用slf4j + log4j、commons-logging + log4j 这两种日志组合框架。

从上图可以看出 slf4j 很强大吧,不但能和各种日志框架对接,还能和日志门面 commons-logging 进行融合。

日志级别详解

日志的输出都是分级别的,不同的设置不同的场合打印不同的日志。下面拿最普遍用的 Log4j 日志框架来做个日志级别的说明,这个也比较奇全,其他的日志框架也都大同小异。

Log4j 的级别类 org.apache.log4j.Level 里面定义了日志级别,日志输出优先级由高到底分别为以下8种。

日志级别 描述
OFF 关闭:最高级别,不输出日志。
FATAL 致命:输出非常严重的可能会导致应用程序终止的错误。
ERROR 错误:输出错误,但应用还能继续运行。
WARN 警告:输出可能潜在的危险状况。
INFO 信息:输出应用运行过程的详细信息。
DEBUG 调试:输出更细致的对调试应用有用的信息。
TRACE 跟踪:输出更细致的程序运行轨迹。
ALL 所有:输出所有级别信息。

所以,日志优先级别标准顺序为

ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF

如果日志设置为 L ,一个级别为 P 的输出日志只有当 P >= L 时日志才会输出。

即如果日志级别 L 设置 INFO,只有 P 的输出级别为 INFO、WARN,后面的日志才会正常输出。

猜你喜欢

转载自blog.csdn.net/CoderTnT/article/details/121306258