Skip to content

Commit

Permalink
separate slogerr package
Browse files Browse the repository at this point in the history
  • Loading branch information
Greg Weber committed Jan 7, 2025
1 parent 4304999 commit c701d45
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 364 deletions.
8 changes: 5 additions & 3 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@
// %-v similar to %s but newline separated. No stack traces included.
package errors

import "github.com/gregwebs/errors/stackfmt"

// GetStackTracer will return the first StackTracer in the causer chain.
// This function is used by AddStack to avoid creating redundant stack traces.
//
// You can also use the StackTracer interface on the returned error to get the stack trace.
func GetStackTracer(origErr error) StackTracer {
var stacked StackTracer
func GetStackTracer(origErr error) stackfmt.StackTracer {
var stacked stackfmt.StackTracer
WalkDeep(origErr, func(err error) bool {
if stackTracer, ok := err.(StackTracer); ok {
if stackTracer, ok := err.(stackfmt.StackTracer); ok {
stacked = stackTracer
return true
}
Expand Down
10 changes: 6 additions & 4 deletions errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,10 +406,12 @@ func TestErrorWrapper(t *testing.T) {
t.Errorf("wrapf Error() %s", s)
}

err.WrapError(WrapsFn("wraps", "i", 2))
if s := err.Error(); s != "wraps i=2: wrapf 1: wrap: underlying" {
t.Errorf("wrapf Error() %s", s)
}
/*
err.WrapError(WrapsFn("wraps", "i", 2))
if s := err.Error(); s != "wraps i=2: wrapf 1: wrap: underlying" {
t.Errorf("wrapf Error() %s", s)
}
*/
}

type ErrArray []error
Expand Down
40 changes: 21 additions & 19 deletions errstack.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import (
"fmt"
"io"
"log"

"github.com/gregwebs/errors/stackfmt"
)

// New returns an error with the supplied message.
// New also records the stack trace at the point it was called.
func New(message string) error {
return &fundamental{stderrors.New(message), NewStack()}
return &fundamental{stderrors.New(message), stackfmt.NewStack()}
}

// Errorf formats according to a format specifier and returns the string
Expand All @@ -19,11 +21,11 @@ func New(message string) error {
func Errorf(format string, args ...interface{}) error {
err := fmt.Errorf(format, args...)
if _, ok := err.(unwrapper); ok {
return &addStack{withStack{err, NewStack()}}
return &addStack{withStack{err, stackfmt.NewStack()}}
} else if _, ok := err.(unwraps); ok {
return &addStack{withStack{err, NewStack()}}
return &addStack{withStack{err, stackfmt.NewStack()}}
}
return &fundamental{err, NewStack()}
return &fundamental{err, stackfmt.NewStack()}
}

// fundamental is a base error that doesn't wrap other errors
Expand All @@ -33,11 +35,11 @@ func Errorf(format string, args ...interface{}) error {
// The latter is done in part to support %w, but note that if %w is used we don't use fundamental
type fundamental struct {
error
Stack
stackfmt.Stack
}

func (f *fundamental) StackTrace() StackTrace { return f.Stack.StackTrace() }
func (f *fundamental) HasStack() bool { return true }
func (f *fundamental) StackTrace() stackfmt.StackTrace { return f.Stack.StackTrace() }
func (f *fundamental) HasStack() bool { return true }
func (f *fundamental) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
Expand All @@ -63,7 +65,7 @@ func AddStack(err error) error {
if HasStack(err) {
return err
}
return &addStack{withStack{err, NewStack()}}
return &addStack{withStack{err, stackfmt.NewStack()}}
}

// Same as AddStack but specify an additional number of callers to skip
Expand All @@ -74,25 +76,25 @@ func AddStackSkip(err error, skip int) error {
if HasStack(err) {
return err
}
return &addStack{withStack{err, NewStackSkip(skip + 1)}}
return &addStack{withStack{err, stackfmt.NewStackSkip(skip + 1)}}
}

type withStack struct {
error
Stack
stackfmt.Stack
}

func (w *withStack) StackTraceFormat(s fmt.State, v rune) { w.Stack.FormatStackTrace(s, v) }
func (w *withStack) StackTrace() StackTrace { return w.Stack.StackTrace() }
func (w *withStack) StackTrace() stackfmt.StackTrace { return w.Stack.StackTrace() }
func (w *withStack) Unwrap() error { return w.error }
func (w *withStack) ErrorNoUnwrap() string { return "" }
func (w *withStack) HasStack() bool { return true }
func (w *withStack) Format(s fmt.State, verb rune) {
formatErrorUnwrap(w, s, verb)
}

var _ StackTracer = &withStack{}
var _ StackTraceFormatter = &withStack{}
var _ stackfmt.StackTracer = &withStack{}
var _ stackfmt.StackTraceFormatter = &withStack{}

// addStack is returned directly whereas withStack is always used composed
// they Unwrap differently
Expand All @@ -118,7 +120,7 @@ func Wrap(err error, message string) error {
}
return &withMessage{
msg: message,
withStack: withStack{err, NewStack()},
withStack: withStack{err, stackfmt.NewStack()},
}
}

Expand All @@ -135,7 +137,7 @@ func Wrapf(err error, format string, args ...interface{}) error {
}
return &withMessage{
msg: fmt.Sprintf(format, args...),
withStack: withStack{err, NewStack()},
withStack: withStack{err, stackfmt.NewStack()},
}
}

Expand Down Expand Up @@ -203,10 +205,10 @@ func HasStack(err error) bool {
if errWithStack, ok := err.(StackTraceAware); ok {
return errWithStack.HasStack()
}
if _, ok := err.(StackTracer); ok {
if _, ok := err.(stackfmt.StackTracer); ok {
return true
}
if _, ok := err.(StackTraceFormatter); ok {
if _, ok := err.(stackfmt.StackTraceFormatter); ok {
return true
}
return false
Expand All @@ -224,9 +226,9 @@ func formatErrorUnwrap(err error, s fmt.State, verb rune) {
} else {
writeStringErrstack(s, err.Error())
}
if stackTracer, ok := err.(StackTracer); ok {
if stackTracer, ok := err.(stackfmt.StackTracer); ok {
stackTracer.StackTrace().Format(s, verb)
} else if stackTracer, ok := err.(StackTraceFormatter); ok {
} else if stackTracer, ok := err.(stackfmt.StackTraceFormatter); ok {
stackTracer.FormatStackTrace(s, verb)
}
return
Expand Down
55 changes: 2 additions & 53 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

"github.com/gregwebs/errors"
"github.com/gregwebs/errors/stackfmt"
)

func ExampleNew() {
Expand Down Expand Up @@ -122,7 +123,7 @@ func ExampleErrorf() {

func Example_stackTrace() {
type stackTracer interface {
StackTrace() errors.StackTrace
StackTrace() stackfmt.StackTrace
}

err, ok := errors.Cause(newWrappedErr()).(stackTracer)
Expand All @@ -137,43 +138,6 @@ func Example_stackTrace() {
// github.com/gregwebs/errors_test.Example_stackTrace
}

func ExampleStructuredError() {
err := errors.Wraps(
errors.New("cause"),
"structured",
"key", "value",
"int", 1,
)

fmt.Println(err.Error())
// Output: structured key=value int=1: cause
}

func ExampleSlog() {
err := errors.Slog(
"cause",
"key", "value",
"int", 1,
)

fmt.Println(err.Error())
// Output: cause key=value int=1
}

func ExampleSlogRecord() {
err := errors.Wraps(
errors.New("cause"),
"structured",
"key", "value",
"int", 1,
)

rec := errors.SlogRecord(err)
fmt.Println(rec.Message)
// Output:
// structured: cause
}

type ErrEmpty struct{}

func (et ErrEmpty) Error() string {
Expand Down Expand Up @@ -249,18 +213,3 @@ func ExampleWrapInPlace() {
// Regular error wrapped in place: false
// Regular error: regular error
}

func ExampleWraps() {
// Create a base error
baseErr := fmt.Errorf("database connection failed")

// Wrap the error with additional structured information
wrappedErr := errors.Wraps(baseErr, "user authentication failed",
"user_id", "123",
"attempt", 3,
)

// Print the error
fmt.Println(wrappedErr)
// Output: user authentication failed user_id=123 attempt=3: database connection failed
}
21 changes: 0 additions & 21 deletions format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,27 +358,6 @@ func TestFormatWrapNoStack(t *testing.T) {
}
}*/

func testFormatString(t *testing.T, n int, arg interface{}, format, wantAll string) {
t.Helper()
got := fmt.Sprintf(format, arg)
gotLines := strings.SplitN(got, "\n", -1)
wantLines := strings.SplitN(wantAll, "\n", -1)

if len(wantLines) > len(gotLines) {
t.Errorf("test %d: wantLines(%d) > gotLines(%d):\n got: %q\nwant: %q", n+1, len(wantLines), len(gotLines), got, wantLines)
return
}

for i, wantLine := range wantLines {
want := wantLine
got := gotLines[i]
adjustedGot := regexp.MustCompile(`\S.*/errors`).ReplaceAllString(got, `github.com/gregwebs/errors`)
if want != adjustedGot {
t.Errorf("test %d: line %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, adjustedGot, want)
}
}
}

func testFormatRegexp(t *testing.T, n int, arg interface{}, format, wantAll string) {
t.Helper()
got := fmt.Sprintf(format, arg)
Expand Down
33 changes: 33 additions & 0 deletions slogerr/base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package slogerr

import (
"fmt"
)

func formatterPlusV(s fmt.State, verb rune, err error) {
if f, ok := err.(fmt.Formatter); ok {
f.Format(s, verb)
} else {
fmt.Fprintf(s, "%+v", err)
}
}

type unwrapper interface {
Unwrap() error
}

type unwraps interface {
Unwrap() []error
}

type errorUnwrap interface {
Unwrap() error
// ErrorNoUnwrap is the error message component of the wrapping
// It will be a prefix of Error()
// If there is no message in the wrapping then this can return an empty string
ErrorNoUnwrap() string
}

type stackTraceAware interface {
HasStack() bool
}
60 changes: 60 additions & 0 deletions slogerr/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package slogerr_test

import (
"errors"
"fmt"

"github.com/gregwebs/errors/slogerr"
)

func ExampleStructuredError() {
err := slogerr.Wraps(
errors.New("cause"),
"structured",
"key", "value",
"int", 1,
)

fmt.Println(err.Error())
// Output: structured key=value int=1: cause
}

func ExampleNew() {
err := slogerr.New(
"cause",
"key", "value",
"int", 1,
)

fmt.Println(err.Error())
// Output: cause key=value int=1
}

func ExampleSlogRecord() {
err := slogerr.Wraps(
errors.New("cause"),
"structured",
"key", "value",
"int", 1,
)

rec := slogerr.SlogRecord(err)
fmt.Println(rec.Message)
// Output:
// structured: cause
}

func ExampleWraps() {
// Create a base error
baseErr := fmt.Errorf("database connection failed")

// Wrap the error with additional structured information
wrappedErr := slogerr.Wraps(baseErr, "user authentication failed",
"user_id", "123",
"attempt", 3,
)

// Print the error
fmt.Println(wrappedErr)
// Output: user authentication failed user_id=123 attempt=3: database connection failed
}
Loading

0 comments on commit c701d45

Please sign in to comment.