Python通用函数起止日志装饰器实现:提取指定参数入日志
通用Python日志装饰器实现
核心思路
要实现满足需求的日志装饰器,核心难点是安全提取指定参数——不管被装饰函数的参数是位置传参、关键字传参,还是参数可选,都能从args和kwargs中准确拿到目标字段,同时避免直接打印整组参数。
这里用inspect模块解析函数签名,把args和kwargs绑定到函数的参数定义上,生成一个包含所有参数映射的字典,再从中提取我们需要的字段(比如product_id、message_id),完美解决参数形式不固定的问题。
完整实现代码
import inspect import datetime import logging # 初始化日志配置 logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" ) logger = logging.getLogger(__name__) def log_with_context(target_fields=["product_id", "message_id"]): def decorator(func): def wrapper(*args, **kwargs): # 解析被装饰函数的参数签名 sig = inspect.signature(func) try: # 绑定传入的args和kwargs到签名,生成参数映射 bound_args = sig.bind(*args, **kwargs) bound_args.apply_defaults() # 应用默认值,处理可选参数 params = bound_args.arguments except ValueError: # 极端情况:参数不匹配时,用空字典避免报错 params = {} # 提取目标字段,不存在则设为None context = {field: params.get(field) for field in target_fields} # 添加时间戳 context["timestamp"] = datetime.datetime.now().isoformat() # 函数执行前日志 logger.info( f"Executing function: {func.__name__} | Context: {context}" ) # 执行原函数 try: result = func(*args, **kwargs) # 函数执行成功日志 logger.info( f"Function {func.__name__} executed successfully | Context: {context}" ) return result except Exception as e: # 函数执行异常日志 logger.error( f"Function {func.__name__} failed | Error: {str(e)} | Context: {context}" ) raise # 重新抛出异常,不影响原函数的异常处理 return wrapper return decorator
测试示例
测试1:参数为位置传参的函数
@log_with_context() def process_order(product_id, quantity, message_id=None): if quantity <= 0: raise ValueError("Invalid quantity") return f"Processed order for product {product_id}" process_order("PROD-123", 5, "MSG-456") process_order("PROD-789", -2) # 触发异常
测试2:参数为关键字传参的函数
@log_with_context(target_fields=["product_id", "user_id"]) def update_product(product_id, user_id, details=None): return f"Updated product {product_id} by user {user_id}" update_product(user_id="USER-001", product_id="PROD-456")
关键特性说明
- 通用性:不管被装饰函数的参数是位置、关键字还是可选参数,都能正确提取目标字段
- 安全性:参数不匹配时不会崩溃,会用空字典处理
- 低冗余:只打印指定的字段,不会输出整个
args/kwargs的冗余数据 - 可配置:通过
target_fields参数可以自定义要提取的日志字段
内容的提问来源于stack exchange,提问作者shlomiLan
相关产品推荐
相关产品推荐

