package negroni import ( "fmt" "log" "net/http" "os" "runtime" "runtime/debug" ) // Recovery is a Negroni middleware that recovers from any panics and writes a 500 if there was one. type Recovery struct { Logger *log.Logger PrintStack bool ErrorHandlerFunc func(interface{}) StackAll bool StackSize int } // NewRecovery returns a new instance of Recovery func NewRecovery() *Recovery { return &Recovery{ Logger: log.New(os.Stdout, "[negroni] ", 0), PrintStack: true, StackAll: false, StackSize: 1024 * 8, } } func (rec *Recovery) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { defer func() { if err := recover(); err != nil { if rw.Header().Get("Content-Type") == "" { rw.Header().Set("Content-Type", "text/plain; charset=utf-8") } rw.WriteHeader(http.StatusInternalServerError) stack := make([]byte, rec.StackSize) stack = stack[:runtime.Stack(stack, rec.StackAll)] f := "PANIC: %s\n%s" rec.Logger.Printf(f, err, stack) if rec.PrintStack { fmt.Fprintf(rw, f, err, stack) } if rec.ErrorHandlerFunc != nil { func() { defer func() { if err := recover(); err != nil { rec.Logger.Printf("provided ErrorHandlerFunc panic'd: %s, trace:\n%s", err, debug.Stack()) rec.Logger.Printf("%s\n", debug.Stack()) } }() rec.ErrorHandlerFunc(err) }() } } }() next(rw, r) }