Skip to content

Commit

Permalink
🔄 Sync from monorepo
Browse files Browse the repository at this point in the history
  • Loading branch information
mojo-machine[bot] committed Jan 3, 2025
1 parent b469bd4 commit 3d3a9f7
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 6 deletions.
19 changes: 16 additions & 3 deletions lib/cher/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"slices"

"github.com/pkg/errors"
"github.com/wearemojo/mojo-public-go/lib/stacktrace"
)

// errors that are expected to be common across services
Expand All @@ -32,16 +33,24 @@ const (

// E implements the official CHER structure
type E struct {
Code string `json:"code"`
Meta M `json:"meta,omitempty"`
Reasons []E `json:"reasons,omitempty"`
Code string `json:"code"`
Meta M `json:"meta,omitempty"`
stack []stacktrace.Frame `json:"-"`
Reasons []E `json:"reasons,omitempty"`
}

// New returns a new E structure with code, meta, and optional reasons.
func New(code string, meta M, reasons ...E) E {
// For errors with only one reason, we can merge stack traces so we can actually see where the error originally came from
stack := stacktrace.GetCallerFrames(2)
if len(reasons) == 1 {
rootStack := reasons[0].stack
stack = stacktrace.MergeStacks(rootStack, stack)
}
return E{
Code: code,
Meta: meta,
stack: stack,
Reasons: reasons,
}
}
Expand Down Expand Up @@ -103,6 +112,10 @@ func (e E) Serialize() string {
return string(output)
}

func (e E) GetStack() []stacktrace.Frame {
return e.stack
}

// M it an alias type for map[string]any
type M map[string]any

Expand Down
19 changes: 17 additions & 2 deletions lib/merr/merr.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ var _ interface {
Unwrap() []error
} = E{}

type StackError interface {
GetStack() []stacktrace.Frame
}

// Merrer (merr-er) represents a merr-compatible error
//
// It primarily exists to allow `Wrap` to return nil without forcing us to use
Expand Down Expand Up @@ -47,15 +51,22 @@ func New(ctx context.Context, code Code, meta M, reasons ...error) E {
panic("merr.New: nil reason provided for " + code.String())
}
}

stack := stacktrace.GetCallerFrames(2)
// For errors with only one reason, we can merge stack traces so we can actually see where the error originally came from
if len(reasons) == 1 {
if stackErr, ok := reasons[0].(StackError); ok {
rootStack := stackErr.GetStack()
stack = stacktrace.MergeStacks(rootStack, stack)
}
}
return E{
Code: code,
Meta: meta,

TraceID: spanContext.TraceID(),
SpanID: spanContext.SpanID(),

Stack: stacktrace.GetCallerFrames(2),
Stack: stack,

Reasons: reasons,
}
Expand Down Expand Up @@ -122,3 +133,7 @@ func (e E) Is(err error) bool {
func (e E) Unwrap() []error {
return e.Reasons
}

func (e E) GetStack() []stacktrace.Frame {
return e.Stack
}
7 changes: 6 additions & 1 deletion lib/middleware/request/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,12 @@ func Logger(log *logrus.Entry) func(http.Handler) http.Handler {
if mErr, ok := gerrors.As[merr.E](err); ok {
fn(ctx, mErr)
} else if cErr, ok := gerrors.As[cher.E](err); ok {
fn(ctx, merr.New(ctx, merr.Code(cErr.Code), merr.M(cErr.Meta), slicefn.Map(cErr.Reasons, func(r cher.E) error { return r })...))
reasons := slicefn.Map(cErr.Reasons, func(r cher.E) error { return r })
// If the cher error has no reasons, add the cher error itself
if len(reasons) == 0 {
reasons = append(reasons, cErr)
}
fn(ctx, merr.New(ctx, merr.Code(cErr.Code), merr.M(cErr.Meta), reasons...))
} else {
fn(ctx, merr.New(ctx, "unexpected_request_failure", nil, err))
}
Expand Down
26 changes: 26 additions & 0 deletions lib/stacktrace/frames.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,29 @@ func FormatFrames(frames []Frame) string {

return sb.String()
}

// insert will put frame u into slice s at index at
func insert(s []Frame, u Frame, at int) []Frame {
return append(s[:at], append([]Frame{u}, s[at:]...)...)
}

func MergeStacks(root []Frame, wrapped []Frame) []Frame {
if len(wrapped) == 0 {
return root
}
if len(wrapped) == 1 {
return append(root, wrapped[0])
}

for idx, f := range root {
if f == wrapped[0] {
// root already contains the first frame of wrapped
return root
}
if f == wrapped[1] {
// Insert the first frame into the stack if the second frame is found
return insert(root, wrapped[0], idx)
}
}
return root
}
48 changes: 48 additions & 0 deletions lib/stacktrace/frames_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,51 @@ func TestFormatFrames(t *testing.T) {

is.Equal(FormatFrames(frames), expectedFormat)
}

func TestMergeFrames(t *testing.T) {
is := is.New(t)

root := []Frame{
{
File: "/lib/foo/foo.go",
Line: 123,
Function: "github.com/wearemojo/mojo-public-go/lib/foo.doFoo",
},
{
File: "/lib/foo/bar.go",
Line: 456,
Function: "github.com/wearemojo/mojo-public-go/lib/foo.barThing",
},
}

wrapped := []Frame{
{
File: "/lib/foo/baz.go",
Line: 789,
Function: "github.com/wearemojo/mojo-public-go/lib/foo.bazThing",
},
{
File: "/lib/foo/foo.go",
Line: 123,
Function: "github.com/wearemojo/mojo-public-go/lib/foo.doFoo",
},
}

is.Equal(MergeStacks(root, wrapped), []Frame{
{
File: "/lib/foo/baz.go",
Line: 789,
Function: "github.com/wearemojo/mojo-public-go/lib/foo.bazThing",
},
{
File: "/lib/foo/foo.go",
Line: 123,
Function: "github.com/wearemojo/mojo-public-go/lib/foo.doFoo",
},
{
File: "/lib/foo/bar.go",
Line: 456,
Function: "github.com/wearemojo/mojo-public-go/lib/foo.barThing",
},
})
}

0 comments on commit 3d3a9f7

Please sign in to comment.