007_日志体系

在系统运行的时候,不可避免的会出现问题,这个时候就需要进行排查,而排查的最简单的方式就是查看日志。一些关键位置的日志输出会给开发者排查问题带来巨大的方便。而我们java的日志体系可以说非常复杂。这里我们就研究下java的日志体系。这里先把日志的关系拉出来,你就有很大的感触了。

这都什么玩意儿!!

日志的基本用法

我自己项目内使用的是lombook + slf4j + log4j的组合:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.10</version>
</dependency>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.12.1</version>
</dependency>

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

有了依赖之后在类顶部标记@slf4j即可产生日志对象:

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class LogTest {
    public static void main(String[] args) {
        log.info("打印info日志");
        log.debug("打印debug日志");
        log.warn("打印warn日志");
        log.error("打印error日志");
    }
}

其中四种打印日志的方法需要注意其使用场景:

  • ERROR:当程序发生不可预期的错误(按照设计不应该发生)时打印。或者不可恢复的错误(启动时初始化失败,直接退出)时打印。
  • WARN:当程序发生可预期或者不影响主要功能的时候时使用。
  • INFO:用于追踪程序的正常执行。典型的在 Web 模块中,任何一处 INFO 日志的输出在一次 Web Request 中被调用的次数应该是 “常量级” 别的,且大部分情况下这个常量应该是 1。
  • DEBUG:调试日志,DEBUG 日志打印的量没有限制,但需要注意不要影响执行性能。

发展历史

为了更好理解我们的日志体系结构,先看看历史上日志框架是怎么演变过来的

System.out和System.err

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

Log4j

在1996年初,E.U.SEMPER(欧洲安全电子市场)项目决定编写自己的跟踪API,最后该API演变为Log4jLog4j日志软件包一经推出就备受欢迎,当然这里必须要提到一个人,就是Log4j的主要贡献者,这个大佬:Ceki Gülcü。后来Log4j成为了Apache基金会项目中的一员,同时Log4j的火爆,让Log4j一度成为业内日志标杆。(据说Apache基金会还曾经建议Sun引入Log4jjava的标准库中,但是sun拒绝了)

JUL

果然Sun有自己的考虑,2002年2月Java1.4发布,Sun竟然推出了自己的日志库Java Util Logging(缩写是JUL)。很多日志的实现思想也都是仿照Log4j,毕竟Log4j先出来很多年了,已经很成熟了此时,这两个日志工具打架,显然Log4j是更胜一筹
它们打架感觉就是互相竞争,Sun心里可能在想,不就是做个日志工具嘛,谁不会!

JCL(Jakarta Commons Logging)

于是JUL刚出来不久,2002年8月Apache又推出了日志接口Jakarta Commons Logging,也就是日志抽象层,当然也提供了一个默认实现Simple Log,这野心很大,想一统日志抽象(就像以前的JDBC一统数据库访问层),让日志产品去实现它的抽象,这样只要你的日志代码依赖的是JCL的接口,你就可以很方便的在Log4jJUL之间做切换,当时日志领域大概是这样的结构,当然也还是方便理解的,也很优雅
image.png
但是好景不长,在使用过程中,虽然现在日志系统在JCL的统一下很优雅,很美好,但大家发现了JCL还不够好,有些人甚至认为JCL造成的问题比解决的问题还多。

Slf4j(Simple Logging Facade for Java)

大佬Ceki Gülcü(也就是Log4j的作者)由于一些原因离开了Apache,之后觉得JCL不好,于是于2005年自己撸出一个新工程,也就是一套新日志接口(有得也叫日志门面):Slf4j(Simple Logging Facade for Java),明显这个Slf4j是直指JCL,后面确实也证明了Slf4j是要比JCL在很多地方更优秀
但是由于Slf4j出来的较晚,而且还只是一个日志接口,所以之前已经出现的日志产品,如JULLog4j都是没有实现这个接口的,所以尴尬的是光有一个接口,没有实现的产品也是很憋屈啊,就算开发者想用Slf4j也是用不了。这时候,大佬Ceki Gülcü发话了:别急,我早帮你们想好了,要让Sun或者Apache这两个庞然大物来实现我的接口太难了,我直接帮你们实现!于是大佬Ceki Gülcü撸出了之前提到的桥接包,也就是这种类似适配器模式。大佬提供了桥接包,于是日志系统现在有了这样的结构
image.png
但是其实之前很多Java应用应该依赖的JCL,所以光有日志产品桥接包,好像还不够Ceki Gülcü:没问题,不就是不够桥接包么,我写,我来证明Slf4j是最完美的 于是有了JCL的桥接包image.png
相当于此时的桥接包就是分了两种场景

  1. 之前Java应用用的日志接口(如JCL
  2. 之前Java应用用的日志产品(如Log4j

那好,那我们如果再考虑一下这种场景呢?假设你的Java应用使用了Spring的第三方的框架,但是假设Spring默认用JCL,并且最终用的JUL打印的日志,但是你的系统使用了Slf4j作为日志接口,日志产品使用了Log4j,那你将有两种日志输出,两种日志的打印方式不统一,到时候解决bug的时候就很恼火,而且配置日志的配置文件还需要两份。
image.png
所以为了方便统一应用中的所有日志,大佬发话了Ceki Gülcü:没事,大家都选择用Slf4j统一吧,我来帮大家统一,没有事是桥接包解决不了的,有的话,那就再来个
image.png
当然此时这种场景也是符合之前说的两种情况的,因此现在日志系统大体应该是这样的
image.png
但好景又不长,大佬毕竟是大佬,Log4j不就是自己写的么,所以最清楚Log4j缺点的人也正是他

Logback

由于使用Slf4j,需要一次桥接包,也就是之前的日志产品都不是正统的Slf4j的实现,因此,2006年,出自Ceki Gülcü之手的日志产品Logback应运而生,Logback是完美实现了Slf4j,于是现在日志系统变成了
image.png
ok了,现在咱们有了2个日志接口,3个日志产品,大家也都看起来相安无事。但Slf4j+Logback的模式,显然很冲击JCL+Log4j,并且本身Logback确实比Log4j性能更优,设计更为合理,所以Apache看不下去了

Log4j2

在2012年,Apache直接推出新项目,不是Log4j1.x升级,而是新项目Log4j2,因为Log4j2是完全不兼容Log4j1.x的。Log4j2几乎涵盖Logback所有的特性,更甚者的Log4j2也搞了分离的设计,分化成log4j-apilog4j-core,这个log4j-api也是日志接口,log4j-core是日志产品。
现在我们可有了3个日志接口,以及4个日志产品了。为了让大家可以接入自己的Log4j2Apache也推出了它的桥接包。
image.png
已经没眼看了。

最佳实践

了解了日志的发展历史,那现在我们再回过头来看看如果,你的系统在选择日志方案的时候,如何抉择呢?毕竟我们3个日志接口,以及4个日志产品

  • 显然第一点是使用日志接口的API而不是直接使用日志产品的API这一条也是必须的,也是符合依赖倒置原则的,我们应该依赖日志的抽象,而不是日志的实现
  • 日志产品的依赖只添加一个 当然也这个也是必须的,依赖多个日志产品,只会让自己的应用处理日志显得更复杂,不可统一控制
  • 把日志产品的依赖设置为Optionalruntime scope其中Optional是为了依赖不会被传递,比如别的人引用你这个jar,就会被迫使用不想用的日志依赖而scope设置为runtime,是可以保证日志的产品的依赖只有在运行时需要,编译时不需要,这样,开发人员就不会在编写代码的过程中使用到日志产品的API
  • 9
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值