必须用errors.is(err, syscall.enospc)判断磁盘满,因标准库和主流库仅识别系统错误码enospc或error_disk_full来触发重试/清理;字符串匹配、自定义错误或os.isnospace均无效,且容器中还需检查inodes。

必须用 errors.Is(err, syscall.ENOSPC) 判断磁盘满,字符串匹配或自定义错误全无效。
为什么 errors.Is(err, syscall.ENOSPC) 是唯一可靠方式
Go 标准库(如 os.WriteFile)、主流日志库(lumberjack)和文件系统抽象层,只在底层 syscall 返回 ENOSPC(Linux/macOS)或 ERROR_DISK_FULL(Windows)时触发重试、清理或降级逻辑。其他方式都会被当成普通 I/O 错误忽略:
-
strings.Contains(err.Error(), "no space")在中文 locale 下直接失效,还可能误匹配日志内容里的路径或用户输入 -
errors.New("disk full")或fmt.Errorf("write failed: %w", err)无法通过errors.Is匹配到系统错误码 - Go 没有
os.IsNoSpace()这类 helper 函数 —— 你得自己写判断 - 容器环境还要额外检查 inodes:用
unix.Statfs查AvailInodes,光看字节空间会漏判
测试时如何稳定复现 ENOSPC 错误
别塞满真实磁盘:慢、要 root、不可控、清理麻烦。直接在 I/O 接口层伪造更轻量可靠:
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
- 实现一个
io.Writer,在累计写入达到阈值后返回syscall.ENOSPC(Linux/macOS)或syscall.ERROR_DISK_FULL(Windows) - 若被测代码用了
bufio.Writer,测试前必须调用Flush(),否则错误卡在缓冲区里不抛出 - 替换
os.Stdout时,mock writer 必须同时实现io.Closer,且Close()返回nil,否则调用方os.Stdout.Close()会 panic - 对
io.Copy,注意它接受短写(n 且 <code>err == nil)—— 测试应优先触发错误,而非返回部分成功
写入后不检查 n 值会导致磁盘满被静默掩盖
Write() 和 WriteString() 都返回 (int, error)。磁盘刚满、内核缓存未刷、网络文件系统等场景下,可能只写入部分数据并返回 err == nil —— 这是合法行为,但极易被忽略:
- 只判
err != nil不够:Write()可能返回n = 0, err = nil,后续再写才报ENOSPC - 关键路径建议用
io.WriteString()替代裸Write(),它内部已做补全逻辑 - 对
io.Copy,需在自定义io.Writer的Write方法里主动检查累计写入量是否已达阈值,及时返回ENOSPC
真正难处理的不是“怎么报错”,而是“报错之后要不要继续写”
日志系统在磁盘满时该降级到内存缓冲、切到备用存储,还是直接 panic?这取决于你的 SLA,而不是技术能不能做到。比如:
- 监控告警类日志:可丢弃或暂存内存 ring buffer,避免阻塞主流程
- 事务型写入(如数据库 WAL):必须严格失败,不能降级,否则一致性受损
- 下载器写入(如 Gopeed):应暂停任务、提示用户清理空间,而非静默失败
这些决策点往往藏在错误处理分支最深处,容易被当成“先随便打个 log”跳过 —— 但线上出问题时,最先暴露的恰恰是这里。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










