运维需异常含可定位上下文,如req_id、trace_id及具体字段;须保留异常链、区分类型、结构化日志并透传trace_id。
异常信息里必须包含可定位的上下文
运维最怕看到 ValueError: invalid literal for int() 这种没上下文的报错——根本不知道是哪个字段、哪条数据、哪个服务实例出的问题。关键不是“抛没抛异常”,而是异常里有没有让运维能立刻查日志、翻监控、定位到具体请求的线索。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 在捕获并重新抛出异常时,用
raise ValueError(f"parse_user_id failed for request_id={req_id}, raw_data={raw}") from e补全上下文,而不是直接raise e - 避免裸字符串拼接敏感字段(如用户手机号),可用
repr(data.get('user_id'))或脱敏函数处理 - 确保
req_id、trace_id、service_name等标识已注入当前 logger 或异常链中
不要吞掉原始异常链(raise ... from e 不能少)
常见错误是写成 except Exception as e: log.error("xxx"); raise Exception("业务失败"),这直接切断了异常栈,运维看不到底层是数据库超时还是 JSON 解析失败。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 所有二次包装异常必须显式使用
raise NewException(...) from e,Python 3 默认保留__cause__和 traceback - 用
logging.exception()记录时,它会自动打印完整 traceback;但若用了logging.error(str(e))就只剩一行字符串 - 检查中间件或装饰器是否无意中吃掉了异常(比如某些 Flask 错误处理器返回 500 响应却不 re-raise)
区分异常类型,别全用 Exception 或 RuntimeError
运维排查时依赖异常类型做聚合和告警分级。如果所有错误都抛 RuntimeError,监控系统就无法区分“第三方 API 临时不可用”和“配置文件缺失”这两类完全不同的处置优先级。