1 package middleware
2
3
4
5
6 import (
7 "bytes"
8 "errors"
9 "fmt"
10 "net/http"
11 "os"
12 "runtime/debug"
13 "strings"
14 )
15
16
17
18
19
20
21 func Recoverer(next http.Handler) http.Handler {
22 fn := func(w http.ResponseWriter, r *http.Request) {
23 defer func() {
24 if rvr := recover(); rvr != nil && rvr != http.ErrAbortHandler {
25
26 logEntry := GetLogEntry(r)
27 if logEntry != nil {
28 logEntry.Panic(rvr, debug.Stack())
29 } else {
30 PrintPrettyStack(rvr)
31 }
32
33 w.WriteHeader(http.StatusInternalServerError)
34 }
35 }()
36
37 next.ServeHTTP(w, r)
38 }
39
40 return http.HandlerFunc(fn)
41 }
42
43 func PrintPrettyStack(rvr interface{}) {
44 debugStack := debug.Stack()
45 s := prettyStack{}
46 out, err := s.parse(debugStack, rvr)
47 if err == nil {
48 os.Stderr.Write(out)
49 } else {
50
51 os.Stderr.Write(debugStack)
52 }
53 }
54
55 type prettyStack struct {
56 }
57
58 func (s prettyStack) parse(debugStack []byte, rvr interface{}) ([]byte, error) {
59 var err error
60 useColor := true
61 buf := &bytes.Buffer{}
62
63 cW(buf, false, bRed, "\n")
64 cW(buf, useColor, bCyan, " panic: ")
65 cW(buf, useColor, bBlue, "%v", rvr)
66 cW(buf, false, bWhite, "\n \n")
67
68
69 stack := strings.Split(string(debugStack), "\n")
70 lines := []string{}
71
72
73 for i := len(stack) - 1; i > 0; i-- {
74 lines = append(lines, stack[i])
75 if strings.HasPrefix(stack[i], "panic(0x") {
76 lines = lines[0 : len(lines)-2]
77 break
78 }
79 }
80
81
82 for i := len(lines)/2 - 1; i >= 0; i-- {
83 opp := len(lines) - 1 - i
84 lines[i], lines[opp] = lines[opp], lines[i]
85 }
86
87
88 for i, line := range lines {
89 lines[i], err = s.decorateLine(line, useColor, i)
90 if err != nil {
91 return nil, err
92 }
93 }
94
95 for _, l := range lines {
96 fmt.Fprintf(buf, "%s", l)
97 }
98 return buf.Bytes(), nil
99 }
100
101 func (s prettyStack) decorateLine(line string, useColor bool, num int) (string, error) {
102 line = strings.TrimSpace(line)
103 if strings.HasPrefix(line, "\t") || strings.Contains(line, ".go:") {
104 return s.decorateSourceLine(line, useColor, num)
105 } else if strings.HasSuffix(line, ")") {
106 return s.decorateFuncCallLine(line, useColor, num)
107 } else {
108 if strings.HasPrefix(line, "\t") {
109 return strings.Replace(line, "\t", " ", 1), nil
110 } else {
111 return fmt.Sprintf(" %s\n", line), nil
112 }
113 }
114 }
115
116 func (s prettyStack) decorateFuncCallLine(line string, useColor bool, num int) (string, error) {
117 idx := strings.LastIndex(line, "(")
118 if idx < 0 {
119 return "", errors.New("not a func call line")
120 }
121
122 buf := &bytes.Buffer{}
123 pkg := line[0:idx]
124
125 method := ""
126
127 idx = strings.LastIndex(pkg, string(os.PathSeparator))
128 if idx < 0 {
129 idx = strings.Index(pkg, ".")
130 method = pkg[idx:]
131 pkg = pkg[0:idx]
132 } else {
133 method = pkg[idx+1:]
134 pkg = pkg[0 : idx+1]
135 idx = strings.Index(method, ".")
136 pkg += method[0:idx]
137 method = method[idx:]
138 }
139 pkgColor := nYellow
140 methodColor := bGreen
141
142 if num == 0 {
143 cW(buf, useColor, bRed, " -> ")
144 pkgColor = bMagenta
145 methodColor = bRed
146 } else {
147 cW(buf, useColor, bWhite, " ")
148 }
149 cW(buf, useColor, pkgColor, "%s", pkg)
150 cW(buf, useColor, methodColor, "%s\n", method)
151
152 return buf.String(), nil
153 }
154
155 func (s prettyStack) decorateSourceLine(line string, useColor bool, num int) (string, error) {
156 idx := strings.LastIndex(line, ".go:")
157 if idx < 0 {
158 return "", errors.New("not a source line")
159 }
160
161 buf := &bytes.Buffer{}
162 path := line[0 : idx+3]
163 lineno := line[idx+3:]
164
165 idx = strings.LastIndex(path, string(os.PathSeparator))
166 dir := path[0 : idx+1]
167 file := path[idx+1:]
168
169 idx = strings.Index(lineno, " ")
170 if idx > 0 {
171 lineno = lineno[0:idx]
172 }
173 fileColor := bCyan
174 lineColor := bGreen
175
176 if num == 1 {
177 cW(buf, useColor, bRed, " -> ")
178 fileColor = bRed
179 lineColor = bMagenta
180 } else {
181 cW(buf, false, bWhite, " ")
182 }
183 cW(buf, useColor, bWhite, "%s", dir)
184 cW(buf, useColor, fileColor, "%s", file)
185 cW(buf, useColor, lineColor, "%s", lineno)
186 if num == 1 {
187 cW(buf, false, bWhite, "\n")
188 }
189 cW(buf, false, bWhite, "\n")
190
191 return buf.String(), nil
192 }
193
View as plain text