92 lines
1.8 KiB
Go
92 lines
1.8 KiB
Go
|
package internal
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"runtime"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
type StackTracer struct {
|
||
|
CustomFn func(...interface{})
|
||
|
Err error
|
||
|
}
|
||
|
|
||
|
// Error implements error interface.
|
||
|
func (s StackTracer) Error() string {
|
||
|
if s.Err == nil {
|
||
|
return ""
|
||
|
}
|
||
|
return s.Err.Error()
|
||
|
}
|
||
|
|
||
|
// Unwrap implements the interface needed by errors.Unwrap.
|
||
|
func (s StackTracer) Unwrap() error {
|
||
|
return s.Err
|
||
|
}
|
||
|
|
||
|
// CheckStackTracer checks for specific error returned by
|
||
|
// NewNotFoundResponder function or Trace Responder method.
|
||
|
func CheckStackTracer(req *http.Request, err error) error {
|
||
|
if nf, ok := err.(StackTracer); ok {
|
||
|
if nf.CustomFn != nil {
|
||
|
pc := make([]uintptr, 128)
|
||
|
npc := runtime.Callers(2, pc)
|
||
|
pc = pc[:npc]
|
||
|
|
||
|
var mesg bytes.Buffer
|
||
|
var netHTTPBegin, netHTTPEnd bool
|
||
|
|
||
|
// Start recording at first net/http call if any...
|
||
|
for {
|
||
|
frames := runtime.CallersFrames(pc)
|
||
|
|
||
|
var lastFn string
|
||
|
for {
|
||
|
frame, more := frames.Next()
|
||
|
|
||
|
if !netHTTPEnd {
|
||
|
if netHTTPBegin {
|
||
|
netHTTPEnd = !strings.HasPrefix(frame.Function, "net/http.")
|
||
|
} else {
|
||
|
netHTTPBegin = strings.HasPrefix(frame.Function, "net/http.")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if netHTTPEnd {
|
||
|
if lastFn != "" {
|
||
|
if mesg.Len() == 0 {
|
||
|
if nf.Err != nil {
|
||
|
mesg.WriteString(nf.Err.Error())
|
||
|
} else {
|
||
|
fmt.Fprintf(&mesg, "%s %s", req.Method, req.URL)
|
||
|
}
|
||
|
mesg.WriteString("\nCalled from ")
|
||
|
} else {
|
||
|
mesg.WriteString("\n ")
|
||
|
}
|
||
|
fmt.Fprintf(&mesg, "%s()\n at %s:%d", lastFn, frame.File, frame.Line)
|
||
|
}
|
||
|
}
|
||
|
lastFn = frame.Function
|
||
|
|
||
|
if !more {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// At least one net/http frame found
|
||
|
if mesg.Len() > 0 {
|
||
|
break
|
||
|
}
|
||
|
netHTTPEnd = true // retry without looking at net/http frames
|
||
|
}
|
||
|
|
||
|
nf.CustomFn(mesg.String())
|
||
|
}
|
||
|
err = nf.Err
|
||
|
}
|
||
|
return err
|
||
|
}
|