OSDN Git Service

feat(warder): add warder backbone (#181)
[bytom/vapor.git] / vendor / github.com / gin-gonic / gin / recovery.go
1 // Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
2 // Use of this source code is governed by a MIT style
3 // license that can be found in the LICENSE file.
4
5 package gin
6
7 import (
8         "bytes"
9         "fmt"
10         "io"
11         "io/ioutil"
12         "log"
13         "net/http"
14         "net/http/httputil"
15         "runtime"
16         "time"
17 )
18
19 var (
20         dunno     = []byte("???")
21         centerDot = []byte("·")
22         dot       = []byte(".")
23         slash     = []byte("/")
24 )
25
26 // Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
27 func Recovery() HandlerFunc {
28         return RecoveryWithWriter(DefaultErrorWriter)
29 }
30
31 // RecoveryWithWriter returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one.
32 func RecoveryWithWriter(out io.Writer) HandlerFunc {
33         var logger *log.Logger
34         if out != nil {
35                 logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags)
36         }
37         return func(c *Context) {
38                 defer func() {
39                         if err := recover(); err != nil {
40                                 if logger != nil {
41                                         stack := stack(3)
42                                         httprequest, _ := httputil.DumpRequest(c.Request, false)
43                                         logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s", timeFormat(time.Now()), string(httprequest), err, stack, reset)
44                                 }
45                                 c.AbortWithStatus(http.StatusInternalServerError)
46                         }
47                 }()
48                 c.Next()
49         }
50 }
51
52 // stack returns a nicely formatted stack frame, skipping skip frames.
53 func stack(skip int) []byte {
54         buf := new(bytes.Buffer) // the returned data
55         // As we loop, we open files and read them. These variables record the currently
56         // loaded file.
57         var lines [][]byte
58         var lastFile string
59         for i := skip; ; i++ { // Skip the expected number of frames
60                 pc, file, line, ok := runtime.Caller(i)
61                 if !ok {
62                         break
63                 }
64                 // Print this much at least.  If we can't find the source, it won't show.
65                 fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
66                 if file != lastFile {
67                         data, err := ioutil.ReadFile(file)
68                         if err != nil {
69                                 continue
70                         }
71                         lines = bytes.Split(data, []byte{'\n'})
72                         lastFile = file
73                 }
74                 fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
75         }
76         return buf.Bytes()
77 }
78
79 // source returns a space-trimmed slice of the n'th line.
80 func source(lines [][]byte, n int) []byte {
81         n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
82         if n < 0 || n >= len(lines) {
83                 return dunno
84         }
85         return bytes.TrimSpace(lines[n])
86 }
87
88 // function returns, if possible, the name of the function containing the PC.
89 func function(pc uintptr) []byte {
90         fn := runtime.FuncForPC(pc)
91         if fn == nil {
92                 return dunno
93         }
94         name := []byte(fn.Name())
95         // The name includes the path name to the package, which is unnecessary
96         // since the file name is already included.  Plus, it has center dots.
97         // That is, we see
98         //      runtime/debug.*T·ptrmethod
99         // and want
100         //      *T.ptrmethod
101         // Also the package path might contains dot (e.g. code.google.com/...),
102         // so first eliminate the path prefix
103         if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 {
104                 name = name[lastslash+1:]
105         }
106         if period := bytes.Index(name, dot); period >= 0 {
107                 name = name[period+1:]
108         }
109         name = bytes.Replace(name, centerDot, dot, -1)
110         return name
111 }
112
113 func timeFormat(t time.Time) string {
114         var timeString = t.Format("2006/01/02 - 15:04:05")
115         return timeString
116 }