63 lines
1.5 KiB
Go
63 lines
1.5 KiB
Go
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(`<h2>Server internal error: %s</h2><pre>%s</pre>`, 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)
|
|
})
|
|
}
|
|
}
|