hmn/src/oops/oops.go

80 lines
1.6 KiB
Go

package oops
import (
"fmt"
"github.com/go-stack/stack"
"github.com/rs/zerolog"
)
type Error struct {
Message string
Wrapped error
Stack CallStack
}
func (e *Error) Error() string {
if e.Wrapped == nil {
return e.Message
} else {
return fmt.Sprintf("%s: %v", e.Message, e.Wrapped)
}
}
func (e *Error) Unwrap() error {
return e.Wrapped
}
type CallStack []StackFrame
func (s CallStack) MarshalZerologArray(a *zerolog.Array) {
for _, frame := range s {
a.Object(frame)
}
}
type StackFrame struct {
File string `json:"file"`
Line int `json:"line"`
Function string `json:"function"`
}
func (f StackFrame) MarshalZerologObject(e *zerolog.Event) {
e.
Str("file", f.File).
Int("line", f.Line).
Str("function", f.Function)
}
var ZerologStackMarshaler = func(err error) interface{} {
if asOops, ok := err.(*Error); ok {
return asOops.Stack
}
// NOTE(asaf): If we got here, it means zerolog is trying to output a non-oops error.
// We remove this call and the zerolog caller from the stack.
return Trace()[2:]
}
func New(wrapped error, format string, args ...interface{}) error {
return &Error{
Message: fmt.Sprintf(format, args...),
Wrapped: wrapped,
Stack: Trace()[1:], // NOTE(asaf): Remove the call to New from the stack
}
}
func Trace() CallStack {
trace := stack.Trace().TrimRuntime()[1:]
frames := make(CallStack, len(trace))
for i, call := range trace {
callFrame := call.Frame()
frames[i] = StackFrame{
File: callFrame.File,
Line: callFrame.Line,
Function: callFrame.Function,
}
}
return frames
}