Skip to content

Commit

Permalink
feat(errors): extract pkg/errors stacktraces
Browse files Browse the repository at this point in the history
  • Loading branch information
kattrali committed Nov 3, 2020
1 parent 32cba73 commit a5cd81e
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 2 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## TBD

### Enhancements

* Extract stacktrace contents on errors wrapped by
[`pkg/errors`](https://github.com/pkg/errors).

### Bug fixes

* Send web framework name with severity reason if set. Previously this value was
Expand Down
16 changes: 16 additions & 0 deletions errors/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package errors
import (
"bytes"
"fmt"
"github.com/pkg/errors"
"reflect"
"runtime"
)
Expand Down Expand Up @@ -33,6 +34,11 @@ type ErrorWithStackFrames interface {
StackFrames() []StackFrame
}

type errorWithStack interface {
StackTrace() errors.StackTrace
Error() string
}

// New makes an Error from the given value. If that value is already an
// error then it will be used directly, if not, it will be passed to
// fmt.Errorf("%v"). The skip parameter indicates how far up the stack
Expand All @@ -48,6 +54,16 @@ func New(e interface{}, skip int) *Error {
Err: e,
stack: e.Callers(),
}
case errorWithStack:
trace := e.StackTrace()
stack := make([]uintptr, len(trace))
for i, ptr := range trace {
stack[i] = uintptr(ptr) - 1
}
return &Error{
Err: e,
stack: stack,
}
case ErrorWithStackFrames:
stack := make([]uintptr, len(e.StackFrames()))
for i, frame := range e.StackFrames() {
Expand Down
27 changes: 25 additions & 2 deletions errors/error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import (
"bytes"
"fmt"
"io"
"runtime"
"strings"
"testing"

"github.com/pkg/errors"
)

// fixture functions doing work to avoid inlining
Expand Down Expand Up @@ -44,7 +47,7 @@ func TestParsePanicStack(t *testing.T) {
}
expected := []StackFrame{
StackFrame{Name: "TestParsePanicStack.func1", File: "errors/error_test.go"},
StackFrame{Name: "a", File: "errors/error_test.go", LineNumber: 13},
StackFrame{Name: "a", File: "errors/error_test.go", LineNumber: 16},
}
assertStacksMatch(t, expected, err.StackFrames())
}()
Expand Down Expand Up @@ -88,7 +91,7 @@ func TestSkipWorks(t *testing.T) {
}

expected := []StackFrame{
StackFrame{Name: "a", File: "errors/error_test.go", LineNumber: 13},
StackFrame{Name: "a", File: "errors/error_test.go", LineNumber: 16},
}

assertStacksMatch(t, expected, err.StackFrames())
Expand Down Expand Up @@ -182,6 +185,26 @@ func TestNewError(t *testing.T) {
}
}

func TestUnwrapPkgError(t *testing.T) {
_, _, line, ok := runtime.Caller(0) // grab line immediately before error generator
top := func() error {
err := fmt.Errorf("OH NO")
return errors.Wrap(err, "failed") // the correct line for the top of the stack
}
unwrapped := New(top(), 0) // if errors.StackTrace detection fails, this line will be top of stack
if !ok {
t.Fatalf("Something has gone wrong with loading the current stack")
}
if unwrapped.Error() != "failed: OH NO" {
t.Errorf("Failed to unwrap error: %s", unwrapped.Error())
}
expected := []StackFrame{
StackFrame{Name: "TestUnwrapPkgError.func1", File: "errors/error_test.go", LineNumber: line + 3},
StackFrame{Name: "TestUnwrapPkgError", File: "errors/error_test.go", LineNumber: line + 5},
}
assertStacksMatch(t, expected, unwrapped.StackFrames())
}

func ExampleErrorf() {
for i := 1; i <= 2; i++ {
if i%2 == 1 {
Expand Down

0 comments on commit a5cd81e

Please sign in to comment.