默认的gin.recovery()不够用,因其仅捕获panic、打印堆栈到终端并返回500,不支持写入文件日志、环境区分、trace id注入或自定义响应结构,导致线上问题难以排查与监控。

为什么默认的 gin.Recovery() 不够用
默认 Recovery 中间件只做两件事:捕获 panic、打印堆栈到终端、返回 500 Internal Server Error。它不记录日志到文件,不区分环境(开发/生产),不携带 trace ID,也不支持自定义响应结构。一旦线上出 panic,你只能翻控制台,没法查上下文、没法告警、没法对接 ELK。
如何用 RecoveryWithWriter() 写入文件日志
关键不是“能不能写文件”,而是“怎么让 Recovery 把错误导向你指定的 writer”。gin.RecoveryWithWriter() 接收一个 io.Writer,比如 *os.File 或 *lumberjack.Logger:
- 先用
os.OpenFile()创建日志文件句柄,设置os.O_CREATE | os.O_APPEND | os.O_WRONLY - 推荐用
gopkg.in/natefinch/lumberjack.v2做轮转,避免单文件爆炸 - 传给
gin.RecoveryWithWriter()时注意:它只接管 panic 日志输出,不处理正常请求日志
示例片段:
file, _ := os.OpenFile("panic.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
r.Use(gin.RecoveryWithWriter(file))
怎样在 Recovery 中注入自定义错误响应逻辑
直接替换 gin.Recovery() 不行——它内部硬编码了响应格式。正确做法是自己写一个函数,返回 gin.HandlerFunc,并在 defer + recover 里控制流程:
诊断并恢复通过 SSH 隧道连接的 OpenClaw 节点。用于解决配对必需错误、隧道冲突、远程端点错误以及 SSH 目标配置错误等问题。
- 必须用
defer包裹recover(),且放在 handler 开头 - 调用
c.Abort()阻止后续中间件和路由执行 - 根据
gin.Mode()判断是否暴露原始 error(开发环境可显示,生产环境只返回通用提示) - 手动调用
c.JSON()或c.Status(),不要依赖默认行为
常见漏点:recover() 返回的是 interface{},需断言为 error 或用 fmt.Sprintf("%v", err) 安全转字符串。
Recovery 中间件的执行顺序为什么不能乱放
Recovery 必须在所有可能 panic 的中间件之后注册,否则它捕不到。比如你在自定义 auth 中间件里做了空指针解引用,但 Recovery 放在 auth 前面,那 panic 就直接崩了。
- 典型安全顺序:
Logger()→Recovery()→ 其他业务中间件(auth、cors、token)→ 路由 - 如果用了
r.Group()并在 group 上挂中间件,注意 Recovery 是全局生效还是仅 group 生效 - 切忌在 Recovery 里再调用
c.Next()—— 它本就不该继续往下走
最常被忽略的一点:Recovery 只捕获当前 goroutine 的 panic。如果你在 goroutine 里起异步任务并 panic,它完全不管——那种情况得自己在 goroutine 里套 defer+recover。










