Log4j2 实战指南:Java 功能最强的日志框架
Log4j2 中文实战指南:log4j-api/log4j-core 依赖、log4j2.xml 配置结构(Appenders/Loggers)、PatternLayout 占位符、JsonLayout/JsonTemplateLayout JSON 输出、异步 Logger 与 garbage-free 性能模式、ThreadContext 上下文注入,以及观测云 DataKit 采集落地方案。
Log4j2 是 Apache 对 Log4j 1 的全面重写,模块化架构、LMAX Disruptor 异步、garbage-free 模式,是 Java 生态功能与性能上限最高的日志框架。本文从依赖配置讲到生产调优,并给出观测云平台的接入方案。
核心要点速览
- 两个依赖起步:
log4j-api(API)+log4j-core(实现);业务代码建议面向 SLF4J 门面。 - log4j2.xml 两段式:Appenders 定义去向与格式,Loggers 定义级别与路由。
- 性能双武器:全异步 Logger(Disruptor)+ garbage-free 模式,高吞吐场景碾压同行。
- 采集验证:检查 JsonTemplateLayout 的时间、级别、异常和 ThreadContext 字段能否被后端正确读取。
依赖与最小配置
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.24.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.24.0</version>
</dependency>
<!-- 业务代码面向 SLF4J:再加 log4j-slf4j2-impl 桥接 -->
src/main/resources/log4j2.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{ISO8601} %-5level [%t] %logger{36} - %msg %ex%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
代码:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
private static final Logger log = LogManager.getLogger(OrderService.class);
log.info("订单创建成功 orderId={}", order.getId());
log.error("扣款失败", ex); // 异常作为参数,自动带堆栈
级别:TRACE → DEBUG → INFO → WARN → ERROR → FATAL。
PatternLayout 常用占位符
| 占位符 | 含义 |
|---|---|
%d{ISO8601} |
ISO-8601 时间 |
%-5level |
级别 |
%logger{36} |
logger 名(缩写至 36 字符) |
%msg / %m |
消息 |
%ex / %throwable |
异常堆栈 |
%X{key} |
ThreadContext(MDC)值 |
JSON 输出:JsonLayout 与 JsonTemplateLayout
加 jackson-databind 依赖后用 JsonLayout 一行切换 JSON;生产更推荐 JsonTemplateLayout(依赖 log4j-layout-template-json),用模板文件精确控制输出结构:
<Console name="JsonConsole" target="SYSTEM_OUT">
<JsonTemplateLayout eventTemplateUri="classpath:EcsLayout.json"/>
</Console>
内置的 EcsLayout.json 模板直接输出 Elastic Common Schema 规范字段(@timestamp、log.level、log.logger);也可复制模板改名定制——比如把 @timestamp 改成 time、log.level 改成 status,对齐团队字段契约。
上下文注入:ThreadContext
ThreadContext.put("trace_id", traceId);
ThreadContext.put("user_id", userId);
try {
log.info("订单处理完成");
} finally {
ThreadContext.clearAll();
}
ThreadContext(MDC 的 Log4j2 叫法)中的键值随每条日志输出,JSON 模板中可直接引用。观测云的链路关联日志使用 trace_id 匹配;先检查模板是否输出该字段,以及它是否与对应请求的 Trace 一致。
性能调优:异步与 garbage-free
# log4j2.component.properties 或系统属性
log4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
log4j2.enableThreadlocals=true
log4j2.garbagefreeThreadContextMap=true
- 全异步 Logger:基于 LMAX Disruptor,日志写入不占用业务线程,吞吐提升一个量级;也可只对部分 logger 用
<AsyncLogger>标签选择性开启; - Garbage-free:复用 ThreadLocal 缓冲区,日志路径几乎零分配,GC 压力显著下降。
注意:异步模式崩溃时内存队列中的日志会丢——审计级日志保持同步,并验证刷盘和退出行为。采集器只能读取已输出的日志,不能恢复仍留在应用内存队列中的记录。
将 Log4j2 日志接入集中检索
主机上使用 RollingFile 输出时,按 DataKit 日志采集文档指定路径、source、service 和 Pipeline。JsonTemplateLayout 的 log.level 应映射为日志级别 status,@timestamp 按实际格式解析为事件时间;保留 logger 名、异常类型和请求标识用于过滤。
生成一条带异常堆栈和 trace_id 的测试日志,在观测云日志查看器核对字段与事件时间,再进入对应链路检查关联结果。最后测试轮转和应用退出,确认新日志仍被采集,避免仅凭正常运行时的一条记录判断接入完成。
常见问题(FAQ)
log4j2.xml 改了要重启吗?
<Configuration monitorInterval="30"> 开启定时检查,改文件 30 秒内自动重载,无需重启。
Spring Boot 项目怎么换成 Log4j2?
排除 spring-boot-starter-logging(Logback),引入 spring-boot-starter-log4j2,放 log4j2.xml 即可。SLF4J 门面保证业务代码零改动。
RollingFile 的轮转策略怎么配?
<TimeBasedTriggeringPolicy interval="1"/>(按天)或 <SizeBasedTriggeringPolicy size="100MB"/>,配合 <DefaultRolloverStrategy max="30"/> 控制历史文件数量。云原生部署建议直接 stdout,轮转交给采集层。
日志里 %X{} 是空的?
ThreadContext 的 put 必须发生在日志调用之前且同一线程。异步线程池场景注意上下文传递(用 TaskDecorator 或 Log4j2 的上下文数据提供器)。
系列阅读
- 上一篇:Java 日志最佳实践十条
- 下一篇:Logback 实战指南
- 相关阅读:Java 日志库对比 | 什么是结构化日志