Uber Go 编码规范之 Handle Errors Once:一次处理错误,避免重复处理与日志噪音
文档【免费下载链接】uber_go_guide_cnUber Go 语言编码规范中文版. The Uber Go Style Guide .项目地址https://gitcode.com/gh_mirrors/ub/uber_go_guide_cn点击查看免费下载本篇文章聚焦 Uber Go Style Guide本仓库中文版为uber_go_guide_cnErrors 章节中的Handle Errors Once一次处理错误准则。该准则回答了一个 Go 服务开发中反复出现的问题当函数收到下游错误时究竟应该记录日志再返回、包装后返回还是就地降级处理读完本文你将掌握一套可判定的错误处理决策框架明确errors.Is/errors.As匹配、%w包装、优雅降级等手法的适用场景并理解为何每个错误只处理一次能显著降低应用日志噪音与调试成本。一次处理原则错误处理的责任归属当调用方caller从被调用方callee收到一个错误时它可以根据自己对错误语义的了解以多种方式处理它。Uber 规范在 src/error-once.md 中列出的处理方式包括但不限于契约错误匹配若被调用方的契约定义了特定的错误则用errors.Is或errors.As匹配该错误并对不同分支采取不同处理可恢复错误降级若错误可恢复则记录日志并优雅降级degrade gracefully领域错误转换若错误代表一个特定领域的失败条件则返回一个定义明确的错误原样上抛直接返回错误可以是 包装后的 或原样verbatim返回。无论调用方选择哪种方式它通常应该对每个错误只处理一次。这是整条准则的核心调用方不应既记录日志又返回错误因为它的上层调用方its callers可能还会再次处理同一个错误重复记录会在应用日志中制造大量噪音而信息价值极低。四类典型场景Bad 与 Good 对照规范用一张描述-代码对照表给出了四个典型案例完整呈现了从反面教材到三种正确姿势的决策路径。Bad记录日志并返回错误双重处理u, err : getUser(id) if err ! nil { // BAD: See description log.Printf(Could not get user %q: %v, id, err) return err }这是典型的一个错误被处理两次本层函数既打印了日志又把错误继续向上抛。堆栈中更上层的调用方很可能对同一个错误采取类似的动作再次记录、再次返回最终同一错误在日志中出现多次只有噪音、没有增量价值。Good包装错误并返回%w保持可匹配u, err : getUser(id) if err ! nil { return fmt.Errorf(get user %q: %w, id, err) }如果本层没有必须处理该错误的理由正确做法是只负责添加上下文把处理权交给上层。使用%w动词包装错误能确保上层调用方在需要时仍可借助errors.Is或errors.As匹配到原始错误。这也与本仓库 src/error-wrap.md 的指导一致%w是大多数包装错误的良好默认值。Good记录错误并优雅降级就地终结if err : emitMetrics(); err ! nil { // Failure to write metrics should not // break the application. log.Printf(Could not emit metrics: %v, err) }当操作并非严格必要、失败不影响主流程时可以在本层记录日志 降级——例如埋点上报失败不应让整个应用崩溃。此时日志打印是唯一一次处理错误不再向上传播责任在此终结。注意这种写法要求你能明确判断该操作可以被降级若无法保证则应回到包装上抛的路径。Good匹配错误并优雅降级其余包装上抛tz, err : getUserTimeZone(id) if err ! nil { if errors.Is(err, ErrUserNotFound) { // User doesnt exist. Use UTC. tz time.UTC } else { return fmt.Errorf(get user %q: %w, id, err) } }这是最精细的一种场景被调用方在契约中定义了特定错误如ErrUserNotFound且该失败可恢复。此时用errors.Is匹配已知错误分支并降级处理改用time.UTC兜底而其余未知错误一律包装后上抛交由更上层处理。这样既保证了一次处理原则又让领域语义清晰的错误被就地消化避免把用户不存在这种可恢复情况当成致命错误传播。决策框架何时记录、何时包装、何时降级综合 src/error-once.md 与同章节的 src/error-type.md、src/error-wrap.md可将一次处理原则落实为一个三层决策能否就地恢复若错误是契约中定义的、可恢复的特定错误如用户不存在、指标写入失败用errors.Is/errors.As匹配后降级处理——这是唯一允许记录日志并继续的情况且必须是该错误被处理的唯一一次。本层是否有必须处理的理由若没有只做一件事用fmt.Errorf%w包装添加上下文如get user %q并返回把匹配与决策留给上层。绝不记录返回双处理。日志打印一旦发生错误就应在该层被消化否则就不要打印让错误在栈中只被最合适的层处理一次。错误匹配与包装的配套约定一次处理原则依赖两个前置能力错误可匹配与错误可包装。可匹配要让上层能用errors.Is匹配错误值应定义为导出的全局变量命名以Err前缀如ErrUserNotFound要让上层能用errors.As匹配则应定义自定义错误类型命名以Error后缀。细节见 src/error-name.md 与 src/error-type.md。可包装包装时用%w保留原始错误的可匹配性用%v则混淆底层错误、切断调用方的匹配能力但未来仍可切换回%w。包装信息应简洁避免 failed to 这类会随栈层层堆积的冗余短语——例如new store: %w优于failed to create new store: %w。详见 src/error-wrap.md。值得注意的是本仓库 READMEREADME.md其中 Errors → 一次处理错误 小节为该规范提供了完整的中文翻译对照且 CHANGELOG.md 记录了该章节于 2023-04-13 加入Errors: 只添加一次错误处理指南可作为追溯该准则演进与原文核对的一手材料。小结Handle Errors Once 的本质是为每个错误明确唯一的责任归属要么在本层匹配并降级就地终结要么仅包装上下文后上抛移交决策而记录日志又返回是最应避免的双重处理模式。把这条准则与错误类型选择、%w/%v包装语义、Err/Error命名约定配合使用你的代码将获得更干净的日志、更可追踪的错误链以及更易维护的错误处理路径。赞分享文档【免费下载链接】uber_go_guide_cnUber Go 语言编码规范中文版. The Uber Go Style Guide .项目地址https://gitcode.com/gh_mirrors/ub/uber_go_guide_cn点击查看免费下载相关推荐Meep FDTD电磁仿真完全指南免费开源软件从入门到精通Meep FDTD电磁仿真完全指南免费开源软件从入门到精通 Meep是一款功能强大的免费开源有限差分时域FDTD电磁仿真软件广泛应用于光子学、纳米光学、开发工具Zephyr 实时操作系统入门教程一个内核跑通 1000 块物联网开发板Zephyr 实时操作系统入门教程一个内核跑通 1000 块物联网开发板 当设备只剩几 KB 空闲 RAM却又要跑蓝牙、文件系统甚至 TCP/IP 时选操作系统嵌入式RTOS物联网ZITADEL错误处理统一异常与日志规范ZITADEL错误处理统一异常与日志规范 引言你还在为分布式系统的错误追踪头痛吗 在复杂的分布式身份管理系统中错误处理往往成为开发者的噩梦相同错误被重后端认证鉴权身份认证单点登录上一篇OpenMontage 如何配置预算控制的 observe、warn、cap 三种模式与审批阈值下一篇终极Apache Shiro配置指南从INI文件到代码配置的无缝转换创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考