调用栈
通常来说为了看一个偶现的问题可以多打印些日志,然后等待复现。不过实际上可以通过 GoLang 异常时打印的堆栈查看问题出现的部分现场。
在如下的代码中,会在 Example()
函数中打印具体的错误栈信息。
package main
func main() {
slice := make([]string, 2, 4)
Example(slice, "hello", 10)
}
func Example(slice []string, str string, i int) {
panic("Want stack trace")
}
其中打印的栈信息如下。
panic: Want stack trace
goroutine 1 [running]: 协程1出的问题
main.Example(0xc420047f38, 0x2, 0x4, 0x46b0e2, 0x5, 0xa) 那个函数出的问题
/tmp/foobar.go:9 +0x39 具体代码位置
main.main()
/tmp/foobar.go:5 +0x72
exit status 2
接下来看下起打印的入参,对应的声明以及调用栈如下。
// Declaration
main.Example(slice []string, str string, i int)
// Call to Example by main.
slice := make([]string, 2, 4)
Example(slice, "hello", 10)
// Stack trace
main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)
可以看到打印的入参是 6 个,而实际函数中是 3 个。这跟 GoLang 中对象的表示方法相关,其中 Slice 包括了地址、长度、容量;String 包括了地址、长度;Int 单个数字。
方法
也就是将方法进行封装。
package main
import "fmt"
type trace struct{}
func main() {
slice := make([]string, 2, 4)
var t trace
t.Example(slice, "hello", 10)
}
func (t *trace) Example(slice []string, str string, i int) {
fmt.Printf("Receiver Address: %p\n", t)
panic("Want stack trace")
}
因为方法声明的是一个指针,在调用方法时,会传入该方法的对象指针,那么最终打印的栈为。
Receiver Address: 0x546fa8
panic: Want stack trace
goroutine 1 [running]:
main.(*trace).Example(0x546fa8, 0xc420047f38, 0x2, 0x4, 0x4b463a, 0x5, 0xa)
/tmp/foobar.go:16 +0x8c
main.main()
/tmp/foobar.go:11 +0x93
exit status 2