Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ var DefaultCap = 20

// Error is an error with stack trace.
type Error interface {
Callers() []uintptr
Error() string
StackTrace() []Frame
Unwrap() error
Expand All @@ -38,6 +39,14 @@ func CustomError(err error, frames []Frame) Error {
}
}

// CustomErrorFromCallers creates an error with provided program counters.
func CustomErrorFromCallers(err error, pcs []uintptr) Error {
return &errorData{
err: err,
pcs: pcs,
}
}

// Errorf creates new error with stacktrace and formatted message.
// Formatting works the same way as in fmt.Errorf.
func Errorf(message string, args ...interface{}) Error {
Expand Down Expand Up @@ -73,14 +82,19 @@ func Unwrap(err error) error {
return e.Unwrap()
}

// Callers returns raw program counters of the stack trace.
func (e *errorData) Callers() []uintptr {
return e.pcs
}

// Error returns error message.
func (e *errorData) Error() string {
return e.err.Error()
}

// StackTrace resolves and returns the stack trace, caching the result.
func (e *errorData) StackTrace() []Frame {
if e.pcs == nil {
if e.frames != nil {
return e.frames
}
cf := runtime.CallersFrames(e.pcs)
Expand All @@ -97,7 +111,6 @@ func (e *errorData) StackTrace() []Frame {
}
}
e.frames = frames
e.pcs = nil
return e.frames
}

Expand Down
23 changes: 23 additions & 0 deletions error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,29 @@ func TestCustomError(t *testing.T) {
}
}

func TestCustomErrorFromCallers(t *testing.T) {
pcs := tracerr.New("some error").Callers()
err := tracerr.CustomErrorFromCallers(errors.New("custom"), pcs)
if len(err.Callers()) != len(pcs) {
t.Errorf("expected %d callers, got %d", len(pcs), len(err.Callers()))
}
if len(err.StackTrace()) == 0 {
t.Error("expected non-empty stack trace")
}
}

func TestCallers(t *testing.T) {
err := tracerr.New("some error")
pcs := err.Callers()
if len(pcs) == 0 {
t.Error("expected non-empty callers")
}
customErr := tracerr.CustomError(errors.New("custom"), nil)
if customErr.Callers() != nil {
t.Error("expected nil callers for CustomError")
}
}

func TestDeepStack(t *testing.T) {
var recurse func(n int) error
recurse = func(n int) error {
Expand Down
Loading