Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AU-217] Make NCError implement StackTracer interface from aws-xray-sdk-go #6

Merged
merged 2 commits into from
Apr 1, 2022
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
31 changes: 25 additions & 6 deletions errors/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package errors
import (
"fmt"
"strings"

"github.com/pkg/errors"
)

type LogSeverity string
Expand Down Expand Up @@ -73,6 +75,8 @@ type NCError struct {
// Contains stack trace from the initial place when the error
// was raised.
Stack []string
// Raw stack trace in the form of program counters, as returned by the runtime.Callers
RawStack *stack
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be embedded instead? Dunno if it would work with the type being unexported 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what would not work here, but if there is no clear reason for embedding then I'd leave it as it is

Copy link
Contributor Author

@okonos okonos Mar 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rationale would be no need for the StackTrace method (re)definition. Currently it's more clear, though, I guess

//The root error at the base level.
RootError error
}
Expand All @@ -85,6 +89,12 @@ func (n NCError) Error() string {
return strings.Join(messages, ": ")
}

// StackTrace returns github.com/pkg/errors stack trace. This is to implement interface from aws-xray-sdk-go
// for passing along stack traces to X-Ray to segments.
func (n NCError) StackTrace() errors.StackTrace {
return n.RawStack.StackTrace()
}

// New error with context.
func New(message string, fields Fields) error {
fileName, funcName, lineNumber := GetRuntimeContext()
Expand All @@ -95,9 +105,12 @@ func New(message string, fields Fields) error {
FileName: fileName,
Line: lineNumber,
Severity: ERROR}
stack, rawStack := getStackTraces()
return NCError{
Causes: []Cause{newCause},
Stack: GetTrace()}
Causes: []Cause{newCause},
Stack: stack,
RawStack: rawStack,
}
}

func NewWithSeverity(message string, fields Fields, severity LogSeverity) error {
Expand All @@ -110,9 +123,11 @@ func NewWithSeverity(message string, fields Fields, severity LogSeverity) error
Line: lineNumber,
Severity: severity,
}
stack, rawStack := getStackTraces()
return NCError{
Causes: []Cause{newCause},
Stack: GetTrace(),
Causes: []Cause{newCause},
Stack: stack,
RawStack: rawStack,
}
}

Expand All @@ -135,9 +150,11 @@ func WithContext(err error, message string, fields Fields) error {
return ncError
}

stack, rawStack := getStackTraces()
return NCError{
Causes: []Cause{newCause, Cause{Message: err.Error()}},
Stack: GetTrace(),
Stack: stack,
RawStack: rawStack,
RootError: err}
}

Expand All @@ -160,9 +177,11 @@ func WithContextAndSeverity(err error, message string, severity LogSeverity, fie
return ncError
}

stack, rawStack := getStackTraces()
return NCError{
Causes: []Cause{newCause, Cause{Message: err.Error()}},
Stack: GetTrace(),
Stack: stack,
RawStack: rawStack,
RootError: err,
}
}
Expand Down
29 changes: 24 additions & 5 deletions errors/stack_trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,35 @@ import (
"regexp"
"runtime"
"strings"

"github.com/pkg/errors"
)

var (
regexFuncName = regexp.MustCompile(`(([^/]+/)+)?([^/.]+)((\.[^/.]+)+)?`)
)

// GetTrace return the simplified stack trace in the format file_name(func_name):line. It also contains the current goroutine entrypoint.
func GetTrace() []string {
var stack []string
// getStackTraces returns custom-formatted and raw (in the form of program counters) stack trace
// for the purpose of initializing NCError struct
func getStackTraces() ([]string, *stack) {
var formattedStack []string
callStack := *callers()
st := callStack[:len(callStack)-1]
for _, f := range st {
frame := frame(f)
stack = append(stack, frame.formatContext())
formattedStack = append(formattedStack, frame.formatContext())
}

return formattedStack, &callStack
}

// GetTrace return the simplified stack trace in the format file_name(func_name):line. It also contains the current goroutine entrypoint.
func GetTrace() []string {
stack, _ := getStackTraces()

return stack
}

type stack []uintptr
type frame uintptr

func (f frame) pc() uintptr { return uintptr(f) - 1 }
Expand Down Expand Up @@ -71,6 +80,16 @@ func GetRuntimeContext() (fileName, funcName string, line int) {
return
}

type stack []uintptr

func (s *stack) StackTrace() errors.StackTrace {
Copy link
Contributor Author

@okonos okonos Mar 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somewhat off-topic, but I wonder what's the point of declaring exported method on unexported type (I copied it over from pkg/errors)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that the type itself is not exported doesn't change the fact that the method can be called on that type. Unexported type just means you can not use the name of that type, but if you have a variable of that type you still can doe whatever is exported on that type. Also := operator works as well for unexported types (e.g. x := ConstructorOfUnexportedType) since it can bypass the need for actually using the name of the type (var x unexportedType would be illegal).

f := make([]errors.Frame, len(*s))
for i := 0; i < len(f); i++ {
f[i] = errors.Frame((*s)[i])
}
return f
}

func callers() *stack {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't see the point of it returning a pointer and neither does the author

const depth = 32
var pcs [depth]uintptr
Expand Down