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.
21 centerDot = []byte("·")
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)
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
35 logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags)
37 return func(c *Context) {
39 if err := recover(); err != nil {
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)
45 c.AbortWithStatus(http.StatusInternalServerError)
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
59 for i := skip; ; i++ { // Skip the expected number of frames
60 pc, file, line, ok := runtime.Caller(i)
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)
67 data, err := ioutil.ReadFile(file)
71 lines = bytes.Split(data, []byte{'\n'})
74 fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
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) {
85 return bytes.TrimSpace(lines[n])
88 // function returns, if possible, the name of the function containing the PC.
89 func function(pc uintptr) []byte {
90 fn := runtime.FuncForPC(pc)
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.
98 // runtime/debug.*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:]
106 if period := bytes.Index(name, dot); period >= 0 {
107 name = name[period+1:]
109 name = bytes.Replace(name, centerDot, dot, -1)
113 func timeFormat(t time.Time) string {
114 var timeString = t.Format("2006/01/02 - 15:04:05")