推荐使用log4j2或logback的异步appender:log4j2基于lmax disruptor,高性能低延迟;logback asyncappender轻量但吞吐略低;均需合理配置队列大小、刷盘与滚动策略,避免丢日志和阻塞。

Java 中将系统日志异步输出到文件,核心是避免主线程阻塞 I/O 操作,同时保证日志不丢失、顺序合理、线程安全。推荐使用成熟日志框架(如 Log4j2 或 Logback)的异步 Appender,而非手动实现线程池写文件——后者容易出错且难以兼顾可靠性与性能。
用 Log4j2 的 AsyncAppender 实现真正异步
Log4j2 内置高性能异步日志机制(基于 LMAX Disruptor),比简单线程池更高效、低延迟、无锁。
- 引入依赖(Maven):
org.apache.logging.log4j
log4j-core
2.20.0 - 配置
log4j2.xml,启用异步 Appender:
- 代码中直接使用 Logger,调用即异步:
Logger logger = LogManager.getLogger();
logger.info("这行日志会异步写入文件");
用 Logback 的 AsyncAppender(基于队列+单消费者线程)
Logback 提供轻量级异步封装,适合对启动时间和内存敏感的场景,但吞吐略低于 Log4j2 的 Disruptor。
- 添加依赖:
ch.qos.logback
logback-classic
1.4.14 - 配置
logback.xml:
logs/app.log
logs/app.%d{yyyy-MM-dd}.%i.log
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
256
0
false
- 注意:
queueSize设太小可能丢日志(默认 256),设太大占内存;生产环境建议配合neverBlock=true避免阻塞应用线程。
不推荐的手动异步方式(仅作对比)
自己用 ExecutorService + FileWriter 异步写日志,看似可控,实则隐患多:
- 无法保证日志顺序(多个线程并发写同一文件易乱序)
- 异常未捕获会导致日志静默丢失
- 没做缓冲和批量写入,I/O 频繁,性能反而更差
- 进程退出时未 flush 或 shutdown,最后几条日志丢失
- 文件滚动、压缩、归档需全部自行实现
关键补充:确保日志不丢失
异步日志的可靠性依赖正确配置:
-
Log4j2:设置
shutdownHookEnabled="true"(默认开启),JVM 关闭前自动刷盘 -
Logback:确保
AsyncAppender的includeCallerData="false"(减少对象创建),并启用neverBlock="true" - 所有配置中,
immediateFlush="true"(FileAppender 级)可强制每次写都刷磁盘,但显著降低性能,仅调试时开启 - 线上建议搭配按时间/大小滚动策略(如
TimeBasedRollingPolicy),避免单文件过大
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











