Python案例如何用日志调试

wen python案例 42

从“黑盒”到“白盒”:Python案例教你如何用日志调试

📖 目录导读

  1. 为什么你需要学会用日志调试?
  2. 新手常犯的三大日志误区
  3. Python日志系统核心配置(含代码案例)
  4. 实战案例:用日志揪出购物车金额计算Bug
  5. 企业级日志最佳实践(结构化+分级)
  6. 日志调试vs print调试:一场效率对决
  7. 常见问答:日志文件太大怎么办?如何避免敏感信息泄露?

为什么你需要学会用日志调试?

很多Python初学者遇到Bug时第一反应是狂加print(),但当你面对一个运行了3天、每天处理百万条订单的脚本时,这种方法几乎无效,日志调试能让你:

Python案例如何用日志调试

  • 回溯历史行为:即使程序崩溃,日志文件保留了崩溃前的执行状态
  • 区分严重等级:INFO/WARNING/ERROR 让问题一目了然
  • 按需过滤信息:生产环境只输出ERROR,开发环境输出DEBUG

关键认知:日志不是“打印变体”,而是一种可持久化、可分级的诊断信息流


新手常犯的三大日志误区

  • ❌ 把print当主力:print只输出到控制台,程序关闭后信息丢失
  • ❌ 只打ERROR不打INFO:没有上下文线索,难以定位异常根源
  • ❌ 所有日志都写在同一文件:混淆了调试信息与业务告警

正确姿势:DEBUG记录变量状态,INFO记录关键流程,WARNING提示异常趋势,ERROR记录致命错误。


Python日志系统核心配置(含代码案例)

不需要复杂的框架,标准库logging足以应付90%场景:

import logging
# 基础配置:同时输出到文件和控制台
logging.basicConfig(
    level=logging.DEBUG,  # 最低日志级别
    format='%(asctime)s | %(levelname)s | %(filename)s:%(lineno)d | %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
    handlers=[
        logging.FileHandler('app_debug.log', encoding='utf-8'),
        logging.StreamHandler()  # 控制台输出
    ]
)
logger = logging.getLogger(__name__)
# 使用范例
logger.debug("变量x当前值为: %s", x)
logger.info("用户ID %s 登录成功", user_id)
logger.warning("数据库连接池使用率85%%")
logger.error("支付接口返回异常: %s", err)

输出示例

2025-06-15 14:30:22 | DEBUG | cashier.py:67 | 变量x当前值为: 0.98
2025-06-15 14:30:23 | INFO | login.py:12 | 用户ID 306 登录成功

实战案例:用日志揪出购物车金额计算Bug

场景:某电商平台用户反映“订单结算金额有时比预期多0.01元”,后端团队用日志快速定位。

错误代码(简化版):

def calc_total(items):
    total = 0.0
    for item in items:
        total += item['price'] * item['qty']
    return round(total, 2)  # 浮点数精度问题

加入日志后

logger.debug("原始折扣后价格为 0.1 的浮点表示: %r", 0.1)
# 输出:原始折扣后价格为 0.1 的浮点表示: 0.10000000000000000555

真相:浮点数精确表示缺陷导致舍入时出现1分钱偏差。

修复方案

from decimal import Decimal
def calc_total(items):
    total = Decimal('0.00')
    for item in items:
        total += Decimal(str(item['price'])) * Decimal(str(item['qty']))
    return float(total)

企业级日志最佳实践(结构化+分级)

  • 结构化日志:使用JSON格式,方便ELK等工具检索
    import json_log_formatter

formatter = json_log_formatter.JSONFormatter() handler = logging.FileHandler('log.json') handler.setFormatter(formatter)


- **分级文件**:
  - `debug.log`:记录所有级别
  - `error.log`:只记录ERROR及以上
  - `access.log`:记录用户请求(INFO级别)
- **按日期滚动**:使用`TimedRotatingFileHandler`
```python
from logging.handlers import TimedRotatingFileHandler
handler = TimedRotatingFileHandler('daily.log', when='midnight', backupCount=30)

日志调试vs print调试:一场效率对决

维度 print调试 日志调试
持久化 写入文件
分级过滤 根据级别筛选
并发安全 可能乱序 线程安全
后期检索 手动翻屏 grep/实时过滤
生产环境适用 ❌ 切勿使用 ✅ 必须使用

print只适合20行以内的临时脚本,任何超过200行的代码都应使用日志。


常见问答

Q:日志文件太大怎么办? A:使用RotatingFileHandler限制单个文件大小(如10MB),并设置backupCount保留3个历史备份。

Q:如何避免日志泄露用户密码? A:自定义过滤器,对敏感字段(如password、token)进行掩码处理:

class SensitiveFilter(logging.Filter):
    def filter(self, record):
        record.msg = record.msg.replace(getattr(record, 'password', ''), '****')
        return True

Q:为什么我的日志没有输出? A:检查三点:

  1. 子logger级别是否盖过了父logger(例如子logger设置level=WARNING会忽略DEBUG)
  2. 是否忘记创建FileHandler对象
  3. 文件路径是否有写入权限

Q:线上环境如何动态调整日志级别? A:通过配置中心或信号机制(如SIGUSR1)动态修改logger.setLevel(logging.DEBUG),无需重启服务。


写在最后

掌握日志调试,等于给代码装上了“行车记录仪”,下次遇到Bug,请关掉写满print的那段代码,打开logging模块,你将看见一个清晰透明的执行世界,从今天起,让每一个变量变化、每一个错误路径都留下可追溯的足迹。

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