package narco import ( "fmt" "log" "net/http" "os" "runtime" "github.com/codemodus/chain" "golang.org/x/net/context" ) type StackResponseFormatter func(err interface{}, stack []byte) string func GoFormatStack(err interface{}, stack []byte) string { return fmt.Sprintf("Server internal error: %s\n%s", err, stack) } func HtmlFormatStack(err interface{}, stack []byte) string { return fmt.Sprintf(`
%s`, err, stack) } type Recoverer struct { Logger *log.Logger ShowStackInResponse StackResponseFormatter StackOtherGoroutine bool } func NewRecoverer() *Recoverer { return &Recoverer{ Logger: log.New(os.Stdout, "[narco] ", log.LstdFlags), ShowStackInResponse: nil, StackOtherGoroutine: false, } } func (rec *Recoverer) Wrap() func(chain.Handler) chain.Handler { return func(other chain.Handler) chain.Handler { return chain.HandlerFunc(func(ctx context.Context, rw http.ResponseWriter, req *http.Request) { defer func() { err := recover() if err == nil { return } stack := make([]byte, 8*1024) stack = stack[:runtime.Stack(stack, rec.StackOtherGoroutine)] rec.Logger.Printf("PANIC: %s\n%s", err, stack) var message string if rec.ShowStackInResponse != nil { message = rec.ShowStackInResponse(err, stack) } else { message = http.StatusText(http.StatusInternalServerError) } Error(ctx, rw, message, http.StatusInternalServerError) }() other.ServeHTTPContext(ctx, rw, req) }) } }