fsnotify 不监听文件读取操作,仅响应写入、创建、删除、重命名等内核级文件系统事件;读取不触发事件是操作系统设计共识,无法绕过,需通过业务层埋点、进程句柄检查或上下游契约解决。

fsnotify 不监听文件读取状态,它只响应写入、创建、删除、重命名等**内核级文件系统事件**;读取操作(如 os.Open、io.Copy)不会触发任何 fsnotify 事件,这是由操作系统设计决定的,无法绕过。
如果你看到“监听文件读取”的需求,实际要解决的通常是以下三类问题之一:
为什么 fsnotify.Add("file.log") 收不到 Read 事件
fsnotify 根本不提供 Read 事件类型——Linux inotify、macOS FSEvents、Windows ReadDirectoryChangesW 全都不上报读操作。这不是 Go 实现缺陷,而是所有主流 OS 的共识:读取不改变文件元数据或结构,不构成可观测的“变更”。
- 试图用
event.Op & fsnotify.Read判断会永远失败,因为fsnotify.Read不存在(编译报错) - 某些日志轮转场景中误以为“被读取”=“被消费”,其实是下游服务主动
os.Stat或os.Open导致,需在业务层埋点,而非依赖文件系统事件 - 若真需感知读取,唯一可行方案是用
LD_PRELOAD(Linux)或 API 钩子(Windows)劫持open()/CreateFile()系统调用——但这属于侵入式监控,不适用于普通 Go 应用
如何间接判断文件是否正被读取(如 tail -f 场景)
当目标是确认某个日志文件是否正被其他进程持续读取(例如 tail -f),可结合 lsof 或 /proc/*/fd/ 检查句柄,而非监听事件:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- Linux 下执行
lsof -Pn +D /path/to/dir 2>/dev/null | grep 'REG.*r$',匹配标记为r(read-only)的普通文件句柄 - 更轻量做法:遍历
/proc/*/fd/,对每个符号链接readlink,检查是否指向目标文件路径(注意需 root 权限或同用户) - Go 中可用
exec.Command("sh", "-c", "lsof -t -w -n -F p /path/to/file 2>/dev/null | head -1")获取占用进程 PID,但稳定性差,仅作调试参考 - 生产环境应避免轮询
lsof,推荐让读取方主动上报心跳(如 HTTP ping 端点),或改用日志采集 agent(Filebeat、Fluent Bit)自带的状态反馈机制
误把“读取完成”当成“写入完成”的典型陷阱
很多用户想“等文件被读完再处理”,实际混淆了两个独立动作:写入结束 ≠ 读取开始 ≠ 读取结束。常见错误逻辑:
- 监听
Write事件后立刻os.Open→ 可能读到截断内容(大文件写入未完成) - 收到
Rename就认为“已就绪”,但下游 reader 可能还没启动 → 此时无读取行为,也无事件可监听 - 试图用
os.Stat().Size()是否稳定来判断“是否读完” → 错误:Size 不变只说明写入停了,不代表有人在读或读完了 - 真正可控的信号只有写入端提供的语义:原子
Rename表示写入完成;读取端需自行协调(如通过 socket 通知、共享内存 flag、或约定文件末尾写入特定 marker)
归根结底,文件读取不可监听是操作系统边界,强行突破代价远高于收益。与其纠结“谁在读”,不如明确上下游契约:写入方负责发出就绪信号(Rename / marker / HTTP webhook),读取方负责按需拉取并反馈进度。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










