logback中需配置neverblock="true"使队列满时调用offer()丢弃日志,配合discardingthreshold="80"提前丢弃低优先级日志,并增大queuesize至8192以上,确保业务线程不阻塞。

核心是把日志写入从主线程剥离出去,让业务线程只负责“投递日志事件”,不等落盘完成——这靠异步 Appender + 合理丢弃策略 + 高效落盘组件共同实现。
Logback:用 AsyncAppender + neverBlock=true
Logback 默认的 AsyncAppender 底层用的是 ArrayBlockingQueue,但默认调用 put() 方法,队列满就阻塞。要真正解耦,必须显式启用非阻塞模式:
- 配置
<neverblock>true</neverblock>,使队列满时改用offer(),失败即丢弃 - 搭配
<discardingthreshold>80</discardingthreshold>(表示剩余容量低于 20% 时开始丢 INFO 及以下日志) -
务必调大队列大小:默认
queueSize="256"完全不够,压测建议设为8192或更高 - ERROR/WARN 日志默认不丢,若也想保底不阻塞,
neverBlock="true"是前提
Log4j2:用 AsyncLogger 或 AsyncAppender + Discarding 策略
Log4j2 异步能力更强,底层基于 LMAX Disruptor 的无锁环形队列。但光开异步还不够,必须防住队列满的阻塞点:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 禁用默认的
DefaultAsyncQueueFullPolicy(会阻塞),改用DiscardingAsyncQueueFullPolicy - 在
log4j2.xml中对应AsyncLogger或AsyncAppender内添加:<discardingasyncqueuefullpolicy></discardingasyncqueuefullpolicy> - 增大 RingBuffer:默认仅
4096,建议设为65536(2 的幂次)或更高,通过系统属性-Dlog4j2.asyncLoggerRingBufferSize=65536 - 避免在异步上下文中频繁调用
Throwable.getStackTrace(),它仍在业务线程执行,可能拖慢投递
配套优化:减少 IO 压力本身
异步只是转移压力,如果落盘本身太慢,队列迟早积压。需同步升级落盘组件:
- Logback 推荐搭配
RollingFileAppender+TimeBasedTriggeringPolicy,避免单文件过大导致滚动卡顿 - Log4j2 强烈推荐
RollingRandomAccessFileAppender:用内存映射文件(MappedByteBuffer)绕过 JVM 堆缓冲和 OutputStream 锁,bufferSize="262144"(256KB)效果明显 - 关闭
immediateFlush="true"(尤其生产环境),允许缓冲批量刷盘;确保磁盘是 SSD,且日志目录不在 NFS 或远程共享路径上
别忘了兜底和可观测性
丢日志不是目的,而是可控降级的手段:
- 只对 DEBUG/TRACE/INFO 等非关键日志启用丢弃;WARN 和 ERROR 建议保留,或降级写入本地紧急日志文件
- 通过
RejectedExecutionHandler统计被丢弃日志数量,接入 Prometheus 或触发告警(比如每分钟丢超 100 条就通知) - 监控队列水位、丢弃率、磁盘 I/O 等指标,避免把“丢日志”当成掩盖磁盘瓶颈的遮羞布
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










