必须自定义recover中间件,defer中调用recover()并用debug.Stack()获取完整堆栈,显式传入echo.Context以记录请求路径、method、request_id等上下文,禁用默认Recover();HTTPErrorHandler不处理panic。

中间件里捕获 panic 并记录完整堆栈
Echo 默认的 Recover() 中间件只打印简单 panic 信息,不带文件行号和调用链。要在中间件中记录详细错误日志,必须自己接管 panic 恢复流程,用 debug.PrintStack() 或 runtime/debug.Stack() 获取完整堆栈。
实操建议:
- 禁用默认
echo.MiddlewareFunc(echo.Recover()),改用自定义中间件 - 在
defer中调用recover(),判断是否为nil再处理 - 用
fmt.Sprintf("%s", debug.Stack())获取字符串形式堆栈,避免直接打印到 Stderr - 把堆栈、当前请求路径、method、时间戳一并写入结构化日志(如
zerolog或logrus)
如何在中间件中获取请求上下文与错误关联信息
仅记录堆栈不够,还要知道这个 panic 发生在哪次请求上。Echo 的 echo.Context 在 panic 后仍可访问,但需在 defer 闭包中显式传入,否则会因作用域丢失而 panic。
常见错误现象:panic: runtime error: invalid memory address or nil pointer dereference —— 实际是 defer 里试图访问已失效的 c 变量。
实操建议:
- 在中间件函数签名里接收
c echo.Context,并在defer匿名函数参数中再次声明c echo.Context - 用
c.Request().URL.Path、c.Request().Method、c.Get("user_id")(如果已设置)补充上下文 - 避免在 defer 中调用可能 panic 的方法(如
c.JSON()),只做日志采集
日志格式要不要包含 trace ID 和 request ID
要。没有唯一标识的错误日志在并发请求下无法归因,尤其是微服务或负载均衡场景。Echo 本身不生成 request ID,需手动注入。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
使用场景:排查某个特定失败请求的全链路行为,或关联网关层、DB 层日志。
实操建议:
- 在最外层中间件(如
RequestID())生成并写入c.Set("request_id", id) - 错误日志中通过
c.GetString("request_id")提取,fallback 到uuid.NewString() - trace ID 若集成 OpenTelemetry,优先从
otel.GetTextMapPropagator().Extract()中提取,而非硬编码 - 注意:不要把 trace ID 当作敏感字段打在日志里泄露给客户端(如响应头中)
为什么不能只依赖 echo.HTTPErrorHandler
echo.HTTPErrorHandler 只处理已知错误(如 c.JSON(500, err) 显式返回),对 panic 完全无感知。它在 return 后触发,而 panic 会跳过所有 defer 和 return,直接终止 goroutine。
性能影响:若在 HTTPErrorHandler 里强行尝试 recover,会导致逻辑混乱且不可靠;兼容性上,Echo v4/v5 均未改变该行为。
实操建议:
- 把错误日志逻辑严格放在 recover 中间件里,
HTTPErrorHandler仅用于统一响应格式 - 二者分工明确:recover 中间件负责「记录」,HTTPErrorHandler 负责「返回」
- 避免在
HTTPErrorHandler中调用c.Logger().Error()记录 panic 类错误——它根本收不到
真正难的不是拿到堆栈,而是确保每次 panic 都能稳定附带请求上下文、trace ID 和业务标识。很多团队漏掉的是 request ID 注入时机——必须在所有其他中间件之前注册,否则后续中间件 panic 时还拿不到 ID。

















