背景
同一个连接错误可能来自用户查询、账单查询或启动预热。只有底层错误文本,排障时仍然不知道失败的是哪个动作。
核心判断
- 库层提供可匹配的错误类型,不把展示字符串当作协议。
- 应用边界补充动作和资源标识,保留原始 cause 链。
- 最外层统一记录日志,中间层不要每次传播都重复打印。
最小示例
下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。
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,调用方仍可正确区分业务拒绝与基础设施故障。
小结
库层保留结构化错误,应用边界补充动作、资源和请求上下文。