You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建复用同一Logger实例的Python函数装饰器?该场景是否有现成设计模式?

复用单一Logger实例的装饰器实现与对应设计模式

咱们先解决核心问题:怎么确保整个项目里的Logger实例是同一个,同时用装饰器给函数加日志功能。其实你给的示例已经摸到门道了——Python的logging.getLogger()本身就会返回单例实例(logging模块内部维护了一个注册表,同名的logger只会创建一次),不过如果要更稳妥地控制实例唯一性,咱们可以用更显式的方式实现,再结合对应的设计模式。

一、正确实现:全局单例Logger的装饰器

首先,咱们可以用模块级单例或者显式单例类来确保Logger实例唯一。这里给两种实用的实现方式:

方式1:利用Python模块的天然单例特性

Python模块在首次导入时会被加载,后续所有导入都是引用同一个模块对象,所以直接在模块里定义Logger实例就行,简单又可靠:

import logging

# 模块级初始化,只会执行一次,全局复用这个实例
logger = logging.getLogger('project_global_logger')
# 统一配置日志格式、级别等(可以根据需求调整)
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s: %(message)s')
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)

def log_action(log_string=''):
    def decorator(func):
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            # 直接复用上面的全局logger实例
            logger.info(f"函数 {func.__name__}: {log_string}")
            return result
        return wrapper
    return decorator

# 使用示例
class MyClass:
    @log_action('Action started')
    def action(self):
        pass

@log_action('独立函数执行完成')
def my_func():
    pass

不管你在项目的哪个模块导入这个装饰器,logger都是同一个实例,完全满足需求。

方式2:显式实现单例类管理Logger

如果你想更清晰地控制单例逻辑(比如防止意外修改logger实例),可以写一个单例类来封装Logger,明确告诉其他开发者这个实例是全局唯一的:

import logging

class SingletonLogger:
    _instance = None

    def __new__(cls, logger_name='project_global'):
        if cls._instance is None:
            cls._instance = logging.getLogger(logger_name)
            # 统一配置日志
            cls._instance.setLevel(logging.INFO)
            formatter = logging.Formatter('%(asctime)s - %(levelname)s: %(message)s')
            cls._instance.addHandler(logging.StreamHandler())
            cls._instance.handlers[0].setFormatter(formatter)
        return cls._instance

# 获取全局唯一实例
global_logger = SingletonLogger()

def log_action(log_string=''):
    def decorator(func):
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            global_logger.info(f"[{func.__name__}] {log_string}")
            return result
        return wrapper
    return decorator

二、对应的设计模式

针对这个场景,主要用到两种核心设计模式:

1. 单例模式(Singleton Pattern)

这是确保Logger实例全局唯一的关键。不管是用模块天然单例,还是显式单例类,本质都是单例模式的应用——保证一个类只有一个实例,并提供一个全局访问点。

在Python里,模块是最简便的单例实现方式,因为Python解释器会自动保证模块只被加载一次;显式单例类则更适合需要自定义单例逻辑的场景。

2. 装饰器模式(Decorator Pattern)

你的日志功能本身就是装饰器模式的典型应用:在不修改原函数代码的前提下,动态地给函数添加额外的日志记录功能,同时保持原函数的调用接口不变。这种方式让日志功能和业务逻辑解耦,你可以随时给任意函数加日志,也能轻松移除。

可选:依赖注入模式(Dependency Injection)

如果想让代码更灵活(比如后续需要替换Logger实现,比如换成文件日志或者第三方日志服务),可以结合依赖注入模式,把Logger实例作为参数传入装饰器:

def log_action(log_string='', logger=None):
    # 没传logger就用全局单例,传了就用传入的实例
    logger = logger or global_logger
    def decorator(func):
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            logger.info(f"[{func.__name__}] {log_string}")
            return result
        return wrapper
    return decorator

这样既保留了全局单例的默认行为,又支持灵活替换Logger,代码扩展性更好。

总结

要实现复用单一Logger实例的装饰器,核心是用单例模式确保实例唯一,再用装饰器模式给函数添加日志功能。Python的模块天然单例是最简便的实现方式,显式单例类则更显式可控,结合依赖注入还能提升代码灵活性。

内容的提问来源于stack exchange,提问作者Vyacheslav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 13:22:37