Java异步日志核心是利用Logback AsyncAppender或Log4j2 AsyncLogger,通过内存队列+专用线程解耦I/O,避免主线程阻塞;禁用手写线程池以防乱序、OOM、泄漏等问题。

Java 中实现异步日志打印不阻塞主流程,核心是把日志写入操作从主线程中剥离出去,交由独立线程或线程池处理。关键不是“自己手写异步逻辑”,而是合理利用成熟日志框架的异步能力,避免因 I/O(如磁盘写入、网络发送)拖慢业务。
用 Logback + AsyncAppender(推荐)
Logback 原生支持高性能异步日志,原理是:日志事件先入内存队列(BlockingQueue),由专用消费者线程异步刷盘。主业务线程只做轻量入队,几乎无等待。
- 添加 AsyncAppender 包裹你的实际 Appender(如 FileAppender 或 ConsoleAppender)
- 配置 queueSize(默认256)和 discardingThreshold,防止队列满时丢日志或阻塞
- 启用 includeCallerData="false"(除非真需要行号),减少日志对象构造开销
示例 logback.xml 片段:
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>app.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<p><appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE"/>
<queueSize>1024</queueSize>
<discardingThreshold>0</discardingThreshold>
<includeCallerData>false</includeCallerData>
</appender></p><p><root level="INFO">
<appender-ref ref="ASYNC"/>
</root>用 Log4j2 的 AsyncLogger(更高性能)
Log4j2 的异步日志基于 LMAX Disruptor(无锁环形缓冲区),吞吐量比 Logback AsyncAppender 更高,且默认不丢失日志(可配)。
立即学习“Java免费学习笔记(深入)”;
- 引入 log4j-core 和 log4j-api,禁用 log4j-1.x 桥接器
- 配置 status="WARN" 查看初始化是否成功
- 在 log4j2.xml 中启用 asyncLogger 或全局 asyncRoot
- 确保业务代码使用 org.apache.logging.log4j.LogManager.getLogger() 获取 logger
不建议手动 new Thread 或 ExecutorService 打日志
自己维护线程/线程池做日志异步化,看似可控,实则容易踩坑:
- 日志顺序无法保证(多线程并发写同一文件易乱序)
- OOM 风险:若日志量突增,无界队列会吃光堆内存
- 资源泄漏:未正确 shutdown 线程池,应用停机时线程残留
- 丢失日志:JVM 强制退出前,异步队列中的日志可能未刷出
除非有极特殊需求(比如必须对接某个自定义传输协议),否则优先用 Logback 或 Log4j2 内置方案。
注意:异步 ≠ 日志内容实时可见
异步只是解耦了“记录动作”和“写入动作”,但日志最终落盘仍有延迟。如果需调试时立刻看到日志:
- 开发环境可临时关闭 AsyncAppender,或用 ConsoleAppender + immediateFlush="true"
- 生产环境应接受几毫秒到几十毫秒的延迟,这是性能与可观测性的合理折中
- 避免在日志里放敏感上下文(如用户 token),异步线程可能持有引用导致内存泄漏



















