3. 常见日志框架介绍
日志框架这东西,说简单也简单,说复杂还真能水好几章。我最早接触的是 Log4j 1.x,那时候觉得能写日志就不错了。后来接手过一个老项目,Log4j 1.x 版本居然还在用,结果某天安全扫描报了个高危漏洞——CVE-2019-17571,搞得我们连夜升级。从那以后,我养成了一个习惯:选日志框架时,先看看它背后有没有活跃的社区和定期的安全更新。
这节课咱们不扯太高大上的理论,我直接给你盘点一下 Java 生态和 Python 生态里最常见的几个日志框架。你想想看,一个系统如果日志输出都没管好,线上出问题你连查都查不了,那多被动。
3.1 日志门面 vs 日志实现
先理清一个概念。很多新手会把「日志框架」混为一谈,其实它们分两类:门面(Facade)和实现(Implementation)。门面只定义接口,不干活;实现才真正去写文件、发网络、控制格式。我建议你记住这张表:
| 角色 | 例 | 说明 |
|---|---|---|
| 门面 | SLF4J、Commons Logging、Python logging.getLogger | 只提供 API,不涉及实际输出 |
| 实现 | Logback、Log4j2、Log4j1、Java Util Logging | 负责格式化、写入目的地 |
为什么要拆开?说白了就是为了解耦。你代码里只用 SLF4J,底层换成 Logback 还是 Log4j2,一行配置改掉就行,代码不用动。
3.2 Java 四大天王
3.2.1 Log4j 1.x —— 老前辈
Log4j 1.x 可以说是日志框架的鼻祖,但现在已经寿终正寝了。Apache 官方在 2015 年就宣布它 EOL(End of Life),不再维护。我记得有一次项目里还用的 Log4j 1.2.17,硬生生因为一个 NDC 线程泄漏问题排查了三天。嗯,这里要注意:新项目千万不要再用 Log4j 1.x,它连异步日志都不支持,性能也差一大截。
3.2.2 Log4j 2.x —— 性能怪兽
Log4j 2 跟 1.x 基本没血缘关系,完全是重写的。它的异步日志用的是 Disruptor 环形队列,吞吐量能比 Logback 高出一个量级。你想想看,如果系统每秒要记几十万条日志,Log4j 2 基本是首选。
配置示例(XML 风格):
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<RollingFile name="File" fileName="logs/app.log"
filePattern="logs/app-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="File"/>
</Root>
</Loggers>
</Configuration>
3.2.3 Logback —— 亲儿子
Logback 是 Ceki Gülcü 写的,他同时也是 Log4j 1.x 的作者。Logback 天生就是 SLF4J 的亲儿子,不需要任何桥接。性能不如 Log4j 2,但胜在稳定、配置简单,而且很多 Spring Boot 项目默认就是用它。我个人习惯在中小型项目里直接用 Logback,省心。
配置示例(logback.xml):
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
3.2.4 Java Util Logging (JUL) —— 自带但鸡肋
JDK 自带的 java.util.logging,功能太弱,配置方式也反人类。平时几乎没人直接用它,但有时候第三方库会用,需要靠 SLF4J 的桥接包来重定向。建议直接忽略。
3.3 Python logging —— 自成体系
Python 的 logging 模块是标准库的一部分,不需要额外装包。它的设计其实跟 Java 的门面+实现很像:Logger 负责对外接口,Handler 负责输出(文件、控制台、网络等),Formatter 负责格式化。只是没有拆成独立的门面库。
基本用法:
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# 控制台 Handler
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
# 文件 Handler
fh = logging.FileHandler('app.log')
fh.setLevel(logging.ERROR)
fh.setFormatter(formatter)
logger.addHandler(ch)
logger.addHandler(fh)
logger.info("程序已启动")
logger.error("发生错误,请检查")
3.4 框架生态对比总览
| 特性 | Log4j 1.x | Log4j 2.x | Logback | Python logging |
|---|---|---|---|---|
| 维护状态 | EOL(已废弃) | 活跃 | 活跃 | 标准库维护 |
| 异步性能 | 不支持 | 极高(Disruptor) | 中(有 AsyncAppender) | 低(可用 QueueHandler) |
| 配置热加载 | 不支持 | 支持 | 支持 | 一般需自定义 |
| 与 SLF4J 集成 | 需桥接包 | 原生 | 原生 | 不适用 |
| 常见漏洞风险 | 高(已 EOL) | 低(修复及时) | 低 | 低 |
3.5 一张图看清日志框架生态
下面这张 SVG 图把 Java 和 Python 的日志框架分层关系画了出来。你看,SLF4J 作为统一的门面,上层对接应用代码,下层可以切换 Logback、Log4j2 或者 JUL。Python 的 logging 模块自己就是一整套,不需要额外门面。
总结一下:选框架时,我个人会先看项目语言——Java 的话,新项目直接 SLF4J + Logback 或 SLF4J + Log4j 2,看对性能的要求;Python 的话,用标准库 logging 就够了,最多加个第三方 Handler 比如 TimedRotatingFileHandler。别为了炫技引入一堆不成熟的框架,稳定才是第一位。