Apache Log4j 2.26.1 集成使用实战详解
从 Maven 依赖引入到三个可运行示例(Console/滚动文件/异步)完整演示 Apache Log4j 2.26.1 的集成使用,全部代码与回显均实测通过。
Apache Log4j 2.26.1 集成使用实战详解
目标:在 Ubuntu 22.04 上以 Maven 项目集成 Log4j 2.26.1,完成控制台、滚动文件、异步日志三个可运行示例,并掌握 Marker/ThreadContext/Filter 等进阶用法。已在 Ubuntu 22.04(OpenJDK 17)实测通过。
一、环境信息
| 项目 | 值 |
|---|---|
| 操作系统 | Ubuntu 22.04.5 LTS |
| 系统架构 | x86_64(amd64) |
| JDK | OpenJDK 17.0.20.1 |
| Maven | Apache Maven 3.6.3(apt 源) |
| 组件版本 | Apache Log4j 2.26.1、disruptor 4.0.0 |
| 项目目录 | /opt/log4j-demo |
| 日志输出目录 | /opt/log4j-demo/logs |
表后逐项说明:JDK 17 是 Log4j 2.24+ 的硬性要求;Maven 仅用于依赖管理与编译,3.6.3 即可;实验项目放 /opt/log4j-demo,文件型日志统一输出到其 logs 子目录。
二、依赖引入
2.1 创建 Maven 工程
mkdir -p /opt/log4j-demo/src/main/java/com/example /opt/log4j-demo/src/main/resources
2.2 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>log4j-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.26.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.26.1</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>4.0.0</version>
</dependency>
</dependencies>
</project>
逐项说明:log4j-api 提供编译期接口;log4j-core 提供运行期实现(两者都显式声明,实测依赖树证实 core 不会把 api 传递进来);disruptor 4.0.0 供 AsyncLogger 使用;编译目标 17 与 JDK 匹配。
2.3 拉取依赖并编译
cd /opt/log4j-demo
mvn compile
实测回显(输出节选,实测首尾):
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------< com.example:log4j-demo >-----------------------
[INFO] Building log4j-demo 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] --- resources:3.3.1:resources (default-resources) @ log4j-demo ---
[INFO] Copying 5 resources from src/main/resources to target/classes
[INFO] --- compiler:3.13.0:compile (default-compile) @ log4j-demo ---
[INFO] Compiling 3 source files with javac [debug target 17] to target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
生成运行用 classpath 文件:
mvn dependency:build-classpath -Dmdep.outputFile=/tmp/cp.txt -q
cat /tmp/cp.txt
实测回显:
/root/.m2/repository/org/apache/logging/log4j/log4j-api/2.26.1/log4j-api-2.26.1.jar:/root/.m2/repository/org/apache/logging/log4j/log4j-core/2.26.1/log4j-core-2.26.1.jar:/root/.m2/repository/com/lmax/disruptor/4.0.0/disruptor-4.0.0.jar
说明:classpath 只含三个 jar,验证了"api + core + disruptor"的最小依赖集;后续所有示例都用 java -cp "target/classes:依赖路径" 方式运行,与 IDE/应用服务器的运行模型一致。
三、示例一:控制台日志(级别/Marker/ThreadContext 实测)
3.1 业务代码
package com.example;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.apache.logging.log4j.ThreadContext;
public class LevelConsoleDemo {
private static final Logger logger = LogManager.getLogger(LevelConsoleDemo.class);
private static final Marker SECURITY = MarkerManager.getMarker("SECURITY");
private static final Marker AUDIT = MarkerManager.getMarker("AUDIT").setParents(SECURITY);
public static void main(String[] args) {
ThreadContext.put("requestId", "req-20260101-0001");
logger.trace("trace 级别消息,低于 Root/INFO 时不输出");
logger.debug("debug 级别消息,SQL 参数预览");
logger.info("info 级别消息,服务启动完成");
logger.warn("warn 级别消息,磁盘使用率 85%");
logger.error("error 级别消息,订单 1001 支付超时", new IllegalStateException("payment timeout"));
logger.fatal("fatal 级别消息,无法连接数据库");
logger.info(AUDIT, "用户 root 登录成功(AUDIT 继承自 SECURITY)");
ThreadContext.clearAll();
}
}
逐项说明:LogManager.getLogger(类) 创建同名 Logger;AUDIT 通过 setParents 挂到 SECURITY 下;ThreadContext.put 写入请求维度键值;error 调用第二参数传异常对象,配合布局的 %throwable 打栈。
3.2 配置文件 log4j2-console.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} [%marker] [req=%X{requestId}] - %msg%n%throwable"/>
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
</Console>
</Appenders>
<Loggers>
<Logger name="com.example" level="DEBUG" additivity="false">
<AppenderRef ref="Console"/>
</Logger>
<Root level="INFO">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
逐项说明:pattern 的 %-5level 左对齐补齐 5 字符;%logger{36} 超长包名缩写;[%marker] 无 Marker 时输出 [];%X{requestId} 取 ThreadContext 键;monitorInterval=30 每 30 秒检测配置变更热加载。
3.3 编译与运行
LC_ALL=C.UTF-8 java -Xms256m -Xmx256m -Dlog4j.configurationFile=src/main/resources/log4j2-console.xml -cp "target/classes:$(cat /tmp/cp.txt)" com.example.LevelConsoleDemo
实测回显:
2026-10-14 16:57:03.122 [main] DEBUG com.example.LevelConsoleDemo [] [req=req-20260101-0001] - debug 级别消息,SQL 参数预览
2026-10-14 16:57:03.126 [main] INFO com.example.LevelConsoleDemo [] [req=req-20260101-0001] - info 级别消息,服务启动完成
2026-10-14 16:57:03.126 [main] WARN com.example.LevelConsoleDemo [] [req=req-20260101-0001] - warn 级别消息,磁盘使用率 85%
2026-10-14 16:57:03.127 [main] ERROR com.example.LevelConsoleDemo [] [req=req-20260101-0001] - error 级别消息,订单 1001 支付超时
java.lang.IllegalStateException: payment timeout
at com.example.LevelConsoleDemo.main(LevelConsoleDemo.java:20)
2026-10-14 16:57:03.128 [main] FATAL com.example.LevelConsoleDemo [] [req=req-20260101-0001] - fatal 级别消息,无法连接数据库
2026-10-14 16:57:03.128 [main] INFO com.example.LevelConsoleDemo [AUDIT[ SECURITY ]] [req=req-20260101-0001] - 用户 root 登录成功(AUDIT 继承自 SECURITY)
逐项说明:trace 被过滤(Logger DEBUG 级别与 ThresholdFilter 双重拦截);ERROR 行带完整异常栈;AUDIT Marker 显示为 AUDIT[ SECURITY ];运行参数 -Xms256m -Xmx256m 限定堆,另开终端用 jps -v 可见:
4314 LevelConsoleDemo -Xms256m -Xmx256m -Dlog4j.configurationFile=src/main/resources/log4j2-console.xml
四、示例二:滚动文件(RollingFile 实测)
4.1 业务代码
package com.example;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class RollingFileDemo {
private static final Logger logger = LogManager.getLogger(RollingFileDemo.class);
public static void main(String[] args) {
for (int i = 0; i < 200; i++) {
logger.info("滚动日志测试第 {} 行:订单 1001 支付成功,金额 128.00 元", i);
}
logger.info("写入完成,请查看 /opt/log4j-demo/logs 目录");
}
}
说明:占位符 {} 由布局用 %msg 配合参数替换渲染,比字符串拼接省去无效拼接开销。
4.2 配置文件 log4j2-rolling.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<RollingFile name="RollingFile" fileName="/opt/log4j-demo/logs/app.log"
filePattern="/opt/log4j-demo/logs/app-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %p %c{1.} [%t] - %m%n"/>
<Policies>
<SizeBasedTriggeringPolicy size="10 KB"/>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
<DefaultRolloverStrategy max="5"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="RollingFile"/>
</Root>
</Loggers>
</Configuration>
逐项说明:fileName 是活动文件;filePattern 是滚动后的命名模板(%i 是当日序号,.gz 后缀触发滚动时自动压缩);SizeBasedTriggeringPolicy size=10 KB 表示单文件超 10KB 滚动;TimeBasedTriggeringPolicy interval=1 按天滚动(与 filePattern 中 %d 粒度一致);DefaultRolloverStrategy max=5 限制当日最多保留 5 个序号文件。
4.3 运行与验证
mkdir -p /opt/log4j-demo/logs
LC_ALL=C.UTF-8 java -Xms256m -Xmx256m -Dlog4j.configurationFile=src/main/resources/log4j2-rolling.xml -cp "target/classes:$(cat /tmp/cp.txt)" com.example.RollingFileDemo
ls -l /opt/log4j-demo/logs/
实测回显:
total 16
-rw-r--r-- 1 root root 492 Oct 14 16:57 app-2026-10-14-1.log.gz
-rw-r--r-- 1 root root 492 Oct 14 16:57 app-2026-10-14-2.log.gz
-rw-r--r-- 1 root root 6321 Oct 14 16:57 app.log
查看归档内容(首尾):
zcat /opt/log4j-demo/logs/app-2026-10-14-1.log.gz | head -2
zcat /opt/log4j-demo/logs/app-2026-10-14-1.log.gz | tail -1
head -2 /opt/log4j-demo/logs/app.log
tail -1 /opt/log4j-demo/logs/app.log
实测回显:
2026-10-14 16:57:13.238 INFO c.e.RollingFileDemo [main] - 滚动日志测试第 0 行:订单 1001 支付成功,金额 128.00 元
2026-10-14 16:57:13.240 INFO c.e.RollingFileDemo [main] - 滚动日志测试第 1 行:订单 1001 支付成功,金额 128.00 元
2026-10-14 16:57:13.249 INFO c.e.RollingFileDemo [main] - 滚动日志测试第 76 行:订单 1001 支付成功,金额 128.00 元
2026-10-14 16:57:13.263 INFO c.e.RollingFileDemo [main] - 滚动日志测试第 154 行:订单 1001 支付成功,金额 128.00 元
2026-10-14 16:57:13.269 INFO c.e.RollingFileDemo [main] - 写入完成,请查看 /opt/log4j-demo/logs 目录
逐项说明:200 行共约 13KB,超过 10KB 阈值滚动两次,序号 1、2 归档压缩(492 字节),活动文件 app.log 保留最后 6321 字节;%c{1.} 输出 com.example 缩写为 c.e.;全部输出经 %d 带毫秒时间戳。
五、示例三:异步日志(AsyncLogger 实测)
5.1 业务代码
package com.example;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class AsyncDemo {
private static final Logger logger = LogManager.getLogger(AsyncDemo.class);
public static void main(String[] args) throws Exception {
long start = System.nanoTime();
for (int i = 0; i < 500000; i++) {
logger.info("异步日志性能测试 {}", i);
}
long submitMs = (System.nanoTime() - start) / 1_000_000;
System.out.println("500000 条异步日志提交耗时 " + submitMs + " ms(入队即返回)");
LogManager.shutdown();
System.out.println("LogManager.shutdown 完成,日志已全部落盘");
}
}
说明:AsyncLogger 把"格式化与写盘"交给 Disruptor 消费线程,业务线程只做入队;退出前调用 LogManager.shutdown 确保缓冲排空。
5.2 配置文件 log4j2-async.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<File name="AsyncFile" fileName="/opt/log4j-demo/logs/async.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %p [%t] %c{1.} - %m%n"/>
</File>
</Appenders>
<Loggers>
<AsyncLogger name="com.example.AsyncDemo" level="INFO" includeLocation="false">
<AppenderRef ref="AsyncFile"/>
</AsyncLogger>
<Root level="INFO">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
逐项说明:AsyncLogger 元素与 Logger 同位置声明,name 匹配的 Logger 自动走 Disruptor;includeLocation=false 关闭调用位置采集(取栈开销大,是异步吞吐的关键开关);Root 保持普通同步(非全局异步模式,只对声明的 AsyncLogger 生效)。
5.3 运行与验证
LC_ALL=C.UTF-8 java -Xms256m -Xmx256m -Dlog4j.configurationFile=src/main/resources/log4j2-async.xml -cp "target/classes:$(cat /tmp/cp.txt)" com.example.AsyncDemo
实测回显:
500000 条异步日志提交耗时 5383 ms(入队即返回)
LogManager.shutdown 完成,日志已全部落盘
落盘完整性验证:
wc -l /opt/log4j-demo/logs/async.log
head -2 /opt/log4j-demo/logs/async.log
tail -1 /opt/log4j-demo/logs/async.log
实测回显:
500000 /opt/log4j-demo/logs/async.log
2026-10-14 16:57:38.504 INFO [main] c.e.AsyncDemo - 异步日志性能测试 0
2026-10-14 16:57:43.887 INFO [main] c.e.AsyncDemo - 异步日志性能测试 499999
逐项说明:50 万条全部落盘(wc -l 计数一致),从首条到末条时间跨度约 5.4 秒;业务线程提交完成后立刻打印耗时(5383 ms),说明写入工作全部由后台线程承担;关机钩子保证 JVM 正常退出时缓冲不丢。
六、进阶用法
6.1 精确级别过滤(LevelMatchFilter)
在 Appender 上只放行某一级别(2.26.1 用 LevelMatchFilter,注意 LevelFilter 这个旧元素名已不存在):
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="LEVELFILTER %-5level - %msg%n"/>
<LevelMatchFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
</Console>
实测输出(Root 级别 DEBUG 下的程序):
LEVELFILTER WARN - warn 级别消息,磁盘使用率 85%
说明:与 ThresholdFilter(放行 >= 阈值)不同,LevelMatchFilter 是"等于"语义,onMatch/onMismatch 支持 ACCEPT/NEUTRAL/DENY 三值组合。
6.2 内部状态排查(status=DEBUG)
2026-10-14T17:26:30.661243734Z main DEBUG Apache Log4j Core 2.26 initializing configuration XmlConfiguration[location=/opt/log4j-demo/src/main/resources/log4j2-debug.xml, lastModified=2026-10-14T17:25:02.014Z]
2026-10-14T17:26:30.663621510Z main DEBUG PluginManager 'Core' found 134 plugins
说明:status=DEBUG 时输出的第一行会显示实际加载的配置文件路径与最后修改时间,是验证"配置到底加载了哪份"的实测依据。
6.3 运行期指定配置文件
所有示例都通过 -Dlog4j.configurationFile 显式指定配置,多环境部署时最直观;不指定时按 classpath 的 log4j2.properties/yaml/json/xml 顺序自动发现,都找不到则回退 ERROR 级别默认配置。
七、常见问题(FAQ)
Q1:mvn compile 报 invalid target release: 17? A:编译机的 JDK 低于 17。确认 java -version 输出 17 开头(Ubuntu 22.04:apt-get install openjdk-17-jdk-headless),并确认 JAVA_HOME 指向 17。
Q2:运行时报 NoClassDefFoundError: org/apache/logging/log4j/Logger? A:classpath 缺 log4j-api。本文运行方式用 dependency:build-classpath 产出的 /tmp/cp.txt 拼进 -cp,直接手写 -cp target/classes 就会丢依赖。
Q3:控制台中文乱码问号? A:JVM 在管道输出时按系统 locale 编码 stdout。给 java 命令前加 LC_ALL=C.UTF-8 LANG=C.UTF-8(实测解决);文件型 Appender 不受影响,因为文件编码默认 UTF-8。
Q4:RollingFile 没有滚动? A:检查三点:Policies 里 SizeBasedTriggeringPolicy 的 size 是否达到(实测 10 KB);filePattern 里是否含 %i(大小滚动序号);写入量是否真的超阈值。本文 200 行约 13KB 滚出两个 gz 归档。
Q5:异步日志丢了最后几条? A:AsyncLogger 的缓冲需要排空。应用退出前调用 LogManager.shutdown()(实测调用后 wc -l 恰好 500000 行);或注册 JVM 关机钩子。异常 kill -9 属不可恢复场景。
Q6:includeLocation=true 后异步吞吐骤降? A:采集调用位置要抓当前线程栈,异步模式下开销被放大。生产建议 includeLocation="false"(本文异步配置即如此),确需位置信息时仅对低频 Logger 开启。
Q7:怎么验证 JVM 堆参数真的生效? A:jps -v 查看进程参数,实测回显包含 -Xms256m -Xmx256m:4314 LevelConsoleDemo -Xms256m -Xmx256m ...。也可 ps -ef | grep [j]ava 看完整命令行。
Q8:占位符 {} 与字符串拼接怎么选? A:所有可能被级别过滤掉的日志都用参数化写法 logger.info("值 {}", obj),避免无效拼接;唯一必须拼接的场景是异常在非最后一个参数位。JDK 17 + Log4j 2.26 建议配合 lambda:logger.debug("值 {}", () -> expensiveCall())。
Q9:同一 Logger 想同时写控制台和文件怎么办? A:Logger 下并列多个 AppenderRef,并把 additivity 设为 false 防止向 Root 重复传播;各 Appender 可配不同 Layout(控制台 Pattern、文件 JsonTemplate)。
Q10:日志文件权限怎么控制? A:File/RollingFile 支持 fileOwner/fileGroup/filePermissions 属性(POSIX),在 Appender 元素上声明即可;也可由运维统一以 umask 或日志目录 ACL 约束。本文实验以 root 运行,文件默认 644。
八、参考链接
- Apache Log4j 官方文档:https://logging.apache.org/log4j/2.x/
- Maven 使用手册(Getting Started):https://logging.apache.org/log4j/2.x/manual/getting-started.html
- log4j-api 2.26.1 下载:https://repo.maven.apache.org/maven2/org/apache/logging/log4j/log4j-api/2.26.1/
- log4j-core 2.26.1 下载:https://repo.maven.apache.org/maven2/org/apache/logging/log4j/log4j-core/2.26.1/
- Disruptor 4.0.0:https://repo.maven.apache.org/maven2/com/lmax/disruptor/4.0.0/
- Log4j ThreadContext 手册:https://logging.apache.org/log4j/2.x/manual/thread-context.html