3. 日志框架选型:Log4j、Logback、Log4j2、SLF4J 对比
日志框架这东西,选对了省心三年,选错了……嗯,我见过太多项目因为日志框架没选好,后期改得头皮发麻。今天咱们就把 Java 生态里这几个主流框架掰开揉碎聊清楚。
3.1 先搞清楚:门面 vs 实现
很多新手上来就问「SLF4J 和 Logback 哪个好?」——这问题本身就有问题。SLF4J 是门面,Logback 是实现,它俩压根不是竞争关系。
打个比方:SLF4J 就像墙上的插座接口,Log4j2、Logback 这些是插头。你只要认准插座接口,插头随便换。我早期带团队时,有个项目直接硬编码 Log4j 的 API,后来想切 Logback,改了上千处代码……从那以后,我所有项目强制上 SLF4J。
3.2 四大框架的恩怨史
咱们按时间线捋一遍,你就明白为什么现在推荐 Log4j2 了。
| 框架 | 诞生时间 | 定位 | 现状 |
|---|---|---|---|
| Log4j 1.x | 2001年 | 最早的 Java 日志实现 | 2015年已终止维护,别用了 |
| Logback | 2006年 | Log4j 1.x 的替代品 | 稳定,Spring Boot 默认 |
| Log4j 2.x | 2014年 | Log4j 1.x 的重写版 | 性能最强,推荐新项目使用 |
| SLF4J | 2005年 | 日志门面(接口) | 事实标准,必须用 |
说白了,Log4j 1.x 已经进坟墓了,别挖出来用。Logback 是 Log4j 1.x 的作者 Ceki Gülcü 后来写的,继承了经验但彻底重写了架构。Log4j 2 是 Apache 社区另起炉灶,吸收了 Logback 的优点,又做了大量优化。
3.3 性能对比:别被数字忽悠
我见过不少文章贴性能对比图,说 Log4j2 比 Logback 快 10 倍。这话对,但也不全对。咱们看几个关键指标:
- 异步日志:Log4j2 的异步日志器用了无锁队列(LMAX Disruptor),吞吐量确实碾压 Logback。我在一个日处理 10 亿条日志的系统中实测,Log4j2 异步模式比 Logback 快 3-5 倍。
- 零 GC:Log4j2 在日志字符串拼接上做了内存复用,GC 压力小很多。高并发场景下,这个优势很明显。
- 热加载:Log4j2 支持配置文件热加载,改日志级别不用重启。Logback 也支持,但 Log4j2 的响应更快。
3.4 配置方式对比
配置这块,Logback 和 Log4j2 都支持 XML、JSON、YAML。但 Log4j2 多了一个无格式的 Properties 配置,适合简单场景。
举个例子,一个最简单的控制台输出配置:
<!-- Logback 配置 -->
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
<!-- Log4j2 配置 -->
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
你看,结构差不多,但 Log4j2 的标签名更简洁。我个人更喜欢 Log4j2 的配置风格,少写几个字母也是生产力嘛。
3.5 避坑指南:我曾经踩过的坑
这些年我见过太多日志相关的线上事故,挑几个典型的说说:
- 类路径冲突:项目里同时引入了 Log4j 1.x 和 Log4j 2.x 的 jar 包,结果 SLF4J 绑定到了旧版,异步日志直接不生效。排查了整整一天……解决方案:用 Maven Helper 插件检查依赖树,排除掉旧版。
- 异步日志丢消息:Logback 的异步 appender 默认队列大小是 256,高并发下队列满了会丢弃日志。我曾经在双十一大促时发现日志少了一半,就是因为队列满了。后来改成 Log4j2 的异步日志器,它的队列可以配置为等待,不会丢消息。
- 配置文件不生效:Log4j2 的配置文件必须放在 classpath 根目录,且文件名必须是 log4j2.xml 或 log4j2-spring.xml(Spring Boot 项目)。我见过有人把配置文件放错目录,结果日志全打到控制台了。
3.6 选型决策树
说了这么多,到底怎么选?我画了一张决策图,你照着走就行:
3.7 最终推荐方案
如果你问我现在的标准配置是什么,我的答案是:
- 门面:SLF4J(没得选,必须用)
- 实现:Log4j2(新项目首选)或 Logback(Spring Boot 默认,安全敏感项目)
- 桥接:引入 log4j-to-slf4j 或 jul-to-slf4j,把其他框架的日志统一路由到 SLF4J
举个例子,一个典型的 Maven 依赖配置:
<!-- SLF4J 门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<!-- Log4j2 实现 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.20.0</version>
</dependency>
<!-- 桥接其他日志框架 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>2.0.9</version>
</dependency>
嗯,这套配置我用了好几年,线上没出过日志相关的问题。记住:选型不是选最好的,而是选最适合你团队和业务场景的。Logback 不丢人,Log4j2 也不是万能药,关键是把门面用对,把实现配稳。