recovery

package
v1.5.6 Latest Latest
Warning

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

Go to latest
Published: Jun 29, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package recovery provides panic recovery middleware for celeris.

The middleware catches panics from downstream handlers, logs the stack trace via slog, and returns a configurable error response instead of crashing the server. Register it as early as possible in the middleware chain so it wraps all other handlers.

Basic usage with defaults (4 KB stack trace, JSON 500 response):

server.Use(recovery.New())

New accepts an optional Config to customise behaviour. Key fields:

  • Config.ErrorHandlerErr — preferred error handler (receives a typed error).
  • Config.ErrorHandler — legacy handler (receives any); kept for compatibility.
  • Config.BrokenPipeHandler — custom handler for broken pipe / ECONNRESET panics.
  • Config.StackSize — max bytes for stack trace capture (default 4096; 0 disables).
  • Config.DisableLogStack — suppress all panic log output when true.
  • Config.LogLevel — slog level for normal panic entries (default slog.LevelError).
  • Config.Logger — slog logger (default slog.Default).
  • Config.Skip / Config.SkipPaths — skip recovery for selected requests.

Panics with http.ErrAbortHandler are re-panicked to preserve standard library abort semantics. Broken pipe and ECONNRESET panics are logged at WARN level without a stack trace.

The package exports sentinel errors for use with errors.Is:

Documentation

Full guides and examples: https://goceleris.dev/docs/error-handling

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrPanic is the base sentinel for all recovery-caught panics.
	ErrPanic = errors.New("recovery: panic")

	// ErrPanicContextCancelled is returned when a panic occurs after
	// the request context has been cancelled.
	ErrPanicContextCancelled = errors.New("recovery: panic (context cancelled)")

	// ErrPanicResponseCommitted is returned when a panic occurs after
	// the response has already been partially written.
	ErrPanicResponseCommitted = errors.New("recovery: panic (response committed)")

	// ErrBrokenPipe is returned when a panic is caused by a broken pipe
	// or connection reset (client disconnected).
	ErrBrokenPipe = errors.New("recovery: broken pipe")
)

Functions

func New

func New(config ...Config) celeris.HandlerFunc

New creates a recovery middleware with the given config.

Performance note: by default `cfg.Logger` falls back to `slog.Default()`, which on most Go programs is the stdlib text-handler writing to `os.Stderr`. Stderr writes serialize on a global mutex inside Go's default handler. Under iouring/epoll's async-dispatch model the dispatch goroutine holds `cs.detachMu` across the `ProcessH1` call, so a synchronous `log.LogAttrs` here will gate the worker thread for the duration of the stderr write — sustained panic loads can drop engine throughput by an order of magnitude (~14×) and let slowloris-defence header-deadlines slip past on concurrent conns. If your handlers can panic at >100 req/sec (recovery middleware is usually the only thing standing between a buggy handler and a process crash), pass an explicit non-blocking Logger:

slog.New(slog.NewTextHandler(io.Discard, nil))  // drop logs
slog.New(myAsyncHandler)                        // async sink

or initialize `slog.SetDefault(...)` to a non-blocking handler at process startup.

Example
package main

import (
	"github.com/goceleris/celeris/middleware/recovery"
)

func main() {
	// Zero-config: catches panics, logs stack trace, returns 500 JSON.
	_ = recovery.New()
}
Example (CustomHandler)
package main

import (
	"github.com/goceleris/celeris"

	"github.com/goceleris/celeris/middleware/recovery"
)

func main() {
	// Custom error handler for panics.
	_ = recovery.New(recovery.Config{
		ErrorHandler: func(c *celeris.Context, err any) error {
			return c.String(500, "something went wrong: %v", err)
		},
	})
}
Example (StackAll)
package main

import (
	"github.com/goceleris/celeris/middleware/recovery"
)

func main() {
	// Capture all goroutine stacks (not just the panicking one).
	_ = recovery.New(recovery.Config{
		StackAll:  true,
		StackSize: 8192,
	})
}

Types

type Config

type Config struct {
	// Skip defines a function to skip this middleware for certain requests.
	Skip func(c *celeris.Context) bool

	// SkipPaths is a list of request paths to exclude from recovery.
	// Matching is exact (no glob or prefix support).
	SkipPaths []string

	// ErrorHandler handles the recovered panic value. Default: JSON 500 response.
	//
	// Prefer [ErrorHandlerErr] for new code: it receives a concrete
	// error value (panic-value-as-error wrapped if necessary), which
	// matches the pattern used by jwt/keyauth/basicauth/csrf and
	// supports errors.Is / errors.As. ErrorHandler is retained for
	// backwards compatibility; if both are set, ErrorHandlerErr wins.
	ErrorHandler func(c *celeris.Context, err any) error

	// ErrorHandlerErr is the error-typed counterpart of [ErrorHandler]
	// added in v1.3.4 to align with the rest of the auth middleware
	// family. The middleware wraps non-error panic values with
	// fmt.Errorf("recovery: panic: %v", r) before invocation.
	ErrorHandlerErr func(c *celeris.Context, err error) error

	// BrokenPipeHandler handles broken pipe / connection reset panics.
	// When nil, broken pipe panics are logged at WARN level without a stack
	// trace and the middleware returns a descriptive error.
	BrokenPipeHandler func(c *celeris.Context, err any) error

	// StackSize is the max bytes for stack trace capture. Default: 4096.
	// Set to 0 to disable stack capture (panic value, method, and path
	// are still logged). Negative values are silently replaced with the default.
	StackSize int

	// DisableLogStack disables all panic logging on recovery. When true,
	// panics are still caught and the error handler is called, but no log
	// entry is emitted. Default: false (panics are logged with stack traces).
	DisableLogStack bool

	// StackAll captures all goroutine stacks, not just the current one.
	// Default: false (current goroutine only).
	StackAll bool

	// DisableBrokenPipeLog suppresses the WARN-level log for broken pipe
	// and ECONNRESET panics. Default: false (log enabled).
	DisableBrokenPipeLog bool

	// Logger is the slog logger for panic logging. Default: slog.Default().
	Logger *slog.Logger

	// LogLevel is the slog level used for normal panic log entries.
	// Default: slog.LevelError. Broken pipe panics are always logged
	// at slog.LevelWarn regardless of this setting.
	LogLevel slog.Level
}

Config defines the recovery middleware configuration.

Jump to

Keyboard shortcuts

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