watchdog基于系统原生事件接口实现低开销、高响应的文件增删改监听,跨平台且维护活跃;需注意路径存在性、递归设置、事件过滤时机、线程阻塞规避、modified去重、moved优先、inode判重及部署权限与守护进程管理。

用 watchdog 监听文件增删改,不是轮询
轮询(比如定时 os.listdir())既耗资源又延迟高,watchdog 是基于操作系统原生事件接口(Linux inotify、macOS FSEvents、Windows ReadDirectoryChangesW)的封装,响应快、低开销。安装后直接监听路径,不需自己解析系统调用。
实操建议:
- 用
pip install watchdog安装,别用pyinotify(仅 Linux)或macfsevents(仅 macOS),watchdog跨平台且维护活跃 - 监听目录必须存在,
recursive=True才能捕获子目录事件;若只关心某类文件(如.log),在回调里加if event.src_path.endswith('.log'):过滤,别依赖patterns参数——它只过滤初始扫描,不拦截事件分发 - 事件对象
event的event.is_directory和event.event_type('created'、'modified'、'deleted'、'moved')是判断动作的核心依据
FileSystemEventHandler 里别阻塞主线程
所有事件回调(如 on_created)都在同一个线程中同步执行,如果在里面跑一个耗时脚本(比如调用 subprocess.run(['python', 'task.py'])),后续事件会排队等待,导致漏事件或延迟飙升。
实操建议:
- 用
threading.Thread(target=your_task, args=(event,)).start()启动新线程处理,避免阻塞事件循环 - 若任务需顺序执行(如日志归档不能并发),改用
queue.Queue+ 单独工作线程消费,主线程只负责入队 - 注意线程安全:不要在回调里直接修改共享变量(如计数器),用
threading.Lock或queue.Queue
处理 modified 事件要防重复触发
编辑器保存文件时,常先清空再写入,触发多次 modified;或某些程序(如 Vim)会先写临时文件再重命名,导致 created + deleted + moved 组合出现。直接对每个 modified 都执行脚本,可能重复处理同一份数据。
实操建议:
- 对同一文件路径加时间窗口去重:用
time.time()记录上次处理时间,if time.time() - last_modified.get(path, 0) > 1.0:再执行 - 优先监听
moved事件而非modified——很多编辑器最终是原子重命名,更可靠 - 避免用
event.src_path做唯一键,因为软链接或挂载点可能导致路径不一致;可结合os.stat(event.src_path).st_ino(inode)辅助判重(注意 Windows 不稳定)
部署时注意权限、路径和守护进程问题
开发时在终端运行没问题,但放到后台常出问题:路径变成相对路径失效、用户权限不足读不到目标目录、进程被系统回收。
实操建议:
- 所有路径用绝对路径,初始化时用
os.path.abspath()转换,别依赖os.getcwd() - Linux 下用
systemd管理服务:写.service文件,指定User=和WorkingDirectory=,加上Restart=always - 避免用
nohup或&启动——没有进程管理,崩溃后不会自拉起;也不要用screen,它本质还是交互式会话,容易断开
真正难的不是监听到事件,而是确保每次事件都干净、不丢、不重、不卡住。尤其当监控的是日志目录或上传目录时,文件高频写入+编辑器行为差异+跨平台兼容性,会让简单逻辑迅速变得脆弱。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











