前言
假如程序会莫名down掉,我们必须记录日志方便后期排查原因。
这里就说下怎样记录崩溃日志。
程序
1 | func main() { |
代码说明
1 | defer func() { |
这样写的作用是
登记一个匿名函数,注册到当前 goroutine 的退出队列,不立即执行,在
goroutine退出时执行。
defer:把后面的调用 注册到当前 goroutine 的退出队列,不立即执行。
逐行拆解:
| 代码 | 作用 |
|---|---|
defer func() { ... }() |
把匿名函数注册成“main 函数返回前”一定会执行的逻辑;即使后面代码 panic 了,它也会被执行。 |
recover() |
只在 defer 里有效;如果当前 goroutine 正在 panic,它会返回 panic 的值,并终止 panic 的传播;否则返回 nil。 |
debug.Stack() |
立即生成完整的函数调用栈(从 panic 点一路回溯到 main),方便定位是哪一行代码触发了 panic。 |
zlog.LogError(...) |
把 panic 原因 + 堆栈写进你配置的日志文件(或控制台),而不是只打印到标准输出。 |
time.Sleep(100 * time.Millisecond) |
给磁盘/文件句柄一点缓冲时间,确保日志真的落盘,防止进程立刻退出时日志还没写完。 |
os.Exit(1) |
主动退出整个进程,返回码 1,告诉操作系统“我是异常退出的”,方便脚本、容器、守护进程识别。 |
协程中
我这里新建了一个协程执行逻辑
1 | go handleMessages() |
那么新建的协程中也要做相应的处理
1 | func handleMessages() { |
log.Fatal
启动尽量不要用log.Fatal
1 | log.Fatal(http.ListenAndServe(":"+config.PORT, nil)) |
我们可以在退出前做一些常见的操作(打印日志、发告警、尝试备用端口、让 systemd/k8s 重启策略接管等)再退出。
1 | // ✅ 捕获 ListenAndServe 的错误,不要用 log.Fatal |