From bc44c5f4c085607d28a1ab051a287c5c648c7ba7 Mon Sep 17 00:00:00 2001 From: david-littlefarmer Date: Mon, 30 Oct 2023 15:54:20 +0100 Subject: [PATCH] max error stack trace frames as option --- devslog.go | 5 ++++- stacktrace.go | 37 ++++++++++++++----------------------- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/devslog.go b/devslog.go index 67dc9cb..8cd47f4 100644 --- a/devslog.go +++ b/devslog.go @@ -52,6 +52,9 @@ type Options struct { // Set color for Error level, default: devslog.Red ErrorColor Color + + // Max stack trace frames when unwrapping errors + MaxErrorStackTrace uint } type groupOrAttrs struct { @@ -404,7 +407,7 @@ func (h *developHandler) formatError(err error, l int) (b []byte) { } b = append(b, cs([]byte(errMsg), fgRed)...) - for j, fileLine := range getFileLineFromPC(extractPCFromError(err)) { + for j, fileLine := range h.getFileLineFromPC(h.extractPCFromError(err)) { b = append(b, '\n') tb := strconv.Itoa(j) b = append(b, bytes.Repeat([]byte(" "), l*2+6)...) diff --git a/stacktrace.go b/stacktrace.go index 618211e..303272a 100644 --- a/stacktrace.go +++ b/stacktrace.go @@ -6,9 +6,7 @@ import ( "runtime" ) -var MaxStackTraceFrames int = 2 - -func getFileLineFromPC(pcs []uintptr) (fileLines []string) { +func (h *developHandler) getFileLineFromPC(pcs []uintptr) (fileLines []string) { if len(pcs) == 0 { return nil } @@ -17,7 +15,6 @@ func getFileLineFromPC(pcs []uintptr) (fileLines []string) { for { fr, more := frames.Next() fileLines = append(fileLines, fmt.Sprintf("%v:%v", fr.File, fr.Line)) - if !more { break } @@ -30,19 +27,18 @@ func getFileLineFromPC(pcs []uintptr) (fileLines []string) { // - github.com/pkg/errors // - golang.org/x/xerrors // - golang.org/x/exp/errors -func extractPCFromError(err error) (pc []uintptr) { - if MaxStackTraceFrames == 0 { +func (h *developHandler) extractPCFromError(err error) (pc []uintptr) { + if h.opts.MaxErrorStackTrace == 0 { return nil } v := reflect.ValueOf(err) - - pc = extractPCFromPkgErrors(v) + pc = h.extractPCFromPkgErrors(v) if len(pc) > 0 { return pc } - pc = extractPCFromExpErrors(v) + pc = h.extractPCFromExpErrors(v) if len(pc) > 0 { return pc } @@ -50,7 +46,7 @@ func extractPCFromError(err error) (pc []uintptr) { return pc } -func extractPCFromPkgErrors(v reflect.Value) (pc []uintptr) { +func (h *developHandler) extractPCFromPkgErrors(v reflect.Value) (pc []uintptr) { // https://github.com/pkg/errors/blob/master/stack.go#L155 // // type stackTracer interface { @@ -63,24 +59,26 @@ func extractPCFromPkgErrors(v reflect.Value) (pc []uintptr) { if !v.IsValid() { return nil } + v = v.Call(nil)[0] if v.Kind() != reflect.Slice { return nil } // Get up to two frames from github.com/pkg/errors StackTrace. - for i := 0; i < min(v.Len(), MaxStackTraceFrames); i++ { + for i := 0; i < min(v.Len(), int(h.opts.MaxErrorStackTrace)); i++ { index := v.Index(i) if !index.CanUint() { return pc } + pc = append(pc, uintptr(index.Uint())) } return pc } -func extractPCFromExpErrors(v reflect.Value) (pc []uintptr) { +func (h *developHandler) extractPCFromExpErrors(v reflect.Value) (pc []uintptr) { // https://cs.opensource.google/go/x/exp/+/92128663:errors/fmt/errors.go;l=24 // https://cs.opensource.google/go/x/exp/+/92128663:errors/errors.go;l=25 // @@ -103,9 +101,11 @@ func extractPCFromExpErrors(v reflect.Value) (pc []uintptr) { if v.Kind() == reflect.Ptr && !v.IsNil() { v = v.Elem() } + if v.Kind() != reflect.Struct { return nil } + v = v.FieldByName("frame") if v.Kind() != reflect.Struct { return nil @@ -123,23 +123,14 @@ func extractPCFromExpErrors(v reflect.Value) (pc []uintptr) { // Skip first frame pointing at fmt.Errorf() or errors.New(). skip := 1 - - for i := skip; i < min(v.Len(), skip+MaxStackTraceFrames); i++ { + for i := skip; i < min(v.Len(), skip+int(h.opts.MaxErrorStackTrace)); i++ { index := v.Index(i) if !index.CanUint() { return nil } + pc = append(pc, uintptr(index.Uint())) } return pc } - -// As of Go 1.21, we could use the built-in min() function. -// This function provides backward compatibility with older versions of Go. -func min(i int, j int) int { - if i < j { - return i - } - return j -}