Python实现日志实时监测的示例详解

江屿
江屿
发布时间:2026-10-15 | 2人浏览过
原创

本文详解Python中观察者模式(发布-订阅模式)的应用,通过构建发布者和订阅者类实现日志实时监测。示例展示了如何注册短信、邮件及日志订阅者,并在地图加载失败等异常发生时自动触发通知机制,实现松耦合的事件处理。

介绍

观察者模式属于行为型设计模式,核心在于管理对象间的责任关系。它定义了一种订阅机制,允许在对象状态发生变化或触发特定事件时,通知所有注册了该对象的“观察者”。这种模式主要用于处理对象之间的交互逻辑。

观察者模式亦被称为发布-订阅模式,它确立了对象间一对多的依赖关系。当被观察的对象(发布者)状态发生改变时,所有依赖于此的对象(订阅者)都会收到通知,并依据各自的逻辑进行更新。

作为最简单的行为模式之一,观察者设计模式包含一个维护依赖列表的对象(即主题)。主题通过该列表,利用观察者定义的方法,向所有注册的观察者广播其所发生的变化。

观察者模式的应用场景

在广播系统或发布-订阅系统中,观察者设计模式的应用十分广泛,其主要适用场景包括:

1、在分布式系统中实现事件服务。

2、广播或发布/订阅系统场景。

3、用作新闻分发系统的框架。

4、股票行情监测机器人。

观察者模式类图

观察者模式类图

1、发布者(Publisher):负责向其他对象发送值得关注的信息。事件通常在发布者状态改变或执行特定行为后发生。发布者内部包含订阅机制,允许新订阅者加入或现有订阅者退出列表。

2、订阅者(Subscriber):定义通知接口。通常该接口仅包含一个update()更新方法。该方法可接受多个参数,以便发布者在更新时传递详细的事件信息。

3、客户端(Client):负责分别创建发布者和订阅者对象,随后为订阅者进行注册,并触发发布者的更新操作。

观察者模式示例

假设我们需要对应用函数的运行状态进行监测,当发生异常时进行报警记录,可以通过观察者模式实现信息订阅,例如:1、短信通知 2、日志记录 3、邮件发送。

代码实现---subscription_model.py

1、创建订阅者类

Subscriber(订阅者):所有希望关注发布者状态变化的其他对象。

此处提供了三个主要的订阅者(观察者)接口,它们跟踪同一个发布者类的事件。主要包括:

1)、每个具体订阅者的__init__()方法使用attach()方法向发布者注册,以获取信息更新。

2)、具体订阅者的update()方法用于接收更新消息。

#抽象订阅者
from abc import ABCMeta,abstractmethod
class Subscriber(metaclass=ABCMeta):
    #向具体订阅者发送消息的方法
    @abstractmethod
    def update(self):
        pass

#具体订阅者
#1、短信订阅者
class SMSSubscriber(Subscriber):
    def __init__(self,publisher):
        self.publisher = publisher
        self.publisher.attach(self)

    def update(self):
        print(type(self).__name__,self.publisher.getNews())


#2、邮件订阅者
class EmailSubscriber(Subscriber):
    def __init__(self, publisher):
        self.publisher = publisher
        self.publisher.attach(self)

    def update(self):
        print(type(self).__name__,self.publisher.getNews())
        info = self.publisher.getNews()
        # 发送邮件
        Sender_mail(info).sender_mail()

#3、日志订阅(文件存储)
class LoggerSubscriber(Subscriber):

    def __init__(self, publisher):
        log_dir = os.path.expanduser(r".\apps\Mapview\logs")
        log_file = os.path.join(log_dir, "file_{time}.log")
        logger.add(log_file, rotation="100KB", retention=2)
        self.publisher = publisher
        self.publisher.attach(self)

    def update(self):
        print(type(self).__name__,self.publisher.getNews())
        info=self.publisher.getNews()
        logger.info(f"{info}")

2、创建发布者类

Publisher(发布者):将自身的状态改变通知其他对象,并为发布者添加订阅机制,使得每个对象都能订阅或取消订阅事件流。

主要包括:

1)self.__subscribers = []:用于存储订阅对象列表。

2)供订阅者调用NewsPublisher来注册或删除订阅用户。

3)几个用于添加、删除或查看列表中订阅者的公有方法。

4)notifySubscribers(self):用于通知所有订阅者出现新信息。发送者会遍历订阅列表,并通过内部调用具体订阅者实现的update()方法来实现通知。

5)创建新消息和返回最新消息的方法。

#创建发布者
class NewsPublisher:
    def __init__(self):
        self.__subscribers = []
        self.__latestNews = None
    
    #将订阅者添加到队列中
    def attach(self,subscriber):
        self.__subscribers.append(subscriber)
    
    #从订阅的主题里面移除
    def detach(self):
        return self.__subscribers.pop()

    #生成观察者列表
    def subscribers(self):
        return [type(x).__name__ for x in self.__subscribers]

    #发送通知给相关的主题订阅者
    def notifySubscribers(self):
        for sub in self.__subscribers:
            #update()方法由具体的观察者或订阅者实现的
            sub.update()  #推送更新
    
    #创建新消息
    def addNews(self,news):
        self.__latestNews = news
    
    #返回最新消息,并通知观察者
    def getNews(self):
        return "Got News:",self.__latestNews

3、应用客户端-Map_server_client.py

订阅者通常需要一些上下文信息来正确处理更新。因此,发布者通常会将一些上下文数据作为通知方法的参数传递。

这里补充第一篇文章中遗留的内容,客户端实例化get_Map_model方法时添加带参数装饰器@fail_data(msg='地图加载失败')以添加接口调用失败处理机制,并追加日志记录。这里可以进一步将更多细节参数添加到日志中,通过装饰器传参并在接口中声明通知方法及参数,这样发布者在发出通知时即可传递一些上下文数据。

from apps.tools.subscription_model import NewsPublisher,LoggerSubscriber,EmailSubscriber
import functools


#如果加载失败,调用订阅者
def publisher(info):
    news_publisher = NewsPublisher()
    # for Subscribers in [EmailSubscriber, LoggerSubscriber]:
    for Subscribers in [LoggerSubscriber]:
        # eval(LoggerSubscriber)(news_publisher)
        Subscribers(news_publisher)
        print("\nSubscribers", news_publisher.subscribers())
        news_publisher.addNews(f"{info}")
        news_publisher.notifySubscribers()


#处理异常的装饰器
def fail_data(msg='地图加载失败'):
    def catch_exception(origin_func):
        @functools.wraps(origin_func)
        def wrapper(*args, **kwargs):
            try:
                u = origin_func(*args, **kwargs)
                print("这个函数正常执行:%s" % origin_func.__name__)
                return u

            except Exception as e:
                info = f"{msg}:{e.__doc__}"
                """
                接口调用失败处理机制,追加日志
                """
                print(info)
                publisher(info)
                # news_publisher = NewsPublisher()
                # LoggerSubscriber(news_publisher)
                # print("\nSubscribers", news_publisher.subscribers())
                # news_publisher.addNews(f"{info}")
                # news_publisher.notifySubscribers()
        return wrapper
    return catch_exception

4、测试

if __name__ == '__main__':

    from loguru import logger
    from apps.tools.Sender_Email import Sender_mail

    news_publisher =NewsPublisher()
    for Subscribers in [LoggerSubscriber]:
        print(Subscribers)
        Subscribers(news_publisher)

    print("\nSubscribers",news_publisher.subscribers())

    news_publisher.addNews("地图加载失败!")
    news_publisher.notifySubscribers()

结果

class '__main__.LoggerSubscriber'; Subscribers ['LoggerSubscriber'] LoggerSubscriber ('Got News:', '地图加载失败!') 2022-04-05 16:38:00.667 | INFO | __main__:update:81 - ('Got News:', '地图加载失败!')

以上实现了一个简单的发布订阅模式,发布者与订阅者之间是松耦合的,添加新订阅者无需修改发布者。所有具体订阅者类都实现了同样的接口。

到此这篇关于Python实现日志实时监测的示例详解的文章就介绍到这了,更多相关Python日志监测内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表