4. 错误码设计原则:HTTP 状态码类比、业务错误码分类、错误码的层级结构
在构建高可观测性的 Go 服务时,错误码不仅仅是“错误编号”,它是服务间通信的契约、故障排查的索引、以及审计追踪的锚点。本章从三个维度拆解错误码的设计原则:HTTP 状态码类比(建立通用语义)、业务错误码分类(划分错误域)、层级结构(组织编码空间)。
4.1 HTTP 状态码类比:建立通用语义
HTTP 状态码是业界最成熟的错误分类体系。将业务错误码与 HTTP 状态码进行语义映射,可以降低跨团队、跨服务的认知成本。核心映射关系如下:
| HTTP 状态码 | 语义 | 业务错误码映射场景 | Go 审计关注点 |
|---|---|---|---|
| 2xx (成功) | 请求正常处理 | 业务成功,无需错误码 | 审计日志中不应记录为错误 |
| 4xx (客户端错误) | 请求方问题 | 参数校验失败、权限不足、资源不存在 | 检查是否暴露了内部实现细节 |
| 5xx (服务端错误) | 服务方问题 | 数据库超时、依赖服务不可用、内部断言失败 | 必须记录完整堆栈,禁止吞没 |
审计基线要点: 业务错误码的“大类”必须与 HTTP 状态码的语义对齐。例如,参数错误不应返回 5xx 类错误码,内部服务不可用不应返回 4xx 类错误码。违反此原则将导致监控告警误报,属于 严重 级别缺陷。
4.2 业务错误码分类:划分错误域
业务错误码不能是扁平的一维列表,必须按 错误域 进行分类。常见的分类维度包括:
- 系统级错误 (SYS):基础设施故障、网络超时、磁盘 I/O 错误、内存不足。
- 应用级错误 (APP):配置错误、依赖服务调用失败、内部状态异常、断言失败。
- 业务逻辑错误 (BIZ):参数校验失败、权限不足、资源不存在、业务规则冲突。
- 安全审计错误 (SEC):认证失败、签名校验失败、越权访问、敏感操作未授权。
| 错误域 | 错误码前缀 | 典型场景 | 审计日志级别 |
|---|---|---|---|
| 系统级 (SYS) | 5xxxx | 数据库连接池耗尽、gRPC 调用超时 | ERROR |
| 应用级 (APP) | 5xxxx | 配置加载失败、内部状态机异常 | ERROR |
| 业务逻辑 (BIZ) | 4xxxx | 用户不存在、余额不足、订单状态冲突 | WARN |
| 安全审计 (SEC) | 4xxxx | Token 过期、签名不匹配、越权操作 | WARN / ERROR |
设计建议: 在 Go 代码中,建议使用
const 块定义错误码,并按照错误域分组。例如:ErrCodeSysDBTimeout = 50001、ErrCodeBizUserNotFound = 40401。避免在代码中直接使用魔法数字。
4.3 错误码的层级结构:组织编码空间
错误码的层级结构决定了编码空间的扩展性和可读性。推荐使用 “大类 + 中类 + 小类” 的三级结构:
- 第一级(大类):1 位数字,对应 HTTP 语义(4 或 5)。
- 第二级(中类):2 位数字,对应错误域(00~99)。
- 第三级(小类):2 位数字,对应具体错误(00~99)。
编码格式示例:4 01 01 → 4(客户端错误)+ 01(参数校验域)+ 01(必填参数缺失)。
4.4 Go 代码中的错误码断言实践
在 Go 审计中,错误码断言的核心是验证错误码的 层级一致性 和 语义正确性。以下是一个符合层级结构的错误码定义示例:
// 错误码定义:遵循 4/5 + 两位域 + 两位具体错误
const (
// ===== 4xx 客户端错误 =====
// 参数校验域 (01)
ErrCodeParamRequired = 40101 // 必填参数缺失
ErrCodeParamInvalid = 40102 // 参数格式错误
ErrCodeParamOutOfRange = 40103 // 参数超出范围
// 权限校验域 (02)
ErrCodeAuthTokenExpired = 40201 // Token 已过期
ErrCodeAuthSignInvalid = 40202 // 签名校验失败
ErrCodeAuthForbidden = 40203 // 无操作权限
// ===== 5xx 服务端错误 =====
// 数据库域 (01)
ErrCodeDBTimeout = 50101 // 数据库查询超时
ErrCodeDBConnectionLost = 50102 // 数据库连接丢失
ErrCodeDBDuplicateKey = 50103 // 唯一键冲突
// 依赖服务域 (02)
ErrCodeDepServiceDown = 50201 // 依赖服务不可用
ErrCodeDepServiceTimeout= 50202 // 依赖服务调用超时
)
审计红线: 禁止在错误码中使用
0 或 -1 作为有效错误码。禁止将不同错误域的错误码混用(例如将参数校验错误归入 5xxxx 域)。违反者将导致审计失败,需立即修复。
4.5 错误码断言审计清单
在 Go 代码审计中,针对错误码设计原则,需要逐项检查以下要点:
- 语义对齐检查:所有 4xxxx 错误码是否确实由客户端请求引发?所有 5xxxx 错误码是否确实由服务端内部故障引发?
- 层级完整性检查:错误码是否包含完整的三级结构?是否存在仅使用两位数字的错误码?
- 错误域隔离检查:不同错误域的错误码是否使用了不同的中类编号?是否存在跨域复用?
- 常量命名检查:错误码常量是否按照
ErrCode + 域 + 具体描述的格式命名?是否使用了魔法数字? - 审计日志关联检查:错误码是否在审计日志中完整记录?日志中是否包含错误码对应的中英文描述?
审计工具集成: 建议在 CI/CD 流水线中集成自定义 linter,自动检查错误码定义是否符合层级结构。例如:
golangci-lint 配合自定义规则,禁止出现 400、500 等不符合三级结构的错误码。