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 }