比如下面这个例子,我们使用 Caller 方法,将文件名、行号、堆栈函数打印出来:
// 在 prog.go 文件,main 库中调用 call 方法func call(skip int) bool { //24 行pc,file,line,ok := runtime.Caller(skip) //25 行pcName := runtime.FuncForPC(pc).Name() //26 行fmt.Println(fmt.Sprintf("%v %s %d %s",pc,file,line,pcName)) //27 行return ok //28 行} //29 行
打印出的第一层堆栈函数的信息:
4821380 /tmp/sandbox064396492/prog.go 25 main.call
打印堆栈的时候就有这么一个逻辑:先去本地查找是否有这个源代码文件,如果有的话,获取堆栈所在的代码行数,将这个代码行数直接打印到控制台中。
// 打印具体的堆栈信息func stack(skip int) []byte {...// 循环从第 skip 层堆栈到最后一层for i := skip; ; i++ {pc, file, line, ok := runtime.Caller(i)// 获取堆栈函数所在源文件if file != lastFile {data, err := ioutil.ReadFile(file)if err != nil {continue}lines = bytes.Split(data, []byte{'\n'})lastFile = file}// 打印源代码的内容fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))}return buf.Bytes()}
这样,打印出来的堆栈信息形如:
/Users/yejianfeng/Documents/gopath/pkg/mod/github.com/gin-gonic/gin@v1.7.2/context.go:165 (0x1385b5a)(*Context).Next: c.handlers[c.index](c)
