Java 日志相关框架五花八门,索性梳理下日志相关组件
一、为什么会有日志框架
为什么会有日志框架?我直接用 System.out
不行吗?
- 性能考虑:
System.out
是一个同步方法,会阻塞线程执行,高并发情况下严重影响性能;日志框架通常异步记录日志 - 日志级别:日志库允许我们根据日志的重要性设置不同的日志级别,如 DEBUG、INFO、WARN、ERROR 等。这样我们可以根据需要调整显示的日志级别,比如在开发环境中我们可能需要所有级别的日志,而在生产环境中我们可能只关心 WARN 和 ERROR 级别的日志。
- 日志持久化:使用
System.out.println
输出的日志信息,一旦程序结束,日志信息就会丢失。而日志库可以配置将日志信息输出到文件,这样就可以永久保存日志信息,方便后续查看和分析。 - 灵活性:日志库提供了丰富的配置选项,我们可以自定义日志的输出格式,输出目标(控制台、文件、数据库等),甚至可以根据日志级别选择不同的输出目标。
- 集成性:如果你的代码被其他人用作库或框架,那么使用日志库可以让最终的用户选择他们自己喜欢的日志系统。
二、日志框架分类
日志框架分为日志门面框架和日志实现框架
2.1 日志门面框架
所谓日志门面框架,就是提供了日志接口,但是没有提供具体实现,这样的好处是可以根据自己的需要更换底层日志实现,而不用变动代码,日志门面框架有两个:
- JCL:Jakarta Commons Logging 后面改名为 Apache Commons Logging,看官网从 2014 年后就再也没更新了
- Slf4j:Simple Logging Facade for Java,是目前主流的日志门面框架
2.2 日志实现框架
- Log4J:第一代日志框架,现在不咋用了
- JUL:模仿 Log4J 写的,
java.util.logging
在 jDK 中可以直接使用,无需引入依赖 - Logback:和 Slf4j 一起推出,是同一个人写的,也是目前最常用的组合即
Slf4j + Logback
- Log4j2:完全重写 Log4j1.x,和 Logback 竞争,有 Logback 全部特性,目前也有
Slf4j + Log4j2
的组合2.3 总结
- Log4j1.x 直到现在仍被许多项目使用,不过早在2015年,官方已经停止了维护。并且 Log4j1.x 最有用的特性之一在 Java 9中也无法使用。
- SLF4J/Logback 到现在仍被众多开发者使用。
- Log4j 2.x也提供了桥接包,这样开发者可以使用 JCL 或者 SLF4J 日志门面作为API,后台使用Log4j2为日志框架。
三、关于 Slf4j 的众多桥接包
3.1 桥接包关系图
3.2 使用说明
- 日志框架使用时,推荐用日志门面去调用日志实现,这样后续想使用其他日志实现,引入日志实现包和对应适配器即可
- 桥接包和适配器包的区别:桥接包是日志实现把数据传入日志门面,适配器是由日志门面把数据传给具体日志实现
- 不能同时引入同一个日志实现框架的适配器包和桥接包,会造成循环调用,桥接包是日志实现把数据传入日志门面的,适配器包是日志门面把数据传给具体日志实现的,如果同一个实现框架的话,自然就循环调用了
- 注意在使用 slf4j 日志门面之后,只能指定一个 slf4j 的适配库。在引入其他包的时候,如果有日志冲突,需要使用
<exclusion>...</exclusion>
来去掉包。 - 在 Logback 中,并没有对应的桥接器,因为是同一个作者写的,所以已经适配 SLF4J 了
3.3 无痛更换日志实现(实战)
场景:现有一个项目是使用的 JUL 实现,我们想要更换成 Logback 实现,如何做?
思路:引入 jul-to-slf4j 桥接器,就可以把日志数据传入到门面去执行,再引入 Logback 依赖,这样门面就会调用 Logback 去执行
具体操作:
添加 SLF4J 和 Logback 的依赖:首先,需要在的项目中添加 SLF4J 和 Logback 的依赖。这样,项目就可以使用 SLF4J 的 API,并且 SLF4J 的日志消息会被 Logback 处理。
1
2
3
4
5
6
7
8
9
10
11
12<!-- SLF4J的API -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<!-- Logback的核心库 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>添加 JUL-to-SLF4J 的桥接器:然后,需要添加 JUL-to-SLF4J 的桥接器的依赖。这个桥接器会将 JUL 的日志消息转发到 SLF4J。
1
2
3
4
5
6<!-- JUL到SLF4J的桥接器 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.26</version>
</dependency>在代码中安装桥接器:最后,需要在代码中安装这个桥接器。可以在主类或者Web应用的初始化代码中添加以下代码:
1
2
3// 安装JUL到SLF4J的桥接器
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
这样,项目中原本使用 JUL 进行日志记录的代码就会将日志消息发送到 SLF4J,然后 SLF4J 再将日志消息发送到 Logback 进行处理。这个过程不需要修改原有的日志记录代码,只需要添加一些配置和初始化代码即可。
以上完整代码:https://github.com/hczs/java-log-demo
参考: