mirror of
https://github.com/make-42/xyosc
synced 2025-01-18 18:57:10 +01:00
135 lines
2.6 KiB
Go
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,
|
|
}
|
|
}
|