3. 常见日志框架介绍(Log4j、SLF4J、Python logging 等)
日志框架这东西,说白了就是你程序的“黑匣子”。我刚开始写代码那会儿,还靠 System.out.println 抓错,项目一上线就傻眼了——日志满天飞,根本没法复盘。后来才明白,选对日志框架、用对模式,能让你排查故障的效率翻几倍。
这一节,咱们就把 Java 和 Python 领域常见的日志框架捋一遍。我不只讲概念,还会告诉你每个框架在真实项目里“坑”在哪。
3.1 Log4j 家族——从 1.x 到 2.x 的进化
Log4j 1.x 是很多老项目的心头好。它靠一个 log4j.properties 就能搞定配置,简单粗暴。
# log4j.properties 示例
log4j.rootLogger=DEBUG, console, file
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c - %m%n
但我得提醒你,Log4j 1.x 已经 EOL(终止维护)了。我记得 2021 年那次 Log4shell 漏洞,很多项目就因为还在用 1.x,直接裸奔。要是你还在维护这类项目,赶紧升级。
Log4j 2.x 是重写版本。它引入了异步 Logger、无锁设计,性能比 1.x 快十几倍。关键是用 log4j2.xml 配置,更清晰:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{ISO8601} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
核心组件:Logger(日志器)→ Appender(输出目标)→ Layout(格式器)。三者组合决定了日志去哪、长啥样。
3.2 SLF4J——日志门面的“粘合剂”
SLF4J 本身不写日志,它是一个门面(Facade)。你代码里只依赖 SLF4J 的 API,运行时再绑定具体实现(Logback、Log4j 2 等)。这样切换框架时,你一行代码都不用改。
举个例子:你想从 Logback 切到 Log4j 2,只需要改 Maven 依赖,把 logback-classic 换成 log4j-slf4j-impl,其他不用动。
// 代码里只依赖 SLF4J API
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Logger log = LoggerFactory.getLogger(MyClass.class);
log.info("用户 {} 登录成功", userId);
我的习惯:新项目一律用 SLF4J + Logback 组合。Logback 是 Log4j 1.x 的继任者,原生支持 SLF4J,配置灵活,性能也很稳。
3.3 Python logging 模块——内置但容易用错
Python 自带的 logging 模块功能其实很强,但层次关系容易让人懵。它的核心是 Logger(日志器)、Handler(处理器)、Formatter(格式器)。注意:Python 的 Logger 是有父子关系的!比如 myapp.web 的父 Logger 是 myapp,默认会向上传递。
import logging
# 创建一个 Logger
logger = logging.getLogger('myapp')
logger.setLevel(logging.DEBUG)
# 创建一个 Handler
handler = logging.StreamHandler()
handler.setLevel(logging.INFO)
# 创建 Formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
# 给 Logger 添加 Handler
logger.addHandler(handler)
# 使用
logger.info("服务已启动")
我曾经踩过的坑:在 Flask 项目里直接用 logging.root 加 Handler,结果框架内部日志和业务日志混在一起。后来才改成给每个模块建独立的 Logger,用 propagate=False 切断向上传递,才清静下来。
3.4 其他日志框架快速对比
| 框架 | 语言 | 类型 | 特点 | 适合场景 |
|---|---|---|---|---|
| Log4j 2 | Java | 实现 | 异步、无锁、垃圾回收优化 | 高并发 Java 应用 |
| SLF4J | Java | 门面 | 统一 API,可切换实现 | 所有 Java 项目(首选) |
| Logback | Java | 实现 | 原生 SLF4J,自动重载配置 | Spring Boot、微服务 |
| Python logging | Python | 实现 | 内置,层次化 Logger | 所有 Python 应用 |
| java.util.logging (JUL) | Java | 实现 | JDK 内置,功能弱 | 老旧项目、无法加第三方 |
3.5 如何选择?我的建议
- Java 新项目:SLF4J + Logback 或 SLF4J + Log4j 2。两者性能都很好,Logback 更轻量。
- Java 老项目:如果还在用 Log4j 1.x,至少升级到 2.x,或者迁移到 SLF4J + Logback。
- Python 项目:不要用
print!直接用logging模块,给每个模块创建独立 Logger,并配置统一的dictConfig或fileConfig。
关键原则:日志框架要满足“统一入口、可配级别、可控输出、零性能损耗”。选门面+实现的模式,后期切换成本最小。
3.6 框架生态关系图
3.7 避坑指南
- 不要混用多个日志实现。SLF4J 虽然能绑定多个,但运行时只能选一个,不然会报
org.slf4j.impl.StaticLoggerBinder冲突。我曾经在同一个 Maven 项目里同时引了 Logback 和 Log4j 2,启动时直接挂掉。 - 生产环境慎用 DEBUG 级别。日志输出过多会拖慢 IO,甚至撑爆磁盘。建议线上用 INFO 级别,Debug 日志只在本机开发时开。
- 异步 Appender 要监控队列积压。Log4j 2 的异步 Logger 如果背压导致队列满,会丢日志。我习惯给异步队列加容量报警。
小技巧:调试 SDK 时,可以用 MDC(Mapped Diagnostic Context)给日志注入 traceId、userId,这样从海量日志里筛选某个请求的日志就方便多了。SLF4J 和 Log4j 2 都支持。
好了,这一节内容就这么多。记住:日志框架不是越新越好,而是要适合你的项目架构和团队习惯。只要遵循“门面+实现”的模式,将来想换也方便。