文章

一起学Java(9)-[日志篇]教你掌握Java日志框架的演进和设计逻辑

在项目开发中,日志记录(Logging)扮演着至关重要的角色。日志是开发人员调试代码和诊断问题的重要工具。例如:在生产环境中,开发人员通常无法直接调试代码,日志提供了一个详细的执行轨迹,帮助开发人员重现问题。因此,在进入其他框架和代码研究前,我首先想到的就是先完成项目日志框架的引入,进一步夯实基础工作。

要完成Log框架的引入,首先要弄清楚有哪些框架可选以及框架之间的关系逻辑,进而完成框架对比和选型。这也是本文研究的重点。

一、Java主要日志框架演进

学Java,用日志,还是应该简单熟悉下Java日志框架的演进历史,不能做“闭眼使用族”:

1. Log4j

在Java早期,开发者缺乏标准化的日志记录工具。应用程序需要记录运行时的信息以便于调试和监控。1999年左右,Ceki Gülcü创建了Log4j,提供了一种灵活、可配置的日志记录方式,解决了这一需求。Log4j 1.x版本提供了多种日志级别(如DEBUG、INFO、WARN、ERROR)、灵活的配置文件(如XML)、以及多种输出方式(如控制台、文件)。Log4j迅速流行,被广泛应用于各种Java项目中,成为事实上的标准日志框架,促使许多开发者开始重视和采用日志记录机制。

2. java.util.logging (JUL)

2002年,Java开发团队在JDK 1.4中引入了JUL。作为Java标准库的一部分,JUL无需额外依赖,提供了基本的日志记录功能。JUL成为小型项目的默认选择,但其功能相对简单,未能完全满足复杂项目的需求。它的存在也激发了对更强大日志框架的需求。

3. Apache Commons Logging (JCL)

随着Java项目的复杂性增加,不同项目使用不同的日志框架带来了管理和切换上的困难。也是在2002年,JCL发布。JCL提供了一个通用的日志接口,可以与多种日志实现集成,简化了日志框架的切换和管理。JCL可以通过简单的配置自动选择和切换底层日志框架(如Log4j、JUL等),提供了更大的灵活性。JCL在一段时间内被广泛采用,但其动态查找和配置机制在性能和复杂性方面带来了一些问题,导致部分开发者寻找更高效的替代方案。

4. SLF4J (Simple Logging Facade for Java)

Ceki Gülcü在开发Log4j后,也意识到需要一个统一的日志接口,以便在不修改代码的情况下切换不同的日志实现,上文提到JCL虽然提供了这一特性,但却带来了更多的问题。2005年左右,SLF4J应运而生,提供了这一功能。SLF4J提供了一个简单、统一的日志接口,支持多种日志实现(如Logback、Log4j 2、JUL等),便于项目在不同日志框架之间切换。SLF4J迅速被广泛采用,成为Java项目中最常用的日志门面(看名字也猜到他用的是门面模式,Facade),促进了日志框架的标准化和灵活性。

但是由于SLF4J问世比较晚,而且还只是一套接口,并不是一个可用的日志产品,现有的日志产品虽然不完美但是不用自己去实现,所以对slf4j的推广造成了很大的阻力。于是Ceki Gülcü大佬又写出了SLF4J的各种桥接包,也就是一种适配器模式来完成与各日志框架之间的适配,实现可通过SLF4J层直接使用其他日志实现的效果。这也是你可能觉得为什么用一个日志框架要引入那么多包的原因,这个后续我们再仔细研究。

其实SLF4J除提供了通用接口层外,也提供了日志层的简单事先,即slf4j-simple包。

5. Logback

Ceki Gülcü在开发Log4j后,意识到Log4j 1.x存在一些性能和设计上的局限性,于是于2006年创建了Logback,作为Log4j的继任者。Logback从一开始就设计为高性能和灵活性,特别是与SLF4J的紧密集成(无需桥接包),使得它成为很多Java项目(如大名鼎鼎的Spring)的首选日志框架。

6. Log4j 2

2014年,为了应对Log4j 1.x的性能、安全和扩展性问题,Apache开发了Log4j 2Log4j 2引入了异步日志、插件架构、支持多种配置格式(XML、JSON、YAML)以及强大的过滤和路由功能。

Log4j2 的核心库由两个主要部分组成:log4j-api 和 log4j-core。这两个部分通过分离职责来实现灵活性和模块化设计。

  • log4j-api 是 Log4j2 提供的公共 API,它定义了应用程序与日志框架交互的接口。开发者使用 log4j-api 来记录日志,而不需要直接依赖具体的日志实现。
  • log4j-core 是 Log4j2 的核心实现部分,它包含了具体的日志实现逻辑,包括日志的配置、处理、输出等。

通过 API 与实现的分离,应用程序可以独立于具体的日志实现。这使得开发者可以在不修改应用代码的情况下切换不同的日志实现(如从 Log4j2 切换到其他日志框架)。这也是Java日志框架领域整体的设计思想。

总结概括

  1. Log4j的成功促使了对日志框架标准化和灵活性的需求,导致了JCL和SLF4J的出现。
  2. JCL的复杂性问题推动了SLF4J的发展,提供了更简洁和高效的解决方案。
  3. Log4j 1.x的局限性性能需求导致了Logback的创建,Logback进一步与SLF4J集成,提供了更好的性能和灵活性。
  4. Log4j 1.x的安全和性能问题促使了Log4j 2的开发,引入了异步日志和插件化架构,提升了性能和扩展性。

二、Java日志框架设计思想

根据上面的介绍,我们已经大概摸出了Java日志框架的设计思想:即接口层和实现层分离的思想。这也是一般软件设计中比较典型的解耦设计,如图所示:

Java日志框架设计逻辑

这样设计实现了接口层和实现层的分离,你开发的应用程序调用通用的接口层,可以在不修改代码的情况下,实现日志实现产品的替换,低耦合、高灵活。因此上面介绍的框架就可以分成两类。

  • 日志接口层框架:SLF4JJCL、Log4j2中的log4j-api
  • 日志实现层框架:Log4jslf4j-simpleLogbackJULLog4j 2.x

如果你足够细心,你可能会发现像SLF4J这样的接口层框架反而是晚于一些实现层框架出现的,从逻辑上自然不可能直接和实现层无缝衔接。于是日志框架的桥接层就出现了(也就是桥接模式)。框架逻辑变成了如下的样子:

java-log-bridge

这样,我们的思路也清晰了,接口层目前主流的框架无疑是SLF4J,实现层是LogbackLog4j 2两大框架分庭抗礼。桥阶层则是根据接口和实现层选择合适的“桥”,基本不存在选型一说。后续,我们计划按照这个思路分别从浅入深研究一下这些框架如何集成使用、集成原理以及一些功能特性的差异和组件开发方式。

喝酒走肾,写码走心。

本文由作者按照 CC BY 4.0 进行授权