Log4j
前置知识:如果你还不了解日志框架的基本概念和 Java 日志生态全景,请先阅读「日志框架」和「JUL」。
本文你会学到:
- Log4j 1.x 的三大核心组件:
Logger、Appender、Layout如何协作 - 8 个日志级别从
OFF到ALL的含义与继承机制 - 从
log4j.properties到BasicConfigurator的多种配置方式 - 5 种 Appender(控制台、文件、按大小拆分、按时间拆分、数据库)的实战配置
PatternLayout占位符的完整用法- 自定义 Logger 和
additivity机制解决日志重复输出问题
📜 Log4j 的历史地位¶
当你听到「Log4j」时,可能会想到 2021 年底震惊业界的 Log4Shell 漏洞。但那个漏洞属于 Log4j 2——我们这里讨论的是它的前身 Log4j 1.x,一个诞生于 1999 年的 Apache 开源项目。
Log4j 1.x 是 Java 日志世界的奠基者。它首创了「Logger → Appender → Layout」三层架构,后来的 Logback、Log4j 2 都沿用了这套设计。可以说,理解了 Log4j 1.x,其他日志框架的架构你也能举一反三。
但现实是:Log4j 1.x 已于 2015 年停止维护(EOL),Spring Boot 从 1.4 版本起也不再支持它。既然已经过时,为什么还要学?
- 理解设计模式:
Logger→Appender→Layout是经典的职责分离设计,学习它能加深对架构设计的理解 - 维护遗留系统:很多老项目仍在使用 Log4j 1.x,了解它有助于排查问题
- 学习曲线的跳板:掌握了 Log4j 1.x 的概念,学习 Logback 和 Log4j 2 会事半功倍
🧱 核心组件¶
Log4j 1.x 的架构围绕三个核心组件展开。如果你读过「JUL」,会发现它们与 JUL 的 Logger → Handler → Formatter 高度相似:
graph LR
App["业务代码"] -->|"1. 调用日志方法"| Logger["Logger<br/>记录器"]
Logger -->|"2. 过滤级别"| Appender1["ConsoleAppender<br/>控制台输出"]
Logger -->|"2. 过滤级别"| Appender2["FileAppender<br/>文件输出"]
Appender1 -->|"3. 格式化"| Layout1["PatternLayout<br/>自定义格式"]
Appender2 -->|"3. 格式化"| Layout2["HTMLLayout<br/>HTML 格式"]
Layout1 --> Output1["控制台"]
Layout2 --> Output2["日志文件"]
classDef app fill:transparent,stroke:#539bf5,color:#adbac7,stroke-width:2px
classDef core fill:transparent,stroke:#f57c00,color:#adbac7,stroke-width:2px
classDef appender fill:transparent,stroke:#388e3c,color:#adbac7,stroke-width:1px
classDef layout fill:transparent,stroke:#7b1fa2,color:#adbac7,stroke-width:1px
classDef output fill:transparent,stroke:#d32f2f,color:#adbac7,stroke-width:1px
class App app
class Logger core
class Appender1,Appender2 appender
class Layout1,Layout2 layout
class Output1,Output2 output
三大组件的职责:
| 组件 | 类比 | 职责 |
|---|---|---|
Logger |
快递员 | 接收业务代码的日志请求,是日志系统的入口 |
Appender |
分拣中心 | 决定日志发往哪里(控制台、文件、数据库等) |
Layout |
打包员 | 将日志事件格式化为最终输出文本 |
Loggers(记录器)¶
Logger 是你代码中直接交互的对象。获取方式与 JUL 类似:
| 获取 Logger 实例 | |
|---|---|
Log4j 1.x 中有一个特殊的 RootLogger,它是所有 Logger 层级树的根节点,通过 Logger.getRootLogger() 获取。RootLogger 的配置对所有 Logger 生效,相当于全局默认配置。
Appenders(输出控制器)¶
Appender 决定日志输出到哪里。Log4j 1.x 提供了 5 种常用 Appender:
| Appender | 说明 |
|---|---|
ConsoleAppender |
输出到控制台(System.out 或 System.err) |
FileAppender |
输出到文件 |
DailyRollingFileAppender |
按时间拆分日志文件 |
RollingFileAppender |
按文件大小拆分日志文件 |
JDBCAppender |
持久化到数据库 |
Layout(格式化器)¶
Layout 负责将日志事件转换为字符串。Log4j 1.x 提供了 3 种 Layout:
| Layout | 说明 |
|---|---|
HTMLLayout |
输出为 HTML 表格格式 |
SimpleLayout |
仅包含级别和消息(LEVEL - message) |
PatternLayout |
通过占位符自定义格式(最常用) |
实际开发中几乎都使用 PatternLayout,它通过 ConversionPattern 配置灵活的输出格式(详见「格式化器」章节)。
📊 日志级别¶
Log4j 1.x 定义了 5 个标准级别,外加 OFF 和 ALL 两个特殊值:
| 级别 | 说明 |
|---|---|
OFF |
关闭所有日志(最高级别,不输出任何日志) |
FATAL |
致命错误,程序无法继续运行 |
ERROR |
错误信息,不影响程序继续运行 |
WARN |
警告信息,表示潜在问题 |
INFO |
一般信息 |
DEBUG |
调试信息(默认级别) |
TRACE |
最详细的调试信息(Log4j 1.2.12 引入) |
ALL |
输出所有日志(最低级别) |
级别继承机制:Logger 按名称层级自动形成父子关系(与 JUL 相同)。子 Logger 如果没有显式设置级别,会从父 Logger 继承。最终追溯到 RootLogger 的级别——默认为 DEBUG。
🚀 快速上手¶
Maven 依赖引入¶
| pom.xml 中引入 Log4j 1.x | |
|---|---|
仅用于学习
Log4j 1.x 已停止维护,生产环境请使用 Logback 或 Log4j 2。这里使用 1.2.17 版本仅用于学习核心概念。
BasicConfigurator:无需配置文件的快速启动¶
当你没有提供任何配置文件时,Log4j 不会输出任何日志。最简单的解决方式是调用 BasicConfigurator.configure():
| BasicConfigurator.configure() 的作用 | |
|---|---|
BasicConfigurator.configure() 做了三件事:
- 为
RootLogger添加一个ConsoleAppender - 设置
PatternLayout,格式为%r [%t] %p %c %x - %m%n - 设置
RootLogger级别为DEBUG
完整示例¶
项目中有完整的可运行示例,路径为 code/java/javase/logging/log4j-demo/。
⚙️ 配置详解¶
实际开发中不会使用 BasicConfigurator,而是通过 log4j.properties 配置文件来管理日志行为。Log4j 1.x 会在 classpath 下自动查找 log4j.properties 或 log4j.xml。
log4j.properties 语法¶
配置文件的核心语法可以归纳为三条规则:
本项目的默认配置文件:
输出到控制台¶
ConsoleAppender 是最简单的 Appender,将日志输出到 System.out 或 System.err:
| ConsoleAppender 配置 | |
|---|---|
| 配置项 | 说明 |
|---|---|
Target |
输出目标:System.out(默认)或 System.err |
layout |
格式化器,通常使用 PatternLayout |
输出到文件¶
FileAppender 将日志写入文件,适合持久化保存:
| 配置项 | 说明 |
|---|---|
File |
日志文件路径(相对路径相对于项目根目录) |
Append |
true = 追加模式(默认),false = 每次启动覆盖 |
Encoding |
文件编码,推荐 UTF-8 |
FileAppender 的问题
FileAppender 会把所有日志写入同一个文件,随着时间推移文件会越来越大,最终变得难以打开和检索。实际项目中应使用 RollingFileAppender 或 DailyRollingFileAppender 来自动拆分日志。
按文件大小拆分¶
RollingFileAppender 在日志文件达到指定大小时自动创建新文件,保留固定数量的备份:
| 配置项 | 说明 |
|---|---|
MaxFileSize |
单个日志文件的最大大小(如 10MB、1GB) |
MaxBackupIndex |
保留的备份文件数量(如 5 表示最多保留 application.log.1 到 application.log.5) |
文件滚动过程:当 application.log 达到 10MB 时,application.log.1 重命名为 application.log.2,原文件重命名为 application.log.1,然后创建新的空 application.log。
按时间拆分¶
DailyRollingFileAppender 按时间周期自动创建新文件,适合按天归档日志:
DatePattern 值 |
滚动周期 | 生成的文件名示例 |
|---|---|---|
'.'yyyy-MM-dd |
每天(最常用) | application.log.2026-04-12 |
'.'yyyy-MM-dd-HH |
每小时 | application.log.2026-04-12-14 |
'.'yyyy-MM |
每月 | application.log.2026-04 |
'.'yyyy-ww |
每周 | application.log.2026-15 |
DailyRollingFileAppender 的局限
它不支持限制备份文件数量,旧日志文件会一直累积,需要手动清理或借助外部脚本。如果你需要同时按时间和大小控制日志文件,建议升级到 Logback 或 Log4j 2。
持久化到数据库¶
JDBCAppender 将日志写入数据库表,适合需要集中管理日志或做日志分析的场景:
对应的建表 SQL:
| JDBCAppender 建表语句 | |
|---|---|
JDBCAppender 的性能问题
每条日志都会执行一次 SQL INSERT,在高并发场景下会严重影响性能。生产环境不建议直接使用 JDBCAppender,应考虑异步写入或使用专业的日志收集系统(如 ELK)。
自定义 Logger¶
当你需要让不同的包或类使用不同的日志策略时,可以配置自定义 Logger:
| 自定义 Logger 配置 | |
|---|---|
additivity:解决日志重复输出¶
自定义 Logger 的日志默认会**同时输出到自己的 Appender 和父 Logger 的 Appender**,这会导致日志重复。additivity 属性用来控制这个行为:
graph TD
Root["RootLogger<br/>console"]
Lgs["com.luguosong<br/>file"]
Svc["com.luguosong.service"]
Root --> Lgs --> Svc
Svc -->|"additivity=true(默认)"| Lgs
Lgs -->|"additivity=true(默认)"| Root
Svc -.->|"additivity=false"| Lgs
classDef root fill:transparent,stroke:#d32f2f,color:#adbac7,stroke-width:2px
classDef node fill:transparent,stroke:#f57c00,color:#adbac7,stroke-width:1px
classDef target fill:transparent,stroke:#388e3c,color:#adbac7,stroke-width:2px
class Root root
class Lgs node
class Svc target
additivity=true(默认):子 Logger 的日志会传递给所有祖先 Logger 的 Appender,可能导致同一条日志被输出多次additivity=false:子 Logger 的日志只由自己的 Appender 处理,不向父 Logger 传递
🎨 格式化器¶
PatternLayout 是 Log4j 1.x 中最常用的格式化器,通过 ConversionPattern 属性配置输出格式。它使用 % 加一个或多个字符作为占位符:
| 占位符 | 说明 | 示例输出 |
|---|---|---|
%d |
日期时间(可指定格式) | 2026-01-01 12:00:00 |
%p |
日志级别 | DEBUG |
%c |
Logger 名称(可指定精度) | com.luguosong.Test |
%m |
日志消息 | Hello World |
%n |
换行符 | — |
%L |
行号 | 42 |
%t |
线程名 | main |
%F |
文件名 | Log4jBasicTest.java |
%C |
类名(完整限定名) | com.luguosong.log4j.Log4jBasicTest |
%M |
方法名 | testBasicLogging |
%r |
从程序启动到当前的毫秒数 | 1234 |
%% |
输出一个百分号字符 | % |
格式修饰符¶
占位符可以添加格式修饰符来控制宽度和对齐:
| 格式修饰符示例 | |
|---|---|
| 修饰符 | 含义 | 示例 |
|---|---|---|
%-5p |
左对齐,宽度 5 | INFO(右补空格) |
%5p |
右对齐,宽度 5 | INFO(左补空格) |
%.10c |
最大宽度 10 | 截断过长的 Logger 名称 |
%c{1} |
Logger 名称只显示最后一段 | com.luguosong.Test → Test |
%c{2} |
Logger 名称显示最后两段 | com.luguosong.Test → luguosong.Test |
常用 ConversionPattern 模板¶
| 几种常用的格式模板 | |
|---|---|