How to get the stacktrace of a panic (and store as a variable)

DebuggingGoPanic

Debugging Problem Overview


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.

Debugging Solutions


Solution 1 - Debugging

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: \n" + 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.

Solution 2 - Debugging

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionhlin117View Question on Stackoverflow
Solution 1 - Debugginghlin117View Answer on Stackoverflow
Solution 2 - DebuggingHimanshuView Answer on Stackoverflow