panicguard

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 23, 2026 License: MIT Imports: 8 Imported by: 0

README

go-panicguard

CI Go Reference License

Panic recovery utilities for Go — safe goroutines, panic-to-error conversion, and HTTP handler protection

Installation

go get github.com/philiprehberger/go-panicguard

Usage

import "github.com/philiprehberger/go-panicguard"
Safe goroutines
panicguard.Go(func() {
    // If this panics, the panic is recovered and the OnPanic hook is called.
    riskyWork()
})
Goroutine with error result
ch := panicguard.GoErr(func() error {
    return doWork()
})
if err := <-ch; err != nil {
    // err is either the returned error or a *panicguard.PanicError
    log.Println(err)
}
Deferred panic-to-error conversion
func handler() (err error) {
    defer func() {
        if e := panicguard.Recover(recover()); e != nil {
            err = e
        }
    }()
    riskyOperation()
    return nil
}
Named goroutines
panicguard.GoNamed("email-sender", func() {
    // If this panics, the name "email-sender" is included in the panic log.
    sendEmails()
})
Context-aware goroutines
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

panicguard.GoCtx(ctx, func(ctx context.Context) {
    // ctx is passed through; panics are still recovered.
    fetchData(ctx)
})
Panic statistics
panicguard.Go(func() { panic("oops") })
time.Sleep(100 * time.Millisecond)

stats := panicguard.Stats()
fmt.Println(stats.TotalPanics) // 1
fmt.Println(stats.LastPanic)   // time of the last panic
fmt.Println(stats.LastValue)   // "oops"

panicguard.ResetStats() // reset counters (useful in tests)
Typed panic recovery
defer func() {
    if pe, ok := panicguard.RecoverAs[*panicguard.PanicError](recover()); ok {
        log.Printf("panic value: %v", pe.Value)
    }
}()
Global panic hook
panicguard.SetOnPanic(func(recovered any, stack []byte) {
    log.Printf("panic recovered: %v\n%s", recovered, stack)
})
HTTP middleware
mux := http.NewServeMux()
mux.HandleFunc("/", handler)
http.ListenAndServe(":8080", panicguard.Middleware(mux))
Custom recovery response
middleware := panicguard.MiddlewareWithHandler(func(w http.ResponseWriter, r *http.Request, recovered any) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusInternalServerError)
    fmt.Fprintf(w, `{"error":"%v"}`, recovered)
})
http.ListenAndServe(":8080", middleware(mux))

API

Function / Type Description
PanicError Wraps a recovered panic value with Value and Stack fields
PanicError.Error() Returns "panic: {value}"
PanicStats Holds TotalPanics, LastPanic, and LastValue fields
Go(fn) Runs fn in a goroutine with panic recovery
GoErr(fn) Runs fn in a goroutine, returns a channel with the error result or *PanicError
GoNamed(name, fn) Runs fn in a named goroutine; name is included in panic reports
GoCtx(ctx, fn) Runs fn in a goroutine, passing ctx; panics are recovered
Recover(r) Call in a deferred func with recover() to convert a panic into a *PanicError
RecoverAs[T](r) Typed panic recovery — extracts error type T from a recovered value
SetOnPanic(fn) Sets a global handler called on every recovered panic
Stats() Returns global panic statistics (thread-safe)
ResetStats() Resets global panic statistics to zero values
Middleware(next) HTTP middleware that recovers panics and returns 500
MiddlewareWithHandler(onPanic) HTTP middleware with a custom recovery response handler

Development

go test ./...
go vet ./...

License

MIT

Documentation

Overview

Package panicguard provides panic recovery utilities for Go.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Go

func Go(fn func())

Go runs fn in a new goroutine with panic recovery. If fn panics, the panic is recovered and the global OnPanic handler is called (if set). The panic is not propagated.

func GoCtx added in v0.2.0

func GoCtx(ctx context.Context, fn func(context.Context))

GoCtx runs fn in a new goroutine with panic recovery, passing ctx to fn. If ctx is already cancelled before fn panics, the panic is still recovered.

func GoErr

func GoErr(fn func() error) <-chan error

GoErr runs fn in a new goroutine and returns a buffered channel that will receive the result. If fn returns an error, it is sent on the channel. If fn panics, a *PanicError is sent instead. The channel is closed after the value is sent.

func GoNamed added in v0.2.0

func GoNamed(name string, fn func())

GoNamed runs fn in a new goroutine with panic recovery. The name is included in the PanicError and passed to the global OnPanic hook for debugging.

func Middleware

func Middleware(next http.Handler) http.Handler

Middleware returns an http.Handler that recovers from panics in the next handler. When a panic occurs, it writes a 500 Internal Server Error response and calls the global OnPanic hook if one is set.

func MiddlewareWithHandler

func MiddlewareWithHandler(onPanic func(w http.ResponseWriter, r *http.Request, recovered any)) func(http.Handler) http.Handler

MiddlewareWithHandler returns HTTP middleware that recovers from panics and delegates the response to the provided onPanic function. This allows callers to customize the error response (e.g., returning JSON, logging, etc.).

func Recover

func Recover(r any) error

Recover converts a recovered panic value into a *PanicError. It is meant to be used inside a deferred function alongside the built-in recover():

defer func() {
    if err := panicguard.Recover(recover()); err != nil {
        // handle err (*PanicError)
    }
}()

Returns nil if r is nil (no panic occurred).

func RecoverAs added in v0.2.0

func RecoverAs[T error](r any) (T, bool)

RecoverAs attempts to extract a typed error from a recovered panic value. If r is nil, it returns the zero value of T and false. If r implements T directly, it returns the value and true. Otherwise, r is wrapped in a PanicError and errors.As is used to attempt the conversion.

func ResetStats added in v0.2.0

func ResetStats()

ResetStats resets the global panic statistics to zero values. This is useful for testing.

func SetOnPanic

func SetOnPanic(fn func(recovered any, stack []byte))

SetOnPanic sets a global panic handler that is called whenever a panic is recovered by Go, GoErr, GoNamed, GoCtx, or the HTTP middleware. The handler receives the recovered value and the stack trace captured at the point of the panic. Pass nil to clear the handler.

Types

type PanicError

type PanicError struct {
	// Value is the value that was passed to panic().
	Value any
	// Stack is the raw stack trace captured via runtime/debug.Stack().
	Stack []byte
}

PanicError wraps a recovered panic value along with the stack trace captured at the point of the panic.

func (*PanicError) Error

func (e *PanicError) Error() string

Error returns a string representation of the panic in the format "panic: {value}".

type PanicStats added in v0.2.0

type PanicStats struct {
	// TotalPanics is the total number of panics recovered across all functions.
	TotalPanics int64
	// LastPanic is the time of the most recent recovered panic.
	LastPanic time.Time
	// LastValue is the value from the most recent recovered panic.
	LastValue any
}

PanicStats holds global panic statistics.

func Stats added in v0.2.0

func Stats() PanicStats

Stats returns the current global panic statistics.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL