Gracefully Capture and Log Go Panics with defer, recover, and debug.Stack
This article demonstrates a concise technique for intercepting Go panics, logging detailed error messages and stack traces using defer, recover, and debug.Stack, and explains when and how to apply it in development and production environments.
Background
In Go, a panic triggers a runtime error that terminates the program after printing an error message and a stack trace. While useful for debugging, developers often need to capture the panic, record it, and handle it gracefully instead of allowing an abrupt exit.
Problem Example
Consider a program that crashes with the following log entry:
2025/03/13 11:29:53 error: runtime error: index out of range [0] with length 0The message indicates an attempt to access the first element of an empty slice, but without additional context it can be hard to pinpoint the exact location.
Solution Using defer, recover, and debug.Stack
Insert the following deferred anonymous function into main (or any function where a panic might occur):
defer func() {
if r := recover(); r != nil {
log.Printf("error: %v
Stack trace: %s", r, debug.Stack())
}
}()This code does three things:
defer schedules the anonymous function to run just before the surrounding function returns, whether the return is normal or caused by a panic.
recover captures the panic value (typically an error message) and returns it; if no panic occurs, it returns nil.
debug.Stack() from the runtime/debug package generates a string containing the current goroutine’s stack trace, including file names and line numbers.
When a panic occurs, the deferred function logs a formatted error message together with the full stack trace, turning an opaque crash into actionable debugging information.
Resulting Log Output
2025/03/13 11:29:53 error: runtime error: index out of range [0] with length 0
Stack trace: goroutine 1 [running]:
runtime/debug.Stack()
/usr/local/go/src/runtime/debug/stack.go:26 +0x5e
main.main.func1()
/root/code/account/account.go:48 +0x6c
panic({0x15fba80?, 0xc0004b88d0?})
/usr/local/go/src/runtime/panic.go:792 +0x132
account/internal/push.NewMarketQuotationsMQ()
/root/code/account/internal/push/push.go:43 +0x245
account/internal/push.InitPush()
/root/code/account/internal/push/push.go:34 +0x18
main.main()
/root/code/account/account.go:55 +0x4fThe stack trace pinpoints the error to line 43 in push.go inside the NewMarketQuotationsMQ function, showing the exact execution path from main to the failing call.
Practical Benefits
Simplicity : Only a few lines of code are needed; no complex error‑handling framework is required.
Debugging Power : The stack trace provides a clear roadmap to the source of the panic, eliminating the need for scattered fmt.Println statements.
Graceful Recovery : By catching the panic, the program can log the issue and decide whether to continue running or shut down cleanly.
When to Use
Development : Quickly identify and fix bugs such as the slice‑index error shown in the example.
Production : Prevent crashes from bringing down services while still recording sufficient diagnostic information for later analysis; additional cleanup (e.g., closing files) can be added before exit.
Caveats
Scope of recover : recover only works inside a deferred function; calling it directly in the function body will not capture a panic.
Performance : Generating a stack trace with debug.Stack() incurs overhead, so use it judiciously in performance‑critical code.
Normal Flow : If no panic occurs, the deferred block runs harmlessly and recover returns nil, having no impact on regular execution.
Conclusion
The combination of defer, recover, and debug.Stack() offers a straightforward and effective way to handle Go panics. As demonstrated, it transforms vague runtime errors into precise, actionable diagnostics, making both prototyping and production services more robust and easier to maintain.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Ops Development & AI Practice
DevSecOps engineer sharing experiences and insights on AI, Web3, and Claude code development. Aims to help solve technical challenges, improve development efficiency, and grow through community interaction. Feel free to comment and discuss.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
