核心是让日志写入彻底脱离业务线程:采用Log4j2+Disruptor无锁环形缓冲区,配置AsyncLogger、丢弃策略与NVMe SSD滚动文件输出。

核心是让日志写入彻底脱离业务线程——用异步队列做缓冲,由专用消费者线程处理落盘,避免 I/O 和锁竞争拖慢接口响应。
选对框架:Log4j2 + Disruptor 是高并发首选
Logback 的 AsyncAppender 基于普通阻塞队列(如 ArrayBlockingQueue),在极端压力下仍可能因锁争用或 GC 拖累性能;Log4j2 内置 LMAX Disruptor,采用无锁环形缓冲区(RingBuffer),吞吐更高、延迟更稳。必须确认两点:
- 引入 disruptor-3.4.4+ 依赖(Log4j2 2.17+ 默认不带,需显式添加)
- 启动时日志中出现 Using Disruptor version X.X.X,表示环形缓冲区已生效
配置真正异步:别被“Async”名字骗了
Log4j2 有三种“异步”模式,只有两种能切断业务线程阻塞:
-
AsyncLogger(推荐):全局统一使用
<asynclogger></asynclogger>或配置Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector,所有日志调用直接进 RingBuffer - AsyncAppender:将普通 Appender 包一层异步包装,但 Logger 本身仍是同步的,适合局部改造
- 普通 Logger + AsyncAppender 组合:无效!Logger 同步格式化 + 异步落盘,格式化阶段仍卡主线程
队列策略要设为“丢弃”,不是“等待”
默认满队列行为是阻塞生产者(即业务线程),这等于把日志瓶颈又还给了接口。必须主动降级:
立即学习“Java免费学习笔记(深入)”;
- 设置
log4j2.asyncLoggerWaitStrategy=Timeout避免死等 - 指定丢弃策略:
log4j2.asyncLoggerQueueFullPolicy=Discarding - 扩大缓冲区:设
log4j2.asyncLoggerRingBufferSize=262144(256K → 2^18),留足削峰空间
输出目标必须避开控制台
哪怕开了 direct="true",ConsoleAppender 在万级 TPS 下仍是短板——System.out 底层锁无法绕过,实测比 SSD 上的 RollingFile 慢 20 倍以上:
- 生产环境禁用任何 ConsoleAppender,包括重定向到
/dev/null的伪优化 - 全部切到
RollingFileAppender,路径挂载本地 NVMe SSD - 滚动策略启用双触发:
TimeBasedTriggeringPolicy+SizeBasedTriggeringPolicy,防止单文件过大影响排查效率



















