Generating Stack Traces in Go

Mar 19 2014

The ability to generate a stack trace can prove to be very useful, especially when writing log files. The Go language's runtime package provides a helper for generating a stack trace. Here's how to use it.

Simply put, you can grab a stack trace from just about anywhere in a Go program. Import the runtime package, create a []byte to use as a buffer, and then call runtime.Stack():

import (
    "runtime"   
    "fmt"
)

func main() {
    trace := make([]byte, 1024)
    count := runtime.Stack(trace, true)
    fmt.Printf("Stack of %d bytes: %s", count, trace)
}

The above will print a simple stack trace. Note that the second argument to runtime.Stack() is a boolean flag. If true, every running go routine will also dump a stack. If false, only the current go routine will dump a stack trace.

More often, it is useful to print a stack trace as a response to an error or panic. Here's a more complex example that shows not only the stack trace gathering, but how it might be employed in response to a panic().

package main

import (
    "runtime"
    "fmt"
)

func main() {
    outer()
}

func outer() {
    inner()
}

func inner() {

    defer func() {
        if err := recover(); err != nil {
            trace := make([]byte, 1024)
            count := runtime.Stack(trace, true)
            fmt.Printf("Recover from panic: %s\n", err)
            fmt.Printf("Stack of %d bytes: %s\n", count, trace)
        }
    }()

    panic("Fake error!")


}

To make the trace a little more interesting, I artificially added some empty layers. In a nutshell, main() calls outer(), which calls inner(), which defers an panic handler and then panics.

The panic handler then calls recover() and, if there is a panic to recover from, prints the panic message along with a stack trace. Here's what it looks like when we run it:

$ go run stack.go
Recover from panic: Fake error!
Stack of 402 bytes: goroutine 1 [running]:
main.func·001()
        /Users/mbutcher/Code/Go/src/scratch/stack.go:21 +0xab
runtime.panic(0x80b80, 0x2101fb150)
        /usr/local/Cellar/go/1.2/libexec/src/pkg/runtime/panic.c:248 +0x106
main.inner()
        /Users/mbutcher/Code/Go/src/scratch/stack.go:27 +0x68
main.outer()
        /Users/mbutcher/Code/Go/src/scratch/stack.go:13 +0x1a
main.main()
        /Users/mbutcher/Code/Go/src/scratch/stack.go:9 +0x1a