package narco import ( "fmt" "io" "log" "net/http" "os" "runtime" "github.com/codemodus/chain" "golang.org/x/net/context" ) type StackResponseFormatter func(w io.Writer, err interface{}, stack []byte) func GoFormatStack(w io.Writer, err interface{}, stack []byte) { fmt.Fprintf(w, "Server internal error: %s\n%s", err, stack) } func HtmlFormatStack(w io.Writer, err interface{}, stack []byte) { fmt.Fprintf(w, `
%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, h http.ResponseWriter, req *http.Request) { defer func() { err := recover() if err == nil { return } // h.Header()["Content-Type"] = []string{"text/html; charset=utf-8"} h.WriteHeader(http.StatusInternalServerError) stack := make([]byte, 8*1024) stack = stack[:runtime.Stack(stack, rec.StackOtherGoroutine)] rec.Logger.Printf("PANIC: %s\n%s", err, stack) if rec.ShowStackInResponse != nil { rec.ShowStackInResponse(h, err, stack) } else { fmt.Fprintf(h, "%s\n", http.StatusText(http.StatusInternalServerError)) } }() other.ServeHTTPContext(ctx, h, req) }) } }