Skip to content
Permalink
Branch: master
Find file Copy path
2 contributors

Users who have contributed to this file

@ztrue @vvakame
135 lines (118 sloc) 2.65 KB
// 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,
}
}
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.