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
|
package xerrors
import (
"fmt"
"io"
"strings"
"github.com/ydb-platform/ydb/library/go/x/xruntime"
)
type wrappedErrorf struct {
err error
stacktrace *xruntime.StackTrace
}
var _ ErrorStackTrace = &wrappedErrorf{}
func Errorf(format string, a ...interface{}) error {
err := fmt.Errorf(format, a...)
return &wrappedErrorf{
err: err,
stacktrace: newStackTrace(1, err),
}
}
func SkipErrorf(skip int, format string, a ...interface{}) error {
err := fmt.Errorf(format, a...)
return &wrappedErrorf{
err: err,
stacktrace: newStackTrace(skip+1, err),
}
}
func (e *wrappedErrorf) Format(s fmt.State, v rune) {
switch v {
case 'v':
if s.Flag('+') {
msg := e.err.Error()
inner := Unwrap(e.err)
// If Errorf wrapped another error then it will be our message' suffix. If so, cut it since otherwise we will
// print it again as part of formatting that error.
if inner != nil {
if strings.HasSuffix(msg, inner.Error()) {
msg = msg[:len(msg)-len(inner.Error())]
// Cut last space if needed but only if there is stacktrace present (very likely)
if e.stacktrace != nil && strings.HasSuffix(msg, ": ") {
msg = msg[:len(msg)-1]
}
}
}
_, _ = io.WriteString(s, msg)
if e.stacktrace != nil {
// New line is useful only when printing frames, otherwise it is better to print next error in the chain
// right after we print this one
_, _ = io.WriteString(s, "\n")
writeStackTrace(s, e.stacktrace)
}
// Print next error down the chain if there is one
if inner != nil {
_, _ = fmt.Fprintf(s, "%+v", inner)
}
return
}
fallthrough
case 's':
_, _ = io.WriteString(s, e.err.Error())
case 'q':
_, _ = fmt.Fprintf(s, "%q", e.err.Error())
}
}
func (e *wrappedErrorf) Error() string {
// Wrapped error has correct formatting
return e.err.Error()
}
func (e *wrappedErrorf) Unwrap() error {
// Skip wrapped error and return whatever it is wrapping if inner error contains single error
// TODO: test for correct unwrap
if _, ok := e.err.(interface{ Unwrap() []error }); ok {
return e.err
}
return Unwrap(e.err)
}
func (e *wrappedErrorf) StackTrace() *xruntime.StackTrace {
return e.stacktrace
}
|