recover不会自动触发通知,它仅在defer中捕获当前goroutine的panic并恢复执行;告警需手动在recover非nil时嵌入日志、HTTP请求等逻辑,且须防二次panic、加兜底与降级。

recover 本身不会自动触发任何通知
recover 只是一个内置函数,作用是在 defer 中捕获当前 goroutine 的 panic,并让程序继续运行。它不带日志、不发 HTTP 请求、不调用告警服务——这些全得你手动加。常见误区是以为只要写了 recover,系统就“自动告警”了,结果 panic 被吞掉后石沉大海。
在 defer + recover 里嵌入告警逻辑才有效
必须把告警代码(比如发钉钉、写日志、调用 Prometheus Alertmanager API)放在 defer 函数内部,且在 recover() 有返回值时执行。否则即使 panic 发生,也不会走告警分支。
- 确保
defer在可能 panic 的代码之前注册(通常放在函数开头) -
recover()返回nil表示没发生 panic,非nil才该发告警 - 避免在告警逻辑里再 panic(比如 HTTP 超时未处理),否则会二次崩溃
- 建议用结构化日志(如
zap)记录 panic 的stack trace,而不仅是错误消息
func handleRequest() {
defer func() {
if r := recover(); r != nil {
// 记录完整堆栈
log.Error("panic recovered", zap.Any("err", r), zap.String("stack", string(debug.Stack())))
// 触发告警:发 webhook、写指标、推消息等
alertPanic(r)
}
}()
// 可能 panic 的业务代码...
}
告警通道选型要考虑失败兜底
线上环境不能假设告警一定成功。比如钉钉机器人超时、网络抖动、Prometheus Pushgateway 不可用——这些都会导致“看似 recover 了,实则无人知晓”。
- 优先写本地日志(
/var/log/app/panic.log),确保至少落盘 - 异步发告警(用 channel + worker goroutine),避免阻塞主流程
- 对关键告警加重试(最多 2 次)和降级:HTTP 失败 → 写文件 → 文件满则轮转
- 避免在
recover块里做耗时操作(如数据库查询),goroutine 可能已处于不稳定状态
注意 recover 无法捕获所有崩溃场景
recover 只对当前 goroutine 的 panic 有效,以下情况它完全无效:
立即学习“go语言免费学习笔记(深入)”;
- 程序被
os.Exit()强制退出 - 发生
runtime.Goexit()(不是 panic) - 出现栈溢出、内存耗尽、SIGKILL 等 OS 级终止
- 其他 goroutine 的 panic(需各自独立 defer + recover)
所以真正可靠的告警体系,得结合进程级监控(如 systemd 日志监听、supervisor 事件钩子)+ 应用内 panic 捕获 + 健康检查接口三者联动。单靠 recover 做告警,就像只靠门铃防小偷——响了说明有人来了,但没响不代表安全。


















