...
1 package internal
2
3 import (
4 "bytes"
5 "fmt"
6 "net/http"
7 "runtime"
8 "strings"
9 )
10
11 type StackTracer struct {
12 CustomFn func(...interface{})
13 Err error
14 }
15
16
17 func (s StackTracer) Error() string {
18 if s.Err == nil {
19 return ""
20 }
21 return s.Err.Error()
22 }
23
24
25 func (s StackTracer) Unwrap() error {
26 return s.Err
27 }
28
29
30
31 func CheckStackTracer(req *http.Request, err error) error {
32 if nf, ok := err.(StackTracer); ok {
33 if nf.CustomFn != nil {
34 pc := make([]uintptr, 128)
35 npc := runtime.Callers(2, pc)
36 pc = pc[:npc]
37
38 var mesg bytes.Buffer
39 var netHTTPBegin, netHTTPEnd bool
40
41
42 for {
43 frames := runtime.CallersFrames(pc)
44
45 var lastFn string
46 for {
47 frame, more := frames.Next()
48
49 if !netHTTPEnd {
50 if netHTTPBegin {
51 netHTTPEnd = !strings.HasPrefix(frame.Function, "net/http.")
52 } else {
53 netHTTPBegin = strings.HasPrefix(frame.Function, "net/http.")
54 }
55 }
56
57 if netHTTPEnd {
58 if lastFn != "" {
59 if mesg.Len() == 0 {
60 if nf.Err != nil {
61 mesg.WriteString(nf.Err.Error())
62 } else {
63 fmt.Fprintf(&mesg, "%s %s", req.Method, req.URL)
64 }
65 mesg.WriteString("\nCalled from ")
66 } else {
67 mesg.WriteString("\n ")
68 }
69 fmt.Fprintf(&mesg, "%s()\n at %s:%d", lastFn, frame.File, frame.Line)
70 }
71 }
72 lastFn = frame.Function
73
74 if !more {
75 break
76 }
77 }
78
79
80 if mesg.Len() > 0 {
81 break
82 }
83 netHTTPEnd = true
84 }
85
86 nf.CustomFn(mesg.String())
87 }
88 err = nf.Err
89 }
90 return err
91 }
92
View as plain text