AsyncAppender 真正解耦的是日志记录调用层,即将 doAppend() 从业务线程移至后台线程执行,使 logger.info() 立即返回,但不改变日志最终写入磁盘的事实。

AsyncAppender 真正解耦的是哪一层调用
AsyncAppender 不改变日志最终写入磁盘的事实,它只把 doAppend() 调用从业务线程挪到后台线程。也就是说,logger.info("xxx") 这行代码执行完就返回,不再等 FileAppender 把内容刷进磁盘——I/O 阻塞被隔离在独立线程里。
注意:它不是“无 I/O”,而是“不卡你”。如果后台线程持续积压、队列满或磁盘本身慢,仍会丢日志(默认策略是丢弃新日志),这点必须提前意识到。
queueSize 设太小反而加剧丢日志风险
queueSize 是 AsyncAppender 内部阻塞队列的容量,默认值通常是 256。在 QPS 过千、单次请求打多条日志的场景下,这个值极易打满。
- 设为
1024或4096更稳妥,但别盲目堆大——内存占用和 GC 压力会上升 - 若日志事件对象本身含大量上下文(比如开启
includeCallerData="true"),每条日志内存开销翻倍,此时更需谨慎调大queueSize - 观察
AsyncAppender#isQueueFull()返回值(可通过 JMX 暴露)比硬调参数更有依据
子 Appender 仍可能是性能瓶颈源头
AsyncAppender 只负责转发,真正干活的还是它包装的子 Appender(如 RollingFileAppender)。如果子 Appender 配置不当,异步也救不了:
- 避免在
encoder中使用%caller{1}或%ex—— 每次都要栈遍历,CPU 开销陡增 -
RollingFileAppender的timeBasedFileNamingAndTriggeringPolicy在高频 rollover 场景下可能引发锁竞争,建议改用SizeAndTimeBasedFNATP并调大maxFileSize - 别让
FileAppender直接写 NFS 或低速云盘,本地 SSD + 异步才是组合拳
为什么 includeCallerData=false 是硬性建议
开启 includeCallerData="true" 会让 Logback 每次都做 Throwable.getStackTrace(),触发完整栈帧解析。这不仅是 CPU 密集型操作,还会显著放大对象分配量,间接加剧 GC 压力。
真实压测中,该开关开启后,吞吐量下降常超 30%,而多数诊断需求其实靠 %logger{36} + 行号(由编译器注入)已足够定位。除非你明确需要方法调用链,否则一律关掉。
真正难调的从来不是 AsyncAppender 本身,而是它背后那个默默扛压的子 Appender 和你的日志内容结构——别让一行 %ex{full} 毁掉整个异步收益。

















