Python日志装饰器案例如何记录日志

wen python案例 29

Python日志装饰器案例:如何高效记录日志与优化程序调试

📚 目录导读

  1. 为什么选择装饰器记录日志?
  2. Python日志装饰器基础实现
  3. 进阶功能:参数化日志装饰器
  4. 实际案例:企业级日志记录方案
  5. 常见问题与性能优化
  6. 问答环节

为什么选择装饰器记录日志?

在Python开发中,日志记录是调试和监控系统运行状态的核心手段,传统方法需要在每个函数内部手动添加logging.info()语句,这不仅导致代码冗余,还容易遗漏关键路径。装饰器(Decorator) 提供了一种优雅的解决方案——通过@log_decorator这样的语法,将日志逻辑与业务逻辑解耦。

Python日志装饰器案例如何记录日志

核心优势:

  • DRY原则:避免重复编写日志代码
  • 透明性:不修改原函数逻辑即可增强功能
  • 灵活性:可针对不同函数动态调整日志级别

Python日志装饰器基础实现

1 标准日志模块配置

import logging
import functools
# 配置日志格式
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

2 基本装饰器实现

def log_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        logger.info(f"调用函数: {func.__name__},参数: {args}, {kwargs}")
        try:
            result = func(*args, **kwargs)
            logger.info(f"函数 {func.__name__} 返回: {result}")
            return result
        except Exception as e:
            logger.error(f"函数 {func.__name__} 异常: {e}")
            raise
    return wrapper
@log_decorator
def add(a, b):
    """计算两数之和"""
    return a + b
# 调用测试
add(5, 3)  # 输出调用和返回日志

关键点说明:

  • @functools.wraps保证装饰器不改变原函数的元信息(如__name__
  • *args, **kwargs确保支持任意参数签名
  • try-except捕获异常并记录错误堆栈

进阶功能:参数化日志装饰器

1 支持日志级别与输出控制

def log_with_level(level=logging.INFO, output_params=True):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if output_params:
                logger.log(level, f"【{func.__name__}】入参: {args}, {kwargs}")
            else:
                logger.log(level, f"【{func.__name__}】已调用")
            start_time = time.time()
            try:
                result = func(*args, **kwargs)
                elapsed = time.time() - start_time
                logger.log(level, f"【{func.__name__}】耗时{elapsed:.4f}s,返回值: {result}")
                return result
            except Exception as e:
                logger.error(f"【{func.__name__}】异常: {e}", exc_info=True)
                raise
        return wrapper
    return decorator
# 使用示例
@log_with_level(level=logging.DEBUG, output_params=False)
def database_query(user_id):
    pass  # 假设数据库操作

2 上下文感知装饰器

def trace_execution(module_name="default"):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            extra = {'module': module_name, 'session_id': uuid4()}
            logger = logging.LoggerAdapter(logging.getLogger(__name__), extra)
            # 日志记录逻辑...
        return wrapper
    return decorator

实际案例:企业级日志记录方案

📊 案例背景

某电商系统需要监控订单处理流程的每个环节,要求:

  • 记录每个函数的调用耗时
  • 敏感参数(如密码)需脱敏
  • 异常时自动发送告警通知

实现方案

import time
import hashlib
def sensitive_log_decorator(sensitive_params=[]):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # 参数脱敏处理
            safe_args = tuple(
                arg if i not in sensitive_params else "***" 
                for i, arg in enumerate(args)
            )
            safe_kwargs = {
                k: "***" if k in sensitive_params else v 
                for k, v in kwargs.items()
            }
            logger.info(f"订单处理 - 函数: {func.__name__}, 参数: {safe_kwargs}")
            start = time.perf_counter()
            try:
                result = func(*args, **kwargs)
                elapsed = time.perf_counter() - start
                logger.info(f"完成: {func.__name__}, 耗时: {elapsed:.3f}s")
                # 耗时超过阈值告警
                if elapsed > 2.0:
                    logger.warning(f"⚠️ 高风险操作: {func.__name__} 耗时{elapsed:.3f}s")
                return result
            except Exception as e:
                logger.critical(f"🔥 订单处理失败: {func.__name__}, 错误: {e}", exc_info=True)
                # 触发告警钩子
                send_alert(e, func.__name__)
                raise
        return wrapper
    return decorator
# 实际应用
@sensitive_log_decorator(sensitive_params=['password', 'credit_card'])
def process_payment(user_id, amount, password, credit_card):
    # 业务逻辑...
    pass

效果对比:

问题 传统方法 装饰器方案
代码重复 每个函数重复写日志 一行@语法解决
日志一致性 容易格式不统一 统一格式和级别
异常处理 零散try-catch 集中捕获与告警
性能监控 手动计时 自动记录耗时

常见问题与性能优化

❌ 常见陷阱

  1. 循环引用:装饰器内部导入模块时需延迟导入
  2. 线程安全:装饰器内状态变量需使用线程锁
  3. 异步支持:处理协程时需使用asyncio专用装饰器

🔧 性能优化策略

# 懒加载日志器对象
_loggers = {}
def get_logger(name):
    if name not in _loggers:
        _loggers[name] = logging.getLogger(name)
    return _loggers[name]
# 使用lru_cache缓存装饰器实例
from functools import lru_cache
@lru_cache(maxsize=128)
def create_log_decorator(level):
    # 返回装饰器工厂
    return log_with_level(level)

异步日志实现

import asyncio
def async_log_decorator(func):
    @functools.wraps(func)
    async def wrapper(*args, **kwargs):
        logger.info(f"异步调用: {func.__name__}")
        try:
            return await func(*args, **kwargs)
        except Exception as e:
            logger.exception(f"异步异常: {e}")
            raise
    return wrapper

问答环节

Q1: 装饰器会降低性能吗?如何量化?

A: 会,但通常可以忽略,单次日志I/O约1-5μs,1000次调用增加5ms,可使用timeit模块测试:

%timeit add(1,2)  # 无装饰器约0.2μs,有装饰器约5μs

优化方向:异步日志写入、使用logging.Handler队列缓冲。

Q2: 如何在不修改装饰器的情况下屏蔽特定函数的日志?

A: 使用装饰器开关模式:

ENABLE_LOGGING = os.getenv('LOG_ENABLED', 'true').lower() == 'true'
def conditional_log(func):
    if not ENABLE_LOGGING:
        return func  # 直接返回原函数
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        # 日志逻辑
        ...
    return wrapper
@conditional_log
def low_priority_task():
    pass

Q3: 日志装饰器如何集成到现有的Flask/Django框架?

A: 使用AOP思想,在框架的中间件层面统一添加,示例:

# Flask路由装饰器
def log_route(route_func):
    @log_decorator
    @functools.wraps(route_func)
    def wrapped(*args, **kwargs):
        return route_func(*args, **kwargs)
    return wrapped
@app.route('/api/order')
@log_route
def create_order():
    pass

Q4: 如何确保日志不泄漏敏感信息?

A: 实现数据脱敏装饰器

def mask_sensitive(pattern='password'):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # 过滤kwargs中的敏感键
            clean_kwargs = {
                k: '****' if pattern in k.lower() else v 
                for k, v in kwargs.items()
            }
            logger.info(f"调用 {func.__name__}, 参数: {clean_kwargs}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

通过Python装饰器实现日志记录,不仅简化了代码维护,还提供了统一的服务治理接口,推荐实践步骤:

  1. 基础建设:配置好日志格式和级别
  2. 通用装饰器:支持参数传递和异常捕获
  3. 企业级扩展:添加性能监控、脱敏、告警等功能
  4. 测试验证:使用单元测试确保装饰器不破坏业务逻辑

掌握这些技巧后,你可以轻松构建健壮的日志系统,提升程序的可观测性和调试效率。

抱歉,评论功能暂时关闭!