30 分钟
实战专项

日志、监控与错误处理体系

构建生产级应用的可观测性体系,让问题能被快速发现和定位

  • 掌握 Python logging 模块的正确用法
  • 学会结构化日志和日志聚合
  • 理解重试、熔断、降级模式
  • 掌握错误处理的最佳实践

为什么 print 不够用?

开发时 print 没问题,但生产环境你需要:日志级别(调试/信息/警告/错误)、输出到文件+控制台、自动轮转、结构化格式、按模块控制级别。Python 内置的 logging 模块就是干这个的。

示例代码(可运行)

结构化日志

生产环境的日志通常会被收集到 ELK/Datadog/ Loki 等系统里搜索分析。纯文本日志不好查,结构化日志(JSON)能按字段过滤。

Python
# 用 python-json-logger 输出 JSON
from pythonjsonlogger import jsonlogger

handler = logging.StreamHandler()
handler.setFormatter(jsonlogger.JsonFormatter(
    "%(asctime)s %(levelname)s %(name)s %(message)s"
))
logging.getLogger().addHandler(handler)

# 输出:{"asctime": "...", "levelname": "ERROR", "name": "payment", ...}

# 更简单:用 structlog 或 loguru
# loguru 示例:
from loguru import logger
logger.add("app.json", serialize=True)  # 自动 JSON
logger.add("app.log", rotation="10 MB", retention="30 days")
logger.bind(user_id=123, order_id=456, amount=99.9).error("支付失败")
⚠️日志安全

不要在日志中记录密码、Token、身份证号、信用卡号等敏感信息。日志文件的访问权限要控制。GDPR/个人信息保护法要求用户数据可以被删除,但日志中的数据很难清理——所以一开始就不要记。

错误处理体系

示例
# 1. 自定义异常层次
class AppError(Exception):
    """应用异常基类"""
    code = "APP_ERROR"

class PaymentError(AppError):
    code = "PAYMENT_ERROR"

class InsufficientBalanceError(PaymentError):
    code = "INSUFFICIENT_BALANCE"

# 2. 精确捕获,不要裸 except
try:
    process_payment(order)
except InsufficientBalanceError:
    return {"error": "余额不足"}, 400
except PaymentError as e:
    logger.error(f"支付失败: {e}")
    return {"error": "支付服务暂时不可用"}, 502
except Exception:
    logger.exception("未预期的错误")  # 记录完整堆栈
    raise  # 重新抛出,不要吞掉

# 3. 永远不要这样写:
try:
    risky_operation()
except:  # 裸 except 会捕获 KeyboardInterrupt 和 SystemExit!
    pass   # 吞掉所有错误,问题被隐藏,调试噩梦

# 4. finally 确保资源释放
try:
    f = open("data.txt")
    process(f)
finally:
    f.close()  # 即使出错也会关闭

# 更好:用上下文管理器
with open("data.txt") as f:
    process(f)  # 自动关闭

重试、熔断与降级

Python
# 重试:网络请求可能因临时故障失败,重试可以解决
import tenacity

@tenacity.retry(
    stop=tenacity.stop_after_attempt(3),       # 最多重试3次
    wait=tenacity.wait_exponential(min=1, max=10),  # 指数退避1s,2s,4s
    retry=tenacity.retry_if_exception_type(NetworkError),
)
def call_external_api():
    return requests.get("https://api.example.com/data", timeout=5)

# 熔断:当依赖服务持续失败时,快速失败而不是一直等待
# 用 circuitbreaker 库或手动实现
class CircuitBreaker:
    def __init__(self, threshold=5, reset_timeout=30):
        self.failures = 0
        self.threshold = threshold
        self.reset_timeout = reset_timeout
        self.opened = False

    def call(self, func, *args):
        if self.opened:
            raise Exception("熔断器开启,快速失败")
        try:
            result = func(*args)
            self.failures = 0  # 成功则重置
            return result
        except Exception:
            self.failures += 1
            if self.failures >= self.threshold:
                self.opened = True
            raise

# 降级:主服务不可用时提供备用方案
def get_user_recommendations(user_id):
    try:
        return ai_service.get_recommendations(user_id)
    except Exception:
        logger.warning("AI推荐不可用,降级为热门推荐")
        return get_popular_items()  # 降级方案
🐍可观测性三支柱

生产系统的可观测性包括:Logs(日志)——离散事件记录;Metrics(指标)——聚合数值(QPS、延迟P99、错误率);Traces(链路追踪)——一个请求经过的所有服务。三者结合才能快速定位分布式系统的问题。

资深工程师加餐

底层原理 · 大厂视角 · 工程经验,点卡片展开

从输入网址到看到结果,大致是:DNS 解析拿到 IP → TCP 三次握手 →(HTTPS 还要 TLS 协商)→ 发出请求 → 服务端经过中间件、路由、业务逻辑、查库 → 返回响应 → 连接复用或四次挥手。理解状态码、幂等方法(GET/PUT/DELETE)与非幂等(POST),是做 Web 和联调的基本功。

挑战任务

实现重试机制

简单+50 XP

写个带指数退避的重试装饰器,最多重试3次,只对网络异常重试。

实现重试机制
3 个测试用例

课后作业

构建日志体系

中等+30 XP

给你的项目配完整日志体系:控制台彩色输出、文件轮转、JSON格式、按模块级别控制,登录、支付、错误这些关键操作都加日志。

构建日志体系
1 个测试用例