非侵入式织入日志的核心是用装饰器作用于类方法而非类本身,封装执行过程实现调用前后自动日志记录且不修改原方法逻辑。一、单方法装饰最常用;二、按命名规则批量装饰;三、类装饰器支持配置化;四、需注意@wraps、类方法处理及避免直接装饰类。

直接用装饰器给类方法加日志,不需要改原有方法逻辑,也不用继承或重写类——这就是非侵入式织入的核心。
关键在于:装饰器作用于方法,而非类本身;它封装执行过程,在调用前后自动插入日志,原方法体完全不动。
一、给单个方法加日志(最常用)
适用于想对特定方法(比如 save()、fetch_data())精准打点:
import logging
from functools import wraps
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def log_method_call(func):
@wraps(func)
def wrapper(self, *args, **kwargs):
logging.info(f"[{self.__class__.__name__}.{func.__name__}] 开始执行,参数: {args}, {kwargs}")
try:
result = func(self, *args, **kwargs)
logging.info(f"[{self.__class__.__name__}.{func.__name__}] 执行成功,返回: {result}")
return result
except Exception as e:
logging.error(f"[{self.__class__.__name__}.{func.__name__}] 执行失败: {e}")
raise
return wrapper
# 使用示例
class UserService:
@log_method_call
def get_user(self, user_id):
return {"id": user_id, "name": "Alice"}
def update_user(self, user_id, data): # 不加装饰器,无日志
return True
# 调用
svc = UserService()
svc.get_user(123) # 自动输出日志✅ 特点:只影响被装饰的方法;
self参数自然传递;支持异常捕获与耗时可扩展(加time.time()即可)。
二、批量装饰类中所有方法(按命名规则)
适合统一规范,比如所有以 do_ 或 handle_ 开头的方法都需日志:
def auto_log_methods(cls):
for attr_name in dir(cls):
attr = getattr(cls, attr_name)
if callable(attr) and not attr_name.startswith('_') and attr_name not in ['__init__', '__str__']:
setattr(cls, attr_name, log_method_call(attr))
return cls
@auto_log_methods
class OrderProcessor:
def validate(self):
return "valid"
def submit(self):
return "submitted"⚠️ 注意:
__init__通常不建议自动加日志(可能干扰实例化),显式排除更安全;也可改为白名单模式(如只装饰['validate', 'submit', 'cancel'])。
三、用类装饰器统一管理(更结构化)
把日志策略封装成类,支持开关、级别、输出目标等配置:
class MethodLogger:
def __init__(self, level=logging.INFO, include_args=True):
self.level = level
self.include_args = include_args
def __call__(self, func):
@wraps(func)
def wrapper(self, *args, **kwargs):
class_name = self.__class__.__name__
msg = f"{class_name}.{func.__name__}"
if self.include_args:
msg += f"({args}, {kwargs})"
logging.log(self.level, f"[START] {msg}")
result = func(self, *args, **kwargs)
logging.log(self.level, f"[END] {class_name}.{func.__name__}")
return result
return wrapper
# 使用
class PaymentService:
@MethodLogger(level=logging.DEBUG)
def charge(self, amount):
return f"charged ${amount}"✅ 优势:复用性强;不同类可配不同日志级别;未来扩展(如加 trace_id、用户 ID)只需改
MethodLogger。
四、避免常见陷阱
-
别漏掉
@wraps(func):否则help()、func.__name__会变成wrapper,影响调试和框架反射(如 Flask、FastAPI 路由识别)。 -
类方法/静态方法要单独处理:
@classmethod和@staticmethod的装饰顺序很重要,日志装饰器应放在最外层:@log_method_call @classmethod def from_config(cls, cfg): ...
-
不建议直接装饰整个类:Python 没有原生“类装饰器织入所有方法”的语法糖;强行遍历
__dict__可能干扰描述符、@property等,稳妥做法仍是方法级或用auto_log_methods显式控制。
不复杂但容易忽略细节。

















