Files
junhong_cmp_fiber/specs/003-error-handling/spec.md
huang fb83c9a706 feat: 实现统一错误处理系统 (003-error-handling)
- 新增统一错误码定义和管理 (pkg/errors/codes.go)
- 新增全局错误处理器和中间件 (pkg/errors/handler.go, internal/middleware/error_handler.go)
- 新增错误上下文管理 (pkg/errors/context.go)
- 增强 Panic 恢复中间件 (internal/middleware/recover.go)
- 新增完整的单元测试和集成测试
- 新增功能文档 (docs/003-error-handling/)
- 新增功能规范 (specs/003-error-handling/)
- 更新 CLAUDE.md 和 README.md
2025-11-15 12:17:44 +08:00

13 KiB
Raw Blame History

Feature Specification: Fiber 错误处理集成

Feature Branch: 003-error-handling
Created: 2025-11-14
Status: Draft
Input: User description: "我想把异常处理集成进来 - Fiber 错误处理集成,包括捕获错误、Panic 恢复、自定义错误处理程序"

Clarifications

Session 2025-11-14

  • Q: 当 Zap 日志系统(如远程日志服务)不可用时,系统应该如何处理错误日志? → A: 静默失败,丢弃日志,确保请求不受影响
  • Q: 当全局错误处理程序ErrorHandler本身执行时发生错误或 panic系统应该如何避免无限循环或崩溃 → A: 使用 defer + recover 保护 ErrorHandler,失败时仅返回 HTTP 500 状态码,空响应体
  • Q: 错误响应结构 ErrorResponse 是否需要包含 request_id 字段?当前 pkg/response/response.go 中没有此字段,但 FR-008 要求关联请求 ID。 → A: 不在响应体中包含 request_id仅在响应 Header 中添加 X-Request-ID
  • Q: 当 HTTP 响应已经部分发送给客户端(如已写入响应头或部分响应体)后发生错误,系统应该如何处理? → A: 静默失败,记录日志但不修改已发送的响应
  • Q: 当错误信息包含敏感数据如数据库连接字符串、内部文件路径、密钥ErrorHandler 应该如何识别并避免泄露到客户端响应或日志中? → A: 为所有错误返回通用消息(如"内部服务器错误"),原始详情仅记录到日志;日志访问受限

User Scenarios & Testing (mandatory)

User Story 1 - 统一错误响应格式 (Priority: P1)

当系统发生任何错误时,API 用户(前端开发者、移动端开发者、第三方集成商)需要接收到结构化、一致的错误响应,以便能够正确识别错误类型并向最终用户展示友好的错误信息。

Why this priority: 这是错误处理的核心功能,直接影响 API 的可用性和用户体验。统一的错误格式是所有后续错误处理功能的基础。

Independent Test: 可以通过调用任意一个会产生错误的 API 端点(如访问不存在的资源、提交无效数据),验证返回的错误响应是否包含标准的字段(错误码、错误消息、时间戳等),并且格式一致。

Acceptance Scenarios:

  1. Given 用户请求一个不存在的资源, When 系统找不到该资源, Then 系统返回包含错误码(如 404)、中文错误描述、时间戳的标准 JSON 响应
  2. Given 用户提交了格式错误的数据, When 系统验证失败, Then 系统返回包含错误码(如 400)、具体验证错误信息、时间戳的标准 JSON 响应
  3. Given 系统内部发生未预期的错误, When 处理请求时出现异常, Then 系统返回包含错误码(500)、通用错误描述(不暴露内部细节)、时间戳的标准 JSON 响应

User Story 2 - 系统稳定性保障(Panic 恢复) (Priority: P1)

当系统某个部分发生严重异常(panic)时,系统需要能够捕获并恢复,而不是整个服务崩溃,确保其他正在进行的请求不受影响,同时记录详细的错误信息供开发人员排查。

Why this priority: 这是系统可用性的关键保障。单个请求的错误不应该导致整个服务不可用,这直接关系到服务的稳定性和用户体验。

Independent Test: 可以创建一个测试端点故意触发 panic,验证系统是否能够捕获该 panic 并返回错误响应,同时其他端点仍然正常工作,且错误被记录到日志中。

Acceptance Scenarios:

  1. Given 某个 API 处理程序内部发生 panic, When 请求到达该端点, Then 系统捕获 panic,返回 500 错误响应,服务继续运行,其他请求不受影响
  2. Given 中间件处理过程中发生 panic, When 请求经过该中间件, Then 系统捕获 panic,返回错误响应,并记录完整的堆栈跟踪信息到日志
  3. Given 多个并发请求中有一个触发 panic, When 系统处理这些请求, Then 只有触发 panic 的请求返回错误,其他请求正常完成

User Story 3 - 业务错误分类处理 (Priority: P2)

运维人员和开发人员需要能够区分不同类型的错误(如客户端错误、服务端错误、业务逻辑错误),以便进行针对性的监控、告警和故障排查。

Why this priority: 这提升了系统的可维护性和可观测性,帮助团队更快地定位和解决问题,但不是系统能够运行的基础功能。

Independent Test: 可以触发不同类型的错误(验证失败、资源未找到、权限不足、系统内部错误),验证每种错误是否被正确分类,记录了适当的日志级别,并返回了相应的 HTTP 状态码。

Acceptance Scenarios:

  1. Given 用户提交了业务上不允许的操作, When 系统验证业务规则, Then 系统返回 400 系列错误码,记录为 Warn 级别日志,包含业务错误码和描述
  2. Given 系统依赖的外部服务不可用, When 尝试调用该服务, Then 系统返回 503 错误,记录为 Error 级别日志,包含重试提示
  3. Given 数据库连接失败, When 执行数据库操作, Then 系统返回 500 错误,记录为 Error 级别日志,触发告警,不暴露敏感信息给客户端

User Story 4 - 错误追踪和调试支持 (Priority: P3)

开发人员在排查问题时需要能够快速定位错误发生的位置和上下文,包括请求 ID、用户信息、错误堆栈等,以提高问题解决效率。

Why this priority: 这是运维和开发效率的提升,但不影响系统的核心功能和用户体验。

Independent Test: 可以触发一个错误,然后在日志中搜索该请求的 request_id,验证是否能找到完整的请求上下文(路径、方法、参数)和错误详情(堆栈、错误消息)。

Acceptance Scenarios:

  1. Given 系统发生错误, When 查看日志, Then 日志包含请求 ID、请求路径、用户标识(如有)、错误类型、错误消息、时间戳
  2. Given 需要追踪某个特定请求的完整流程, When 使用请求 ID 搜索日志, Then 可以找到该请求从接收到响应的所有日志条目
  3. Given panic 发生, When 查看错误日志, Then 日志包含完整的 goroutine 堆栈跟踪,指明 panic 发生的确切位置

Edge Cases

  • 当错误处理程序本身发生错误或 panic 时,使用 defer + recover 保护机制,返回 HTTP 500 状态码和空响应体,避免无限循环或服务崩溃
  • 当日志系统不可用时,系统采用静默失败策略,丢弃日志以确保请求响应不受影响
  • 当响应已经部分发送给客户端后发生错误,采用静默失败策略:仅记录错误日志,不修改已发送的响应内容(避免破坏响应格式)
  • 当错误信息包含敏感数据时,返回通用错误消息给客户端(如"内部服务器错误"),原始错误详情仅记录到受访问控制的日志系统
  • 当并发请求量极高时,错误处理通过异步日志和最小化处理逻辑确保不成为性能瓶颈(目标延迟 < 1ms)
  • 当客户端已断开连接时,错误处理仍会完成日志记录,但可跳过响应写入(Fiber 会自动处理已断开的连接)

Requirements (mandatory)

Functional Requirements

  • FR-001: 系统必须捕获所有路由处理程序和中间件中返回的错误,并统一处理;若响应已部分发送,则仅记录日志,不修改响应
  • FR-002: 系统必须捕获所有 panic 异常,防止服务崩溃,并将 panic 转换为可控的错误响应
  • FR-003: 系统必须为所有错误响应提供统一的 JSON 格式,包含错误码、错误消息、时间戳
  • FR-004: 系统必须支持自定义错误类型,允许指定特定的 HTTP 状态码和错误消息
  • FR-005: 系统必须记录所有错误到日志系统,包含请求上下文和错误详情;当日志系统不可用时采用静默失败策略
  • FR-006: 系统必须区分客户端错误(4xx)和服务端错误(5xx),并返回相应的状态码
  • FR-007: 系统必须在返回给客户端的错误响应中隐藏内部实现细节和敏感信息;所有错误返回通用错误消息,原始错误详情仅记录到受访问控制的日志系统
    • 敏感信息明确定义:以下信息类型严禁暴露给客户端
      • 数据库错误详情 (SQL 语句、表名、字段名、约束冲突详情)
      • 文件系统路径 (绝对路径、相对路径、文件名)
      • 堆栈跟踪信息 (文件名、行号、函数调用链)
      • 环境变量和配置值 (数据库连接串、API 密钥、服务地址)
      • 内部服务名称和版本号
      • 内存地址和对象引用
      • 第三方服务的错误详情 (仅返回通用的"外部服务错误")
    • 通用消息策略:所有 5xx 错误统一返回"内部服务器错误"或"服务暂时不可用",4xx 错误返回业务相关的友好提示
  • FR-008: 系统必须为每个错误关联请求 ID(通过响应 Header X-Request-ID 传递,不在响应体中包含),以便追踪和调试
  • FR-009: 系统必须支持配置全局错误处理程序,允许自定义错误处理逻辑;ErrorHandler 必须使用 defer + recover 保护,当其自身发生 panic 时返回 HTTP 500 空响应体
  • FR-010: panic 恢复后必须记录完整的堆栈跟踪信息到日志

Technical Requirements (Constitution-Driven)

Tech Stack Compliance:

  • 使用 Fiber 框架的错误处理机制(ErrorHandler)
  • 使用 Fiber Recover 中间件处理 panic
  • 使用 Zap 记录错误日志,配置为静默失败模式(日志失败不影响请求处理)
  • 集成现有的 pkg/response/ 统一响应格式
  • 使用 pkg/errors/ 定义的错误码

Architecture Requirements:

  • 错误处理中间件应该全局注册,在所有其他中间件之前
  • Recover 中间件应该在错误处理中间件之后注册
  • 自定义错误类型应该在 pkg/errors/ 包中定义
  • 错误响应格式应该通过 pkg/response/ 包统一处理
  • 所有日志消息使用中文
  • 错误消息支持中文(面向用户的错误消息)
  • 客户端错误响应仅包含通用错误消息和错误码,不暴露原始错误详情(如数据库错误、文件路径、堆栈跟踪)
  • 日志系统访问需配置适当的权限控制,防止敏感信息泄露

Go Idiomatic Design Requirements:

  • 错误处理遵循 Go 的显式错误返回模式
  • 使用标准 error 接口,支持 errors.As 和 errors.Is
  • Panic 只用于真正的不可恢复错误,业务错误使用 error 返回
  • 错误信息简洁明确,便于调试

API Design Requirements:

  • 所有错误响应使用统一 JSON 格式
  • HTTP 状态码与错误类型一致(400 系列=客户端错误, 500 系列=服务端错误)
  • 错误响应包含业务错误码,便于前端识别
  • 错误消息对用户友好,同时在日志中记录技术细节

Performance Requirements:

  • 错误处理不应显著增加请求延迟(< 1ms)
  • 日志记录使用异步方式,避免阻塞请求;日志失败时静默处理,不阻塞响应
  • Panic 恢复不应导致内存泄漏

Testing Requirements:

  • 为错误处理中间件编写单元测试
  • 为 Recover 中间件编写单元测试,包括 panic 场景
  • 为自定义错误类型编写测试
  • 为错误处理程序编写集成测试,覆盖各种错误场景
  • 测试错误日志记录功能
  • 测试并发场景下的错误处理

Key Entities (include if feature involves data)

  • Error: 表示系统中的错误,包含错误码、错误消息、HTTP 状态码、原始错误(用于错误链)
  • ErrorResponse: 表示返回给客户端的错误响应结构(JSON 响应体),包含 code(业务错误码)、message(错误描述)、timestamp(时间戳);request_id 通过响应 Header X-Request-ID 传递,不在响应体中
  • ErrorContext: 表示错误发生时的上下文信息,包含请求路径、方法、参数、用户信息等

Success Criteria (mandatory)

Measurable Outcomes

  • SC-001: 系统能够捕获 100% 的 panic,确保服务不会因单个请求崩溃而停止
  • SC-002: 所有 API 错误响应格式一致,包含必需字段(错误码、消息、时间戳)
  • SC-003: 错误日志记录率达到 100%,所有错误都被记录到日志系统(日志失败时静默处理,不影响响应)
  • SC-004: 客户端能够通过错误码准确识别错误类型,并采取相应的处理措施
  • SC-005: 开发人员能够在 5 分钟内通过请求 ID 定位到错误的完整上下文
  • SC-006: 错误处理增加的响应时间不超过 1ms
  • SC-007: 错误响应不包含任何内部实现细节(数据库错误、文件路径、堆栈跟踪等)
  • SC-008: 在高并发场景下(1000+ 并发请求),错误处理不会成为性能瓶颈