Skip to content

Commit

Permalink
max error stack trace frames as option
Browse files Browse the repository at this point in the history
  • Loading branch information
david-littlefarmer committed Oct 30, 2023
1 parent 93c51c7 commit df0fd2f
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 24 deletions.
5 changes: 4 additions & 1 deletion devslog.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)...)
Expand Down
37 changes: 14 additions & 23 deletions stacktrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
Expand All @@ -30,27 +27,26 @@ 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
}

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 {
Expand All @@ -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
//
Expand All @@ -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
Expand All @@ -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
}

0 comments on commit df0fd2f

Please sign in to comment.