golang当中panic的时候如果启动的goroutine比较多,刷的信息满屏都是,在终端工具上因为刷的信息太多,找不到前边的信息,因此很有必要程序自己捕获panic,并且将错误信息输出到文件当中,以便定位排查问题。
以下是一段捕获panic的代码
package main
import (
"fmt"
"os"
"runtime/debug"
"time"
)
func PanicHandler() {
exeName := os.Args[0] //获取程序名称
now := time.Now() //获取当前时间
pid := os.Getpid() //获取进程ID
time_str := now.Format("20060102150405") //设定时间格式
fname := fmt.Sprintf("%s-%d-%s-dump.log", exeName, pid, time_str) //保存错误信息文件名:程序名-进程ID-当前时间(年月日时分秒)
fmt.Println("dump to file ", fname)
f, err := os.Create(fname)
if err != nil {
return
}
defer f.Close()
if err := recover(); err != nil {
f.WriteString(fmt.Sprintf("%v\r\n", err)) //输出panic信息
f.WriteString("========\r\n")
}
f.WriteString(string(debug.Stack())) //输出堆栈信息
}
以下是测试程序,这里我们测试除数为0的异常
package main
import (
"fmt"
//"sync"
)
func test_panic() {
n := 0
i := 5 / n
fmt.Println(i)
}
func main() {
defer PanicHandler()
test_panic()
/*
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer PanicHandler()
test_panic()
wg.Done()
}()
wg.Wait()
*/
}
捕获的错误信息如下:
runtime error: integer divide by zero
========
F:/MyProject/Go/src/panictest/panicHandler.go:31 (0x4017c2)
PanicHandler: f.WriteString(string(debug.Stack())) //输出堆栈信息
c:/go/src/runtime/asm_amd64.s:437 (0x4543e5)
call32: CALLFN(·call32, 32)
c:/go/src/runtime/panic.go:423 (0x4291f7)
gopanic: reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
c:/go/src/runtime/panic.go:24 (0x427740)
panicdivide: panic(divideError)
c:/go/src/runtime/signal_windows.go:166 (0x43a31f)
sigpanic: panicdivide()
F:/MyProject/Go/src/panictest/main.go:10 (0x401073)
test_panic: i := 5 / n
F:/MyProject/Go/src/panictest/main.go:16 (0x40119c)
main: test_panic()
c:/go/src/runtime/proc.go:111 (0x42b78e)
main: main_main()
c:/go/src/runtime/asm_amd64.s:1721 (0x456821)
goexit: BYTE $0x90 // NOP
至此获取错误并且存储的功能已经实现。
最后注意,如果是启动的多goroutine,需要在每个goroutine执行函数的时候,写上defer PanicHandler()
否则的话是捕获不到其他goroutine当中的painc信息的。