xyosc/vendor/github.com/ztrue/tracerr/error.go
2024-12-21 17:38:26 +01:00

135 lines
2.6 KiB
Go

// Package tracerr makes error output more informative.
// It adds stack trace to error and can display error with source fragments.
//
// Check example of output here https://github.com/ztrue/tracerr
package tracerr
import (
"fmt"
"runtime"
)
// DefaultCap is a default cap for frames array.
// It can be changed to number of expected frames
// for purpose of performance optimisation.
var DefaultCap = 20
// Error is an error with stack trace.
type Error interface {
Error() string
StackTrace() []Frame
Unwrap() error
}
type errorData struct {
// err contains original error.
err error
// frames contains stack trace of an error.
frames []Frame
}
// CustomError creates an error with provided frames.
func CustomError(err error, frames []Frame) Error {
return &errorData{
err: err,
frames: frames,
}
}
// 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 {
return trace(fmt.Errorf(message, args...), 2)
}
// New creates new error with stacktrace.
func New(message string) Error {
return trace(fmt.Errorf(message), 2)
}
// Wrap adds stacktrace to existing error.
func Wrap(err error) Error {
if err == nil {
return nil
}
e, ok := err.(Error)
if ok {
return e
}
return trace(err, 2)
}
// Unwrap returns the original error.
func Unwrap(err error) error {
if err == nil {
return nil
}
e, ok := err.(Error)
if !ok {
return err
}
return e.Unwrap()
}
// Error returns error message.
func (e *errorData) Error() string {
return e.err.Error()
}
// StackTrace returns stack trace of an error.
func (e *errorData) StackTrace() []Frame {
return e.frames
}
// Unwrap returns the original error.
func (e *errorData) Unwrap() error {
return e.err
}
// Frame is a single step in stack trace.
type Frame struct {
// Func contains a function name.
Func string
// Line contains a line number.
Line int
// Path contains a file path.
Path string
}
// StackTrace returns stack trace of an error.
// It will be empty if err is not of type Error.
func StackTrace(err error) []Frame {
e, ok := err.(Error)
if !ok {
return nil
}
return e.StackTrace()
}
// String formats Frame to string.
func (f Frame) String() string {
return fmt.Sprintf("%s:%d %s()", f.Path, f.Line, f.Func)
}
func trace(err error, skip int) Error {
frames := make([]Frame, 0, DefaultCap)
for {
pc, path, line, ok := runtime.Caller(skip)
if !ok {
break
}
fn := runtime.FuncForPC(pc)
frame := Frame{
Func: fn.Name(),
Line: line,
Path: path,
}
frames = append(frames, frame)
skip++
}
return &errorData{
err: err,
frames: frames,
}
}