中间件里包了一层 recover 函数,线上还是能看到 `panic: runtime error: invalid memory address or nil pointer dereference` 把进程带走,那层函数确实被调用了,只是没起作用,日志里连一行记录都没留下。日志里能对上时间点,这种反而好排。
recover 只有直接在 defer 调用的函数里执行才有效,再包一层普通函数调用时它拿到的已经是恢复后的上下文,panic 继续向上传播,进程照样退出,而且日志里看不出异常。这一点先确认清楚,后面就不用反复试。
grep -n 'recover()' -r . grep -n 'defer' handler.go go test -run TestPanic -v ./...
这次卡住的地方就在「recover 无效」这一环,单独把它拎出来验证,别和其他改动混在同一次发布里。偶发问题先统计频率,一小时几次和一天一次,查法完全不同。
改完顺手把文档补一句,省得下次自己重新推一遍。
处理上按这个顺序:把恢复逻辑写在延迟调用的那个匿名函数里面;每个入口协程都单独做一层异常兜底处理才行;恢复之后立刻打印调用栈,把现场的信息留住。
结果对不对很好判断,故意触发 panic 的请求返回 500,进程仍然存活,日志里有完整的调用栈信息。
