aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/go-chi/chi/middleware/recoverer.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/go-chi/chi/middleware/recoverer.go')
-rw-r--r--vendor/github.com/go-chi/chi/middleware/recoverer.go192
1 files changed, 192 insertions, 0 deletions
diff --git a/vendor/github.com/go-chi/chi/middleware/recoverer.go b/vendor/github.com/go-chi/chi/middleware/recoverer.go
new file mode 100644
index 0000000000..785b18c52b
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/middleware/recoverer.go
@@ -0,0 +1,192 @@
+package middleware
+
+// The original work was derived from Goji's middleware, source:
+// https://github.com/zenazn/goji/tree/master/web/middleware
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "net/http"
+ "os"
+ "runtime/debug"
+ "strings"
+)
+
+// Recoverer is a middleware that recovers from panics, logs the panic (and a
+// backtrace), and returns a HTTP 500 (Internal Server Error) status if
+// possible. Recoverer prints a request ID if one is provided.
+//
+// Alternatively, look at https://github.com/pressly/lg middleware pkgs.
+func Recoverer(next http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ defer func() {
+ if rvr := recover(); rvr != nil && rvr != http.ErrAbortHandler {
+
+ logEntry := GetLogEntry(r)
+ if logEntry != nil {
+ logEntry.Panic(rvr, debug.Stack())
+ } else {
+ PrintPrettyStack(rvr)
+ }
+
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+ }()
+
+ next.ServeHTTP(w, r)
+ }
+
+ return http.HandlerFunc(fn)
+}
+
+func PrintPrettyStack(rvr interface{}) {
+ debugStack := debug.Stack()
+ s := prettyStack{}
+ out, err := s.parse(debugStack, rvr)
+ if err == nil {
+ os.Stderr.Write(out)
+ } else {
+ // print stdlib output as a fallback
+ os.Stderr.Write(debugStack)
+ }
+}
+
+type prettyStack struct {
+}
+
+func (s prettyStack) parse(debugStack []byte, rvr interface{}) ([]byte, error) {
+ var err error
+ useColor := true
+ buf := &bytes.Buffer{}
+
+ cW(buf, false, bRed, "\n")
+ cW(buf, useColor, bCyan, " panic: ")
+ cW(buf, useColor, bBlue, "%v", rvr)
+ cW(buf, false, bWhite, "\n \n")
+
+ // process debug stack info
+ stack := strings.Split(string(debugStack), "\n")
+ lines := []string{}
+
+ // locate panic line, as we may have nested panics
+ for i := len(stack) - 1; i > 0; i-- {
+ lines = append(lines, stack[i])
+ if strings.HasPrefix(stack[i], "panic(0x") {
+ lines = lines[0 : len(lines)-2] // remove boilerplate
+ break
+ }
+ }
+
+ // reverse
+ for i := len(lines)/2 - 1; i >= 0; i-- {
+ opp := len(lines) - 1 - i
+ lines[i], lines[opp] = lines[opp], lines[i]
+ }
+
+ // decorate
+ for i, line := range lines {
+ lines[i], err = s.decorateLine(line, useColor, i)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ for _, l := range lines {
+ fmt.Fprintf(buf, "%s", l)
+ }
+ return buf.Bytes(), nil
+}
+
+func (s prettyStack) decorateLine(line string, useColor bool, num int) (string, error) {
+ line = strings.TrimSpace(line)
+ if strings.HasPrefix(line, "\t") || strings.Contains(line, ".go:") {
+ return s.decorateSourceLine(line, useColor, num)
+ } else if strings.HasSuffix(line, ")") {
+ return s.decorateFuncCallLine(line, useColor, num)
+ } else {
+ if strings.HasPrefix(line, "\t") {
+ return strings.Replace(line, "\t", " ", 1), nil
+ } else {
+ return fmt.Sprintf(" %s\n", line), nil
+ }
+ }
+}
+
+func (s prettyStack) decorateFuncCallLine(line string, useColor bool, num int) (string, error) {
+ idx := strings.LastIndex(line, "(")
+ if idx < 0 {
+ return "", errors.New("not a func call line")
+ }
+
+ buf := &bytes.Buffer{}
+ pkg := line[0:idx]
+ // addr := line[idx:]
+ method := ""
+
+ idx = strings.LastIndex(pkg, string(os.PathSeparator))
+ if idx < 0 {
+ idx = strings.Index(pkg, ".")
+ method = pkg[idx:]
+ pkg = pkg[0:idx]
+ } else {
+ method = pkg[idx+1:]
+ pkg = pkg[0 : idx+1]
+ idx = strings.Index(method, ".")
+ pkg += method[0:idx]
+ method = method[idx:]
+ }
+ pkgColor := nYellow
+ methodColor := bGreen
+
+ if num == 0 {
+ cW(buf, useColor, bRed, " -> ")
+ pkgColor = bMagenta
+ methodColor = bRed
+ } else {
+ cW(buf, useColor, bWhite, " ")
+ }
+ cW(buf, useColor, pkgColor, "%s", pkg)
+ cW(buf, useColor, methodColor, "%s\n", method)
+ // cW(buf, useColor, nBlack, "%s", addr)
+ return buf.String(), nil
+}
+
+func (s prettyStack) decorateSourceLine(line string, useColor bool, num int) (string, error) {
+ idx := strings.LastIndex(line, ".go:")
+ if idx < 0 {
+ return "", errors.New("not a source line")
+ }
+
+ buf := &bytes.Buffer{}
+ path := line[0 : idx+3]
+ lineno := line[idx+3:]
+
+ idx = strings.LastIndex(path, string(os.PathSeparator))
+ dir := path[0 : idx+1]
+ file := path[idx+1:]
+
+ idx = strings.Index(lineno, " ")
+ if idx > 0 {
+ lineno = lineno[0:idx]
+ }
+ fileColor := bCyan
+ lineColor := bGreen
+
+ if num == 1 {
+ cW(buf, useColor, bRed, " -> ")
+ fileColor = bRed
+ lineColor = bMagenta
+ } else {
+ cW(buf, false, bWhite, " ")
+ }
+ cW(buf, useColor, bWhite, "%s", dir)
+ cW(buf, useColor, fileColor, "%s", file)
+ cW(buf, useColor, lineColor, "%s", lineno)
+ if num == 1 {
+ cW(buf, false, bWhite, "\n")
+ }
+ cW(buf, false, bWhite, "\n")
+
+ return buf.String(), nil
+}