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 框架生态关系图

常见日志框架生态关系 应用代码 SLF4J API JDK Logging (JUL) Python logging Logback Log4j 2 Log4j 1.x 说明:应用通过 SLF4J 门面调用具体实现; JUL 和 Python logging 自带实现,无需额外绑定。

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 都支持。

好了,这一节内容就这么多。记住:日志框架不是越新越好,而是要适合你的项目架构和团队习惯。只要遵循“门面+实现”的模式,将来想换也方便。