监听文件需改监父目录并过滤文件名,事件后延迟100ms或双检size确认变化,缓存lastknownsize防重复报警,阈值用绝对值+百分比组合。

直接监听文件路径会静默失败,必须监听父目录
fsnotify 不支持对普通文件的直接监控,watcher.Add("/var/log/app.log") 在大多数编辑器保存、logrotate 重命名等场景下完全不触发事件。这是因为原子写入(先写临时文件再 rename)会让原文件 inode 失效,监听句柄随之作废。
正确做法是监听其所在目录,并在事件循环中过滤文件名:
- 调用
watcher.Add(filepath.Dir("/var/log/app.log")),确保该路径存在且可读(os.Stat校验) - 收到事件后,用
filepath.Base(event.Name)提取文件名,再比对是否等于"app.log" - 必须同时检查
event.Op & fsnotify.Write != 0和event.Op & fsnotify.Rename != 0,不能只判== fsnotify.Write - Windows 下
Rename常被拆成Remove + Create,所以单靠事件类型不够,需配合os.Stat确认文件存在且大小 > 0
判断“大小超标”不能只看单次事件,要主动读取并缓存状态
Write 事件只表示有写入发生,不代表最终大小;Rename 事件虽原子,但目标文件可能还没 flush 完。直接在事件回调里调 os.Stat(event.Name) 可能拿到旧 size,尤其在高 I/O 负载下。
稳妥做法是:收到 Rename 或 Write 后,启动一个轻量检查任务,而不是立刻报警:
- 用
time.AfterFunc(100 * time.Millisecond, func() { ... })延迟 100ms 再读os.Stat,避免读到中间状态 - 或更可靠:连续两次读
os.Stat().Size(),间隔 50ms,确认值不再变化 - 把最近一次确认的 size 缓存为
lastKnownSize,只在新值 > 阈值 且 >lastKnownSize时才触发逻辑,防止重复报警 - 阈值建议设为绝对值(如 100MB)+ 百分比(如挂载点可用空间
报警前必须校验挂载点真实可用空间,否则可能误杀
文件变大本身不是问题,真正危险的是写满磁盘导致服务崩溃。但 os.Stat 拿不到挂载点级剩余空间,必须调用 disk.Usage() 或 syscall.Statfs。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
关键约束和坑:
-
disk.Usage()必须传挂载点路径(如"/var/log"),不能传文件路径或设备名("/dev/sda1"会返回ErrNotFound) - Windows 下必须传
"C:"(带冒号),且需提前filepath.EvalSymlinks解析符号链接,否则可能查错卷 - 别用
usage.Percent做判断——它含 root 保留空间(默认 5%),真正可用的是usage.Available - 计算剩余百分比用:
float64(usage.Available) / float64(usage.Total) * 100,不是(usage.Total - usage.Free) / usage.Total - 每次调用加
context.WithTimeout(ctx, 3*time.Second),防止 NFS 挂起阻塞整个监控流程
上报报警要防堆积、防丢失、防重复
日志轮转或批量写入时,短时可能爆发多个“超标”事件,但告警系统吞吐有限。不做控制会导致 HTTP 超时、连接泄漏或下游丢数据。
实操要点:
- 用带缓冲 channel(如
make(chan string, 50))暂存待上报路径,监听循环绝不阻塞 - 上报 goroutine 必须实现指数退避:
time.Sleep(100 * time.Millisecond)→ 翻倍 → 上限30 * time.Second - HTTP 请求必设
context.WithTimeout(ctx, 5*time.Second),失败时记录本地 fallback 文件(而非 panic) - 同一文件在 5 分钟内只报一次,用
map[string]time.Time记最后报警时间,查之前先清理过期项 - 输出 JSON 必须含 RFC3339 时间戳、挂载点、当前文件 size、挂载点
Available字节数,字段全小写,如{"timestamp":"2026-06-19T13:57:00Z","file":"/var/log/app.log","size_bytes":107374182400,"mountpoint":"/var/log","available_bytes":8589934592}
真正容易被忽略的是:Available 字段在某些文件系统(如 XFS 与特定内核版本组合)下行为不一致,上线前必须在目标环境实测验证,不能只信文档。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










