背景

同一个连接错误可能来自用户查询、账单查询或启动预热。只有底层错误文本,排障时仍然不知道失败的是哪个动作。

核心判断

  1. 库层提供可匹配的错误类型,不把展示字符串当作协议。
  2. 应用边界补充动作和资源标识,保留原始 cause 链。
  3. 最外层统一记录日志,中间层不要每次传播都重复打印。

最小示例

下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。

use anyhow::{Context, Result};

async fn load(repo: &Repository, id: i64) -> Result<User> {
    repo.find(id)
        .await
        .with_context(|| format!("load user id={id}"))
}

边界与误区

示例依赖 anyhow,Repository 和 User 是业务类型。上下文不能塞入完整请求体、认证头或个人敏感信息。需要分支处理的业务错误也不应过早转换成字符串。

验证方式

构造数据库超时和记录缺失两条路径,确认日志能识别动作、错误链和关联 ID,调用方仍可正确区分业务拒绝与基础设施故障。

小结

库层保留结构化错误,应用边界补充动作、资源和请求上下文。