直接用 while True + readline() 会卡住日志末尾,因 readline() 到达 EOF 后立即返回空字符串,不会自动阻塞等待新内容;需结合 os.stat().st_size 检测文件增长、time.sleep() 避免空转,并用 tell()/seek() 精确维护字节偏移以处理追加、截断或轮转。
为什么直接用
+
会卡住日志末尾
因为文件对象在首次到达 EOF 后,
不会自动等待新内容,而是立刻返回空字符串。它不会像 shell 的
那样阻塞并监听文件增长。你得自己判断“到底是不是真结束了”,再决定要不要暂停、重试或重新定位。
关键点在于:必须结合
检查文件是否变长,并在无新数据时用
避免空转;同时要用
记录读取位置,防止轮询时重复输出或跳过行。
每次循环开始前先
获取当前大小
如果当前大小 > 上次记录的
,说明有新增,
回退到上次位置再逐行读
读完后更新
,不是用
累加——换行符长度不固定,且可能跨块
必须在
时指定
(行缓冲)或
(无缓冲),否则
可能因内部缓存读不到刚写入的内容
如何正确用
和
维护读取偏移
返回的是字节偏移,不是行号。日志追加是字节流操作,所以这个值可直接用于
定位。但要注意:如果文件被截断(如 logrotate),
可能大于当前文件大小,此时需重置为 0 并 warn。
典型处理逻辑:
立即学习
“
Python免费学习笔记(深入)
”;
初始化时,若文件存在且非空,
移动到末尾,
每次检查
后,若
只在
时才
,然后循环
直到返回空字符串
每成功读一行,立即处理(如 print 或发到队列),再更新
遇到
怎么办
这通常发生在日志文件被外部程序(如
)删除又重建同名文件时。原文件描述符仍指向已删除的 inode,但新文件是另一个 inode,
看到的是新 size,而
在旧 fd 上会失败。
Python 3.14.3
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
下载
安全做法是捕获
/
,关闭旧文件,重新
,并从头或末尾开始读(取决于需求):
用
包裹读取逻辑
在 except 块中
,然后
重新
到新文件末尾,更新
不要假设“只是暂时不可读”,必须重建文件对象
Python 3.8+ 可用
替代轮询吗
可以,但不是万能替代。
基于 inotify/kqueue,能精确感知文件修改事件,避免 sleep 轮询开销。但它只告诉你“变了”,不告诉你“变在哪”——你仍需用
/
配合
找出新增行,否则可能漏掉同一秒内多次写入的多行。
而且
无法感知截断(truncate),也无法区分是追加还是覆盖。所以生产环境建议:用
触发检查,再用
+
精确读新内容,二者组合更稳。
真正容易被忽略的是:所有基于
的方案都依赖文件不被其他进程以
方式打开。一旦发生,偏移就失效——这时候没捷径,只能靠定期全量校验或引入文件 inode 比对。
while Truereadline()readline()tail -fos.stat().st_sizetime.sleep()file.tell()os.stat(filepath).st_sizefile.tell()file.seek(last_pos)last_pos = file.tell()len(line)open()buffering=10readline()tell()seek()tell()seek()last_posfile.seek(0, 2)last_pos = file.tell()st_sizest_size ,说明文件被清空或轮转,打印警告并设 last_pos = 0st_size > last_posfile.seek(last_pos)readline()last_posIOError: [Errno 5] Input/output errorlogrotatestat()readline()OSErrorIOErroropen()try/except (OSError, IOError)file.close()file = open(filepath, 'r', buffering=1)file.seek(0, 2)last_poswatchfileswatchfiles.watch()tell()seek()readline()watchfileswatchfilestell()seek()tell()O_TRUNC