Skip to main content

Overview

The recover middleware catches panics in handlers and converts them to proper error responses. It prevents your server from crashing and logs stack traces for debugging.

Installation

import "github.com/go-mizu/mizu/middlewares/recover"

Quick Start

app := mizu.New()

// Add as first middleware
app.Use(recover.New())

Configuration

OptionTypeDefaultDescription
StackSizeint4096Stack trace buffer size
DisableStackAllboolfalseLimit to current goroutine
DisablePrintStackboolfalseDisable stack logging
ErrorHandlerfunc(*mizu.Ctx, any, []byte) error-Custom panic handler
Logger*slog.Loggerc.Logger()Custom logger

Examples

Basic Recovery

app.Use(recover.New())

app.Get("/", func(c *mizu.Ctx) error {
    panic("something went wrong") // Caught by recover
})
// Returns 500 Internal Server Error

Custom Error Handler

app.Use(recover.WithOptions(recover.Options{
    ErrorHandler: func(c *mizu.Ctx, err any, stack []byte) error {
        // Log to error tracking service
        errorTracker.Report(err, string(stack))

        return c.JSON(500, map[string]string{
            "error": "Internal server error",
        })
    },
}))

Disable Stack Trace

// Production: don't log stack traces
app.Use(recover.WithOptions(recover.Options{
    DisablePrintStack: true,
}))

Custom Logger

logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

app.Use(recover.WithOptions(recover.Options{
    Logger: logger,
}))

With Request ID

app.Use(requestid.New())
app.Use(recover.WithOptions(recover.Options{
    ErrorHandler: func(c *mizu.Ctx, err any, stack []byte) error {
        c.Logger().Error("panic recovered",
            "error", err,
            "request_id", requestid.Get(c),
            "stack", string(stack),
        )
        return c.Text(500, "Internal Server Error")
    },
}))

API Reference

func New() mizu.Middleware
func WithOptions(opts Options) mizu.Middleware

Log Output

{
    "level": "ERROR",
    "msg": "panic recovered",
    "error": "runtime error: index out of range",
    "stack": "goroutine 1 [running]:\nmain.handler..."
}

Technical Details

The recover middleware uses Go’s built-in defer and recover() mechanisms to catch panics in handlers. Here’s how it works internally:

Implementation Architecture

  1. Panic Recovery: A deferred function wraps the next handler, catching any panic that occurs during request processing
  2. Stack Trace Capture: Uses runtime/debug.Stack() to capture the full stack trace when a panic occurs
  3. Stack Size Management: Truncates stack traces to the configured StackSize (default 4096 bytes) to prevent memory issues
  4. Logging Integration: Integrates with Go’s log/slog package for structured logging of panic information
  5. Error Propagation: Converts recovered panics into proper HTTP error responses

Key Implementation Details

  • The middleware returns a closure that wraps the next handler with a deferred recovery function
  • When DisablePrintStack is false, debug.Stack() captures the current goroutine’s stack trace
  • The stack trace is truncated if it exceeds StackSize to prevent excessive memory usage
  • If a custom ErrorHandler is provided, it receives the panic value and stack trace for custom handling
  • Default behavior returns a 500 Internal Server Error with the standard HTTP status text
  • The logger defaults to c.Logger() if no custom logger is specified

Performance Considerations

  • Minimal overhead when no panic occurs (just a defer statement)
  • Stack trace capture only happens during panics
  • Configurable stack size prevents unbounded memory allocation
  • Option to disable stack printing for production environments

Best Practices

  • Always add as the first middleware
  • Log panics for debugging
  • Don’t expose stack traces to users in production
  • Use with request ID for correlation

Testing

The recover middleware includes comprehensive test coverage for all features and edge cases:
Test CaseDescriptionExpected Behavior
TestNew/recovers from panicHandler that panics with “test panic”Returns 500 Internal Server Error status
TestNew/passes through normal requestsHandler that returns normally without panicReturns 200 OK with response body “ok”
TestWithOptions_ErrorHandlerCustom error handler captures panic and stackReturns 503 Service Unavailable with “custom error”, captures panic value and stack trace
TestWithOptions_DisablePrintStackDisablePrintStack option enabledReturns 500 status, no stack trace in log output
TestWithOptions_CustomLoggerCustom slog.Logger providedPanic is logged with “panic recovered” message and panic details
TestWithOptions_StackSizeStackSize set to 100 bytesCaptured stack trace is truncated to maximum 100 bytes