python 使用 watchdog 实现类似 Linux 中 tail -f 的功能

一、代码实现

import logging
import os
import threading
import timefrom watchdog.events import FileSystemEventHandler
from watchdog.observers import Observerlogger = logging.getLogger(__name__)class LogWatcher(FileSystemEventHandler):def __init__(self, log_file, on_modified_callback=None):""""初始化 LogWatcher 类的实例。参数:- log_file:日志文件的路径- on_modified_callback:可选的回调函数,在文件修改时调用属性:- log_file:日志文件的路径- file_object:日志文件对象- on_modified_callback:文件修改回调函数- last_line:最后一行文本- observer:观察者对象- match_string:需要匹配的字符串- stop_watching:停止监视的标志"""self.log_file = log_fileself.file_object = open(log_file, 'rb')self.on_modified_callback = on_modified_callbackself.last_line = self.get_last_line()  # 初始化时获取最后一行文本self.observer = Observer()self.observer.schedule(self, ".", recursive=False)self.match_string = Noneself.stop_watching = Falsedef start(self):"""启动观察者对象,开始监视文件变化。"""self.observer.start()def stop(self):"""停止观察者对象,结束监视文件变化。"""self.observer.stop()self.observer.join()self.file_object.close()def get_last_line(self):"""获取日志文件的最后一行文本。它通过将文件指针移动到文件末尾,然后逐个字符向前搜索,直到找到换行符为止。返回值:- 最后一行文本,如果文件为空则返回None"""# 将文件指针移动到文件末尾self.file_object.seek(0, os.SEEK_END)# 获取当前文件指针的位置(此时指针在最后一行的末尾)position = self.file_object.tell()try:# 尝试向前移动两个字节new_position = max(position - 2, 0)self.file_object.seek(new_position, os.SEEK_SET)except OSError as e:# 如果发生错误,可能是文件太小,返回Nonereturn None# 逐个字符向前搜索,确保文件指针最终停在当前行的第一个字符处while True:# read(1)读取的是指针位置的下一个字符,每次调用read(1)都会读取一个字符,并将指针向后移动一个字符的位置。char = self.file_object.read(1).decode('utf-8', errors='ignore')if char == '\n':breakif new_position == 0:# 如果已经到达文件开头,跳出循环break# 尝试向前移动一个字节位置,确保不越界到文件开头new_position = max(new_position - 1, 0)# 将文件指针移动到新的位置self.file_object.seek(new_position, os.SEEK_SET)# last_line = self.file_object.readline().decode('utf-8', errors='ignore').strip()last_line = self.file_object.read(position - new_position).decode('utf-8', errors='ignore').strip()# 输出调试信息logger.debug(f'Reading line: {last_line}')return last_linedef on_modified(self, event):"""on_modified方法是FileSystemEventHandler的回调方法,当日志文件发生变化时,都会调用这个方法。参数:- event:文件变化事件对象"""# 注意,这里一个要用绝对路径比较,不能直接使用 event.src_path == self.log_file,# event.src_path == self.log_file 的值为false# if event.src_path == self.log_file:if os.path.abspath(event.src_path) == os.path.abspath(self.log_file):# 在文件发生变化时,实时获取最后一行文本self.last_line = self.get_last_line()# 用户可在外部传入一个回调方法,在文本发生变化时执行该事件if self.on_modified_callback:self.on_modified_callback()# 调用基类的同名方法,以便执行基类的默认行为super(LogWatcher, self).on_modified(event)def tail_last_line_and_match(self, match_string=None, max_match_seconds=10):"""实时监控日志文件的变化,并实时获取最后一行文本。如果匹配到指定的字符串,停止监视。参数:- match_string:需要匹配的字符串"""self.match_string = match_stringself.start()end_time = time.time() + max_match_secondstry:while not self.stop_watching and time.time() <= end_time:if self.match_string and self.match_string in self.last_line:self.stop_watching = Trueexcept KeyboardInterrupt:passself.stop_watching = True  # 停止监视循环def write_logs(log_file):"""在新线程中写入日志"""for i in range(10):with open(log_file, 'a') as file:file.write(f'New log entry {i}\n')time.sleep(1)  # 每秒写入一次日志if __name__ == '__main__':import logginglogging.basicConfig(level=logging.DEBUG)log_file = 'demo.log'# 创建日志文件并写入示例日志with open(log_file, 'w') as file:file.write('This is the first line of the log.\n')file.write('This is the second line of the log.\n')log_watcher = LogWatcher(log_file)# 启动新线程写入日志write_thread = threading.Thread(target=write_logs, args=(log_file,))write_thread.start()# 启动实时监控日志文件变化,并打印最后一行文本,直到匹配到指定字符串或超时才停止监视log_watcher.tail_last_line_and_match(match_string='New log entry 9', max_match_seconds=20)# 等待写入线程结束write_thread.join()

三、Demo验证

运行代码,控制台的输出结果:

DEBUG:__main__:Reading line: This is the second line of the log.
DEBUG:__main__:Reading line: New log entry 0
DEBUG:__main__:Reading line: New log entry 1
DEBUG:__main__:Reading line: New log entry 2
DEBUG:__main__:Reading line: New log entry 3
DEBUG:__main__:Reading line: New log entry 4
DEBUG:__main__:Reading line: New log entry 5
DEBUG:__main__:Reading line: New log entry 6
DEBUG:__main__:Reading line: New log entry 7
DEBUG:__main__:Reading line: New log entry 8
DEBUG:__main__:Reading line: New log entry 9Process finished with exit code 0

欢迎技术交流:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/259316.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式——单例模式(Singleton Pattern)

概述 单例模式确保一个类只有一个实例&#xff0c;而且自行实例化并向整个系统提供整个实例&#xff0c;这个类称为单例类&#xff0c;它提供全局访问的方法。单例模式是一种对象创建型模式。单例模式有三个要点&#xff1a;一是某个类只能有一个实例&#xff1b;二是它必须自行…

2023年8月14日 Go生态洞察:向后兼容性、Go 1.21与Go 2

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

python3.5安装教程及环境配置,python3.7.2安装与配置

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;python3.5安装教程及环境配置&#xff0c;python3.7.2安装与配置&#xff0c;现在让我们一起来看看吧&#xff01; python 从爬虫开始&#xff08;一&#xff09; Python 简介 首先简介一下Python和爬虫的关系与概念&am…

Qt12.8

使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否为"admin"&#xff0c;密码是否为…

智能优化算法应用:基于食肉植物算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于食肉植物算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于食肉植物算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.食肉植物算法4.实验参数设定5.算法结果6.参考…

9.关于Java的程序设计-基于Springboot的家政平台管理系统设计与实现

摘要 随着社会的进步和生活水平的提高&#xff0c;家政服务作为一种重要的生活服务方式逐渐受到人们的关注。本研究基于Spring Boot框架&#xff0c;设计并实现了一种家政平台管理系统&#xff0c;旨在提供一个便捷高效的家政服务管理解决方案。系统涵盖了用户注册登录、家政服…

论文精读 MOG 埃里克·格里姆森

Adaptive background mixture models for real-time tracking 用于实时跟踪的自适应背景混合模型 1999年的MOG&#xff0c;作者是麻省理工学院前校长——埃里克格里姆森&#xff08;W. Eric L. Grimson&#xff09;。 目录 摘要 结论 1 介绍 1.1 以往的工作和现在的缺点 …

扁平的MutableList元素每隔若干元素一组装入新MutableList,Kotlin

扁平的MutableList元素每隔若干元素一组装入新MutableList&#xff0c;Kotlin fun main(args: Array<String>) {val array arrayOf("a", "b", "c", "d", "e", "f", "g", "h", "i…

期末速成数据库极简版【查询】(2)

目录 select数据查询----表 【1】筛选列 【2】where简单查询 【3】top-n/distinct/排序的查询 【4】常用内置函数 常用日期函数 常用的字符串函数 【5】模糊查询 【6】表数据操作——增/删/改 插入 更新 删除 【7】数据汇总 聚合 分类 ​ &#x1f642;&#…

【ET8】2.ET8入门-ET框架解析

菜单栏相关&#xff1a;ENABLE_DLL选项 ET->ChangeDefine->ADD_ENABLE_DLL/REMOVE_ENABLE_DLL 一般在开发阶段使用Editor时需要关闭ENABLE_DLL选项。该选项关闭时&#xff0c;修改脚本之后&#xff0c;会直接重新编译所有的代码&#xff0c;Editor在运行时会直接使用最…

寒冬不再寒冷:气膜体育馆如何打造温馨运动天地

取暖季即将来临&#xff0c;随着气温逐渐下降&#xff0c;人们在寒冷的冬季里如何保持运动热情和身体的健康成为了一项挑战。而在这个时候&#xff0c;气膜体育馆成为了运动爱好者们的理想场所&#xff0c;提供如春般温暖舒适的运动环境。那么&#xff0c;让我们一起揭秘气膜体…

CMake ‘3.10.2‘ was not found in PATH or by cmake.dir property.

在部署Yolov5到安卓端的过程中出现&#xff1a;CMake ‘3.10.2’ was not found in PATH or by cmake.dir property. 原因&#xff1a; cmake版本太高&#xff0c;需要安装低版本的cmake 最开始下载的是默认最高版本的cmake,默认是3.22.1&#xff0c;解决方案是&#xff0c;下载…