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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: python 使用 watchdog 实现类似 Linux 中 tail -f 的功能

一、代码实现


import logging
import os
import threading
import time
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
logger = 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_file
        self.file_object = open(log_file, 'rb')
        self.on_modified_callback = on_modified_callback
        self.last_line = self.get_last_line()  # 初始化时获取最后一行文本
        self.observer = Observer()
        self.observer.schedule(self, ".", recursive=False)
        self.match_string = None
        self.stop_watching = False
    def 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:
            # 如果发生错误,可能是文件太小,返回None
            return None
        # 逐个字符向前搜索,确保文件指针最终停在当前行的第一个字符处
        while True:
            # read(1)读取的是指针位置的下一个字符,每次调用read(1)都会读取一个字符,并将指针向后移动一个字符的位置。
            char = self.file_object.read(1).decode('utf-8', errors='ignore')
            if char == '\n':
                break
            if 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_line
    def 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_string
        self.start()
        end_time = time.time() + max_match_seconds
        try:
            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 = True
        except KeyboardInterrupt:
            pass
        self.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 logging
    logging.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 9
Process finished with exit code 0
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
1月前
|
开发框架 数据建模 中间件
Python中的装饰器:简化代码,增强功能
在Python的世界里,装饰器是那些静悄悄的幕后英雄。它们不张扬,却能默默地为函数或类增添强大的功能。本文将带你了解装饰器的魅力所在,从基础概念到实际应用,我们一步步揭开装饰器的神秘面纱。准备好了吗?让我们开始这段简洁而富有启发性的旅程吧!
37 6
|
2月前
|
IDE 数据挖掘 开发工具
Python作为一种广受欢迎的高级编程语言,以其简洁的语法和强大的功能吸引了众多初学者和专业开发者
Python作为一种广受欢迎的高级编程语言,以其简洁的语法和强大的功能吸引了众多初学者和专业开发者
41 7
|
18天前
|
Linux Python
Linux 安装python3.7.6
本教程介绍在Linux系统上安装Python 3.7.6的步骤。首先使用`yum`安装依赖环境,包括zlib、openssl等开发库。接着通过`wget`下载Python 3.7.6源码包并解压。创建目标文件夹`/usr/local/python3`后,进入解压目录执行配置、编译和安装命令。最后设置软链接,使`python3`和`pip3`命令生效。
|
4天前
|
安全 前端开发 数据库
Python 语言结合 Flask 框架来实现一个基础的代购商品管理、用户下单等功能的简易系统
这是一个使用 Python 和 Flask 框架实现的简易代购系统示例,涵盖商品管理、用户注册登录、订单创建及查看等功能。通过 SQLAlchemy 进行数据库操作,支持添加商品、展示详情、库存管理等。用户可注册登录并下单,系统会检查库存并记录订单。此代码仅为参考,实际应用需进一步完善,如增强安全性、集成支付接口、优化界面等。
|
13天前
|
监控 安全 Linux
启用Linux防火墙日志记录和分析功能
为iptables启用日志记录对于监控进出流量至关重要
|
1月前
|
测试技术 Python
探索Python中的装饰器:简化代码,增强功能
在Python的世界中,装饰器是那些能够为我们的代码增添魔力的小精灵。它们不仅让代码看起来更加优雅,还能在不改变原有函数定义的情况下,增加额外的功能。本文将通过生动的例子和易于理解的语言,带你领略装饰器的奥秘,从基础概念到实际应用,一起开启Python装饰器的奇妙旅程。
41 11
|
1月前
|
Python
探索Python中的装饰器:简化代码,增强功能
在Python的世界里,装饰器就像是给函数穿上了一件神奇的外套,让它们拥有了超能力。本文将通过浅显易懂的语言和生动的比喻,带你了解装饰器的基本概念、使用方法以及它们如何让你的代码变得更加简洁高效。让我们一起揭开装饰器的神秘面纱,看看它是如何在不改变函数核心逻辑的情况下,为函数增添新功能的吧!
|
2月前
|
设计模式 监控 程序员
Python中的装饰器:功能增强与代码复用的利器####
本文深入探讨了Python中装饰器的工作原理、应用场景及其在提升代码可读性、减少重复劳动方面的优势。不同于传统方法的冗长和复杂,装饰器提供了一种优雅且高效的方式来增强函数或方法的功能。通过具体实例,我们将揭示装饰器如何简化错误处理、日志记录及性能监控等常见任务,使开发者能够专注于核心业务逻辑的实现。 ####
|
2月前
|
机器人 计算机视觉 Python
Python作为一种高效、易读且功能强大的编程语言,在教育领域的应用日益广泛
Python作为一种高效、易读且功能强大的编程语言,在教育领域的应用日益广泛
56 5
|
2月前
|
开发框架 缓存 测试技术
Python中的装饰器:魔法般的功能增强
在Python编程中,装饰器是一种强大而灵活的工具,它允许开发者修改或扩展函数和类的行为。本文将深入探讨Python装饰器的工作原理,并通过实例演示如何创建和使用自定义装饰器来增强代码的功能性和可读性。我们将从基础概念讲起,逐步深入到高级应用,揭示装饰器背后的“魔法”,并展示它们在实际开发中的多种用途。

热门文章

最新文章