公司有一次线上故障——某个下游服务挂了,调用方在 catch 块里打了 log.error("调用失败", e)。这行代码每分钟被执行了 5 万次,5 万条堆栈日志,每条 3KB,一分钟就写了 150MB 的日志文件。运维发现的时候磁盘已经满了,其他服务也跟着挂。灾难的起点不是下游挂了,而是日志把磁盘写爆了。 这种日志爆炸场景的典型特征是突发性——平时打日志没问题,一旦某个循环里遇到了异常,日志量瞬间暴涨。今天聊聊怎么给日志加上限频和异步落盘,让它在异常场景下也不会失控。 问题出在哪:日志写入是同步阻塞的 Logback 默认的 FileAppender 是同步写盘的。log.error() 调用时,线程会等日志写完了才继续执行。平时没感觉,但一旦日志量暴增,磁盘 IO 就成了瓶颈——所有打日志的线程都堵在等磁盘写入。 而且同步模式下,每行日志都是一次 write() 系统调用。一分钟 5 万条就是 5 万次系统调用,再加上堆栈的格式化,CPU 也被打满。 方案一:异步落盘,业务线程不等 IO Logback 的 AsyncAppender 就是干这个的: <appende....
日志爆炸防护机制:异常打印刷爆磁盘?动态限频+异步落盘救急!
做后端服务的同学肯定都遇到过这个问题:生产环境突然大量异常日志打出来,结果磁盘空间瞬间被占满,导致应用崩溃。更可怕的是,这种日志爆炸往往发生在问题排查的关键时刻——你想查日志定位问题,结果日志系统先挂了。 我之前就经历过这样一个案例:某个接口被恶意刷流量,返回了大量异常,因为异常日志太多,磁盘空间在几分钟内被完全占满。最后不仅业务停了,连日志都没留下,问题排查变得极其困难。 今天我们就来聊聊日志爆炸的防护机制,让你的系统在日志风暴中依然稳稳当当。 日志爆炸的常见场景 1. 恶意请求刷接口 攻击者或者错误配置的前端,不断请求一个会抛异常的接口: 10万次/秒 × 每次打印1KB日志 = 100GB/秒的日志量! 2. 循环打印异常 有些代码在异常处理中又抛出异常,形成死循环: try { doSomething(); } catch (Exception e) { log.error("操作失败", e); // 这里又触发了新异常 throw new RuntimeException(e); // 继续抛出 } 3. 日志配置不当 使用 e.printStackTrace() 而不....
