如何获取紧急情况的堆栈跟踪(并存储为变量)

问题描述:

As we all know, panics produce a stacktrace to stdout (Playground link).:

panic: runtime error: index out of range
goroutine 1 [running]:
main.main()
    /tmp/sandbox579134920/main.go:9 +0x20

And it seems when you recover from a panic, recover() returns only an error which describes what caused the panic (Playground link).

runtime error: index out of range

My question is, is it possible to store the stacktrace which is written to stdout? This provides much better debugging information than the string runtime error: index out of range because it shows the exact line in a file which caused the panic.

众所周知,恐慌会向stdout生成堆栈跟踪(游乐场链接)。: p>

 恐慌:运行时错误:索引超出范围
goroutine  1 [正在运行]:
main.main()
 /tmp/sandbox579134920/main.go:9 + 0x20 
  code>  pre> 
 
 

似乎您是从 恐慌, recover() code>仅返回描述错误原因的 error code>(游乐场链接)。 p>

 运行时错误:索引超出范围
  code>  pre> 
 
 

我的问题是,是否可以存储其中的堆栈跟踪 被写到标准输出? 与字符串运行时错误:索引超出范围 code>相比,这提供了更好的调试信息,因为它显示了引起恐慌的文件中的确切行。 p> div>

Like @Volker mentioned above, and what was posted as a comment, we can use the runtime/debug package.

package main

import (
    "fmt"
    "runtime/debug"
)

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("stacktrace from panic: 
" + string(debug.Stack()))
        }
    }()

    var mySlice []int
    j := mySlice[0]

    fmt.Printf("Hello, playground %d", j)
}

prints

stacktrace from panic: 
goroutine 1 [running]:
runtime/debug.Stack(0x1042ff18, 0x98b2, 0xf0ba0, 0x17d048)
    /usr/local/go/src/runtime/debug/stack.go:24 +0xc0
main.main.func1()
    /tmp/sandbox973508195/main.go:11 +0x60
panic(0xf0ba0, 0x17d048)
    /usr/local/go/src/runtime/panic.go:502 +0x2c0
main.main()
    /tmp/sandbox973508195/main.go:16 +0x60

Playground link.

Create a log file to add the stacktrace to the file for stdout or stderr. This will add the data including the time with line of the error inside a file.

package main

import (
    "log"
    "os"
    "runtime/debug"
)

func main() {

    defer func() {
        if r := recover(); r != nil {
            log.Println(string(debug.Stack()))
        }
    }()

    //create your file with desired read/write permissions
    f, err := os.OpenFile("filename", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
    if err != nil {
        log.Println(err)
    }

    //set output of logs to f
    log.SetOutput(f)
    var mySlice []int
    j := mySlice[0]

    log.Println("Hello, playground %d", j)

    //defer to close when you're done with it, not because you think it's idiomatic!
    f.Close()
}

Working example on Go playground