1
Is logging important?
日志重要吗?
程序中的日志重要吗?
在回答这个问题前,笔者先说个事例:
Negative Example
反面事例
笔者印象尤深的就是去年某位同事,收到了客户反馈的紧急bug。尽管申请到了日志文件,但因为很多关键步骤没有打印日志,导致排查进度很慢,数个小时都没能排查到问题,也无法给出解决对策。导致客户程序一直阻断,最终产生了不少损失。
事后,经过仔细推敲,成功复现了这个bug,其实是一个很不起眼的数据转换导致的。可因为日志内容的匮乏,排查起来难度很大。
其实只要在数据转换前后进行日志输出,这个问题就是一眼的事。但可惜没如果,故事的最后,开发部门还是遭到了客户的投诉,影响到了部门绩效。
对于刚学习编程的同学,很多人都对日志满不在乎,我们在做Code Review的时候,经常发现一些新同学喜欢一个方法写得很长,然后代码中间的注释和日志都少的可怜。
坦白的说,这是很不好的习惯,这意味着日后如果方法里发生了bug,或者需要迭代时,就要花费大量时间来理清方法的思路。
千万别迷信什么“方法名、字段名起的见明知意,就可以不写注释与日志”,那是他们的业务场景不够复杂。(╯▔皿▔)╯
以笔者为例,复杂的场景涉及很多公式、奇特的规定,如果不写注释与日志,后续没人能维护得了。
所以请务必记住,日志在开发过程中非常重要。它可以帮助开发人员了解程序中发生了什么,以及在某些情况下为什么会发生错误或异常。
通过查看日志,开发人员可以轻松地定位并解决问题,并且可以更好地监控和调整应用程序的性能,在必要时进行故障排除和安全检查。

2
Log grading
日志分级
最开始的日志分级是由Syslog的开发者Eric Allman在1981年提出的。之后,这个级别分级系统被广泛应用于各种领域的日志记录和信息处理中。
下面我们就来简单介绍一下常用的日志等级。
No.1
TRACE
最低级别的日志记录,用于输出最详细的调试信息,通常用在开发调试中。
在生产环境中,应该关闭 TRACE 级别的日志记录,以避免输出过多无用信息。
No.2
DEBUG
用于输出程序中的一些调试信息,通常用于开发过程中。
像 TRACE 一样,在生产环境中应该关闭 DEBUG 级别的日志记录。
No.3
INFO
用于输出程序正常运行时的一些关键信息,比如程序的启动、运行日志等。通常在生产环境中开启 INFO 级别的日志记录。
No.4
WARN
用于输出一些警告信息,提示程序可能会出现一些异常或者错误。
在应用程序中,WARN 级别的日志记录通常用于记录一些非致命性异常信息,以便能够及时发现并处理这些问题。
No.5
ERROR
用于输出程序运行时的一些错误信息,通常表示程序出现了一些不可预料的错误。
在应用程序中,ERROR 级别的日志记录通常用于记录一些致命性的异常信息,以便能够及时发现并处理这些问题。
当然,除了这五种级别以外,还有一些日志框架定义了其他级别,例如 Python 中的 CRITICAL、PHP 中的 FATAL 等。
CRITICAL 和 FATAL 都是用于表示程序出现了致命性错误或者异常,即不可恢复的错误。
当然,对于我们今天要说的内容,知道上述五种日志等级就够了。
3
Common log plugins
常用日志插件
了解了日志的几种级别,接下来一起来看看有哪些常用的日志框架吧。
No.1
Log4j
(1999年诞生)
Log4j 是Java领域中最早的流行日志框架之一。它由Ceki Gülcü开发,并后来由Apache软件基金会接管。
Log4j 提供了灵活的配置选项、多种输出目的地、日志级别和分层日志体系。
尽管Log4j 1在其时代取得了巨大的成功,但在性能和某些功能方面存在限制,因此后来演化为Log4j 2。
No.2
SLF4J
(2004年诞生)
严格来说,SLF4J(Simple Logging Facade for Java)并不算一个插件,而是Ceki Gülcü开发的一个日志门面接口。
它为Java应用程序提供了统一的日志抽象,使开发人员可以使用一致的API进行日志记录,而不需要直接依赖于特定的日志实现。
SLF4J 可以与多种底层日志框架(如Logback、Log4j 2、java.util.logging等)结合使用。
No.3
Logback
(2009年诞生)
Logback 是Ceki Gülcü开发的日志框架,他也是Log4j的作者。
Logback 是Log4j 1的后续版本,旨在提供更高性能、更灵活的配置和现代化的日志解决方案。
Logback 支持异步日志记录、多种输出格式、灵活的配置以及与SLF4J紧密集成。
No.4
Log4j 2
(2014年诞生)
Log4j 2 是由Apache软件基金会开发的,也是Log4j 1的下一代版本。
它引入了许多新特性,如异步日志记录、插件支持、丰富的过滤器等,旨在提供更好的性能和灵活性。
Log4j 2 在设计上考虑了Log4j 1的局限性,并且支持多种配置方式。
不难注意到,一个有意思的小故事是,前三款日志插件都是 Ceki Gülcü 开发的,但 Log4j 2 并不是,虽然现在有很多人以为log4j2也是他写的,但我们在 github 上可以看到其个人说明 “Unaffiliated with log4j 2.x.” (与 log4j 2.x 无关)。
Log4j 2 和 logback 都自称是log4j 的后续版本,到底谁才算正统续作呢?这就留给各位读者体会了~
4
SLF4J
SLF4J
基于自己的使用体验,今天来详细聊聊SLF4J。
No.1
SLF4J 的诞生
在早期使用日志框架时,应用程序通常需要直接与具体的日志框架进行耦合,这就导致了以下几个问题:
代码依赖性
应用程序需要直接引用具体的日志框架,从而导致代码与日志框架强耦合,难以满足应用程序对日志框架的灵活配置。
日志框架不统一
在使用不同的日志框架时,应用程序需要根据具体的日志框架来编写代码,这不仅会增加开发难度,而且在多种日志框架中切换时需要进行大量的代码改动。
性能问题
在日志输出频繁的情况下,由于日志框架的实现方式和API设计不同,可能会导致性能问题。
为了解决这些问题,SLF4J提供了一套通用的日志门面接口,让应用程序可以通过这些接口来记录日志信息,而不需要直接引用具体的日志框架。
这样,应用程序就可以在不同的日志框架之间进行灵活配置和切换,同时还可以获得更好的性能表现。
所以,我强烈建议各位使用SLF4J, 而不是直接对接某个具体的日志框架。
No.2
SLF4J 的使用
首先,我们需要在工程内引入包,但是如果你用了springboot,各种 spring-boot-starter 启动器已经引用过了,所以引用前最好确认下:

然后在我们要打印日志的类里加上一行,即可使用,如下:
private static final Logger logger = LoggerFactory.getLogger(XXXX.class);

如果我们引用了lombok的话,也可以使用lombok的注解@Slf4j 代替上面那句话来使用 SLF4J ,如下:

但是,我们都知道SLF4J仅仅是个门面,换句话说,仅有接口而没有实现,如果此刻我们直接运行,打印日志是没有用处的:

所以,我们如果要运行,我们必须要给 SLF4J 安排上实现,而目前最常用的就是 logback 和 log4j2 了,对于这两个网上已经有很多的介绍,有兴趣的同学可以去网上找一下相关的资料查阅。
5
Finally, let's talk about it
最后聊聊
看完本文,你现在大概对这几个常用框架有了一定的了解,并能基础应用了吧。此次我们没有讲源码,也没有深入的讲其配置及进阶使用,这些我们会在后面慢慢学习。但现在我希望你能知道的是:
一定要写好日志!
一定要写好日志!!
一定要写好日志!!!
重要的事情说三遍!这是区别新人和老鸟的一个重要依据,也是让自己排查问题更轻松的不二法门!
另外,现在很多中间件都自己引用了日志插件,我们作为一个整体工程在使用中间件时,要及时发现并解决插件冲突,避免我们自己的日志配置失效,这也是一个程序员该注意的点。