-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtracederror.go
110 lines (91 loc) · 2.25 KB
/
tracederror.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
package tracederror
import (
"fmt"
"runtime"
)
var (
// If debug is false, no stack or file/line information is included with the wrapped error.
Debug = true
// If this is false, then only the file and line where the error was created is printed, instead
// of a full stack trace.
ShowStack = true
)
type TracedError struct {
WrappedError error
Context interface{}
stack []byte
line int
file string
}
var onError func(error, []byte, string, int, interface{}) = nil
func OnError(handler func(error, []byte, string, int, interface{})) {
onError = handler
}
// Creates a new traced error. Calling this on an instance of traced error is idempotent,
// it just returns the original traced error. Calling this on nil returns nil.
func New(wrappedError error) error {
return NewWithContext(wrappedError, nil)
}
func NewWithContext(wrappedError error, context interface{}) error {
if wrappedError == nil {
return nil
}
_, ok := wrappedError.(*TracedError)
if ok {
return wrappedError
}
enErr := &TracedError{
WrappedError: wrappedError,
Context: context,
}
if Debug {
if ShowStack {
if _, ok := wrappedError.(*TracedError); !ok {
enErr.stack = stack()
}
}
if _, file, line, ok := runtime.Caller(1); ok {
enErr.file = file
enErr.line = line
}
if onError != nil {
onError(wrappedError, enErr.stack, enErr.file, enErr.line, context)
}
}
return enErr
}
// Returns the original error's Error(), with optional caller information
func (enErr *TracedError) Error() string {
if Debug {
if ShowStack {
return fmt.Sprintf("Error: %s\nStack: %s", enErr.WrappedError.Error(), string(enErr.stack))
} else {
return fmt.Sprintf("Error: %s\nFile: %s\nLine: %d", enErr.WrappedError.Error(), enErr.file, enErr.line)
}
} else {
return enErr.WrappedError.Error()
}
}
// Returns the wrapped error, or nil if it doesn't exist.
// If it is not a traced error, just return the error by itself.
func Inner(e error) error {
if e == nil {
return nil
}
self, ok := e.(*TracedError)
if ok {
return self.WrappedError
}
return e
}
func stack() []byte {
buf := make([]byte, 32)
for {
n := runtime.Stack(buf, false)
if n < len(buf) {
break
}
buf = make([]byte, len(buf)*2)
}
return buf
}