-
Notifications
You must be signed in to change notification settings - Fork 40
/
Copy patherror.go
134 lines (118 loc) · 2.65 KB
/
error.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// 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,
}
}