Python 日志处理与自定义异常:打造可靠的错误监控系统

王者杯·14天创作挑战营·第9期 10w+人浏览 216人参与

(从基础配置到高级封装,用 100 行代码构建企业级日志方案)


开篇:为什么日志比 print 更重要?

你有没有过这样的经历:

  • 线上代码报错,但不知道哪里出了问题
  • 调试时用 print 输出了一堆信息,上线时忘了删除
  • 系统崩溃后,没有任何记录可以分析

print是调试的好帮手,但在线上环境,日志才是系统的「黑匣子」。Python 的logging模块提供了完整的日志处理功能,结合自定义异常,能帮你打造可靠的错误监控系统。


一、Python 日志处理基础

1. logging 模块的核心组件

logging 模块包含 4 个核心组件:

  • Logger:日志记录器,负责生成日志
  • Handler:日志处理器,负责将日志输出到指定位置(控制台、文件、邮件等)
  • Formatter:日志格式化器,负责日志的格式
  • Filter:日志过滤器,负责过滤日志

2. 基本使用示例

import logging

# 1. 配置日志
logging.basicConfig(
    level=logging.INFO,  # 日志级别
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',  # 日志格式
    datefmt='%Y-%m-%d %H:%M:%S'  # 时间格式
)

# 2. 获取日志记录器
logger = logging.getLogger(__name__)

# 3. 输出不同级别的日志
logger.debug('调试信息')  # 不会输出(默认级别是WARNING)
logger.info('普通信息')  # 输出
logger.warning('警告信息')  # 输出
logger.error('错误信息')  # 输出
logger.critical('致命信息')  # 输出

3. 日志级别

logging 模块定义了 5 种日志级别(从低到高):

级别描述应用场景
DEBUG调试信息开发阶段调试代码
INFO普通信息记录系统运行状态
WARNING警告信息潜在的问题,但不影响系统运行
ERROR错误信息系统出错,但能继续运行
CRITICAL致命信息系统崩溃,无法继续运行

二、高级日志配置

1. 输出日志到文件

import logging

# 创建日志记录器
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)  # 设置日志级别

# 创建文件处理器
file_handler = logging.FileHandler('app.log', encoding='utf-8')
file_handler.setLevel(logging.INFO)  # 文件处理器的级别

# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)  # 控制台处理器的级别

# 创建日志格式化器
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
)

# 为处理器设置格式化器
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)

# 将处理器添加到日志记录器
logger.addHandler(file_handler)
logger.addHandler(console_handler)

# 测试
logger.debug('调试信息')  # 控制台输出
logger.info('普通信息')  # 控制台+文件输出
logger.error('错误信息')  # 控制台+文件输出

2. 日志滚动与切割

当日志文件过大时,需要对其进行滚动和切割。logging 模块提供了RotatingFileHandler(按大小切割)和TimedRotatingFileHandler(按时间切割)。

按大小切割示例
from logging.handlers import RotatingFileHandler

# 创建按大小滚动的文件处理器
# 单个文件最大1MB,保留5个备份文件
rotating_handler = RotatingFileHandler(
    'app.log',
    maxBytes=1024 * 1024,  # 1MB
    backupCount=5,
    encoding='utf-8'
)
rotating_handler.setFormatter(formatter)
logger.addHandler(rotating_handler)
按时间切割示例
from logging.handlers import TimedRotatingFileHandler
import time

# 创建按时间滚动的文件处理器
# 每天凌晨0点切割,保留7个备份文件
timed_handler = TimedRotatingFileHandler(
    'app.log',
    when='midnight',  # 切割时间点
    interval=1,  # 间隔时间
    backupCount=7,  # 备份数量
    encoding='utf-8'
)
timed_handler.setFormatter(formatter)
logger.addHandler(timed_handler)

三、自定义异常

1. 为什么需要自定义异常?

Python 提供了很多内置异常(如ValueErrorTypeError),但在实际开发中,自定义异常能让你的代码更清晰、更易于维护。比如:

  • 你正在开发一个电商系统,需要处理「库存不足」的异常
  • 你正在开发一个 API,需要处理「权限不足」的异常

2. 自定义异常的基本方法

自定义异常需要继承自Exception类或其子类

# 自定义异常
class InventoryInsufficientError(Exception):
    """库存不足异常"""
    def __init__(self, product_id, required_quantity, current_quantity):
        self.product_id = product_id
        self.required_quantity = required_quantity
        self.current_quantity = current_quantity
        # 调用父类的__init__方法
        super().__init__(f"商品{product_id}库存不足:需要{required_quantity},当前{current_quantity}")

# 使用自定义异常
def check_inventory(product_id, required_quantity):
    # 模拟库存查询
    inventory = {
        '1001': 5,
        '1002': 10
    }
    current_quantity = inventory.get(product_id, 0)
    if current_quantity < required_quantity:
        raise InventoryInsufficientError(product_id, required_quantity, current_quantity)
    return True

# 测试
try:
    check_inventory('1001', 10)
except InventoryInsufficientError as e:
    print(f"错误类型:{type(e).__name__}")
    print(f"错误信息:{e}")
    print(f"商品ID:{e.product_id}")
    print(f"需要数量:{e.required_quantity}")
    print(f"当前库存:{e.current_quantity}")

运行结果

错误类型:InventoryInsufficientError
错误信息:商品1001库存不足:需要10,当前5
商品ID:1001
需要数量:10
当前库存:5

四、日志与自定义异常的结合

1. 异常捕获与日志记录

在捕获自定义异常时,应该将异常信息完整地记录到日志中:

import logging

# 配置日志(省略,同上)

try:
    check_inventory('1001', 10)
except InventoryInsufficientError as e:
    # 记录错误日志,包括异常的详细信息
    logger.error(
        "库存不足异常",
        extra={
            'product_id': e.product_id,
            'required_quantity': e.required_quantity,
            'current_quantity': e.current_quantity
        },
        exc_info=True  # 记录完整的堆栈信息
    )

日志输出

2024-05-20 14:30:00 - my_logger - ERROR - app.py:100 - 库存不足异常
Traceback (most recent call last):
  File "app.py", line 98, in <module>
    check_inventory('1001', 10)
  File "app.py", line 50, in check_inventory
    raise InventoryInsufficientError(product_id, required_quantity, current_quantity)
InventoryInsufficientError: 商品1001库存不足:需要10,当前5

2. 企业级异常处理方案

结合日志和自定义异常,我们可以构建一个统一的异常处理装饰器

import logging
import functools

# 配置日志(省略,同上)

def handle_exception(func):
    """统一异常处理装饰器"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except InventoryInsufficientError as e:
            logger.error(
                "库存不足异常",
                extra={
                    'product_id': e.product_id,
                    'required_quantity': e.required_quantity,
                    'current_quantity': e.current_quantity
                },
                exc_info=True
            )
            # 可以在这里添加额外的处理逻辑,如发送邮件、短信等
            return False
        except Exception as e:
            logger.error(
                "未知异常",
                exc_info=True
            )
            return False
    return wrapper

# 使用装饰器
@handle_exception
def process_order(order_id):
    # 模拟订单处理流程
    check_inventory('1001', 10)
    # 其他订单处理逻辑...
    return True

# 测试
process_order('order_001')

五、最佳实践

1. 日志相关

  • 不要使用 print:在线上环境,所有输出都应该通过日志记录
  • 设置合适的日志级别:开发阶段用 DEBUG,线上环境用 INFO 或 WARNING
  • 使用结构化日志:包含时间、级别、文件名、行号、用户 ID、请求 ID 等信息
  • 定期备份和清理日志:避免日志文件过大
  • 记录异常的完整信息:包括堆栈追踪、参数信息等

2. 自定义异常相关

  • 继承自 Exception 类:不要继承自 BaseException
  • 提供详细的错误信息:包含异常的上下文和参数
  • 保持异常类的简洁:只包含必要的属性和方法
  • 使用一致的命名规范:以 Error 或 Exception 结尾
  • 不要过度使用自定义异常:只有在需要区分特定错误场景时才使用

六、总结

日志处理和自定义异常是 Python 开发中不可或缺的两个部分

  • 日志:记录系统的运行状态和错误信息,是系统的「黑匣子」
  • 自定义异常:清晰地表达特定的错误场景,提高代码的可读性和维护性

结合日志和自定义异常,你可以构建一个可靠的错误监控系统,及时发现和定位线上问题。在实际开发中,应该遵循最佳实践,确保日志和异常处理的规范和高效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值