recover必须在defer函数内直接调用才有效,否则恒返回nil;它仅捕获同goroutine的panic,不跨协程、不修复状态,仅实现软着陆。

recover 能捕获 panic,但日志不落盘是常态,不是 bug —— 根本原因是日志缓冲未刷新,不是 recover 失效。
recover 必须在 defer 函数里调用,否则永远返回 nil
直接写 recover() 没用,它只在 panic 正在传播、且当前 goroutine 尚未退出时有效。唯一可靠的位置是 defer func() 内部。
- 错误写法:
func f() { recover(); panic("x") }→recover()立即执行,此时还没 panic,必然返回nil - 正确写法:
func f() { defer func() { if r := recover(); r != nil { /* 处理 */ } }(); panic("x") } - 多个 defer 按后进先出执行,
recover()应放在最靠近 panic 的那个 defer 里(或至少确保它不被更外层的 panic 中断)
glog.Errorf 记录后必须手动调用 glog.Flush()
glog 默认缓冲写入:日志先存内存,不主动 flush 就不会写入 INFO、ERROR 等文件。panic 后函数虽能走 defer,但若没 flush,日志就丢在内存里,随 goroutine 结束而蒸发 —— 你可能只在 /var/log/messages 看到它,因为 glog 退回到 stderr 输出,被 syslog 拦截了。
- 关键操作顺序必须是:
glog.Errorf(...)→glog.Flush(),且都在同一个 defer 函数内 -
glog.Flush()是同步阻塞调用,别在高频循环里滥用,但在 panic 恢复路径下是必要开销 - 确认 glog 已初始化,比如启动时传了
-log_dir=/path/to/logs,否则Flush()也无目标可写
子 goroutine panic 不会被外层 recover 捕获
HTTP handler 里 defer + recover 只管本 goroutine;如果里面起 go fn(),而 fn panic 了,handler 的 recover 完全看不到。
- 所有显式启动的 goroutine 都得自带
defer func() { if r := recover(); r != nil { /* log + cleanup */ } }() - 别裸写
go something(),封装成safeGo(func(){...}),内部包好 recover 和资源清理 - recover 后别继续用可能已损坏的对象(比如 panic 来自空指针解引用,recover 后再用该指针仍会 crash)
recover 捕获的值类型不确定,别直接当 string 打印
recover() 返回 interface{},可能是 string、*runtime.Error、自定义 error,甚至 int 或结构体。用 err.(error) 强制断言会 panic(runtime 抛的 panic 类型不实现 error 接口)。
- 安全打印方式:
fmt.Sprintf("%v", r)或fmt.Sprint(r) - 要保留调用栈,得用
debug.PrintStack()或runtime.Stack(buf, false)显式抓取 - 生产环境别只记
r,至少补上goroutine id和 traceID(如果有),否则排查时无法定位上下文
真正难的不是写对 recover,而是确保每个可能 panic 的执行点——无论是主逻辑、子 goroutine 还是第三方库回调——都有对应的 defer + recover + flush + cleanup 组合。漏掉任意一层,日志就断链,问题就变黑盒。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











