/
errx.go
207 lines (179 loc) · 5.29 KB
/
errx.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
// errx 包提供一组类型与方法,用于将错误信息封装起来形成错误链,以便在程序中更精准的定位和跟踪错误。
package errx
import (
"errors"
"fmt"
"io"
"strconv"
"strings"
)
// ErrorWrapper 是一个 StackfulError ,封装另一个 error ,其表示引起当前错误的原因。
type ErrorWrapper struct {
ErrorCause
ErrorStack
msg string
}
var _ StackfulError = (*ErrorWrapper)(nil)
var _ fmt.Formatter = (*ErrorWrapper)(nil)
// Error 返回以 Describe() 的格式输出错误信息。
func (w *ErrorWrapper) Error() string {
// Describe() 不会调用 BizError 之外的 StackfulError.Error() ,所以不会死循环。
return Describe(w)
}
// Message 返回错误的描述信息,但不包含 Stack 。
//
// 返回格式如下,当前实例的 message 为空时,前置的“message:”部分被省略。
// - 若 cause 为 nil,则仅返回当前实例的错误信息;
// - 若 cause 为 StackfulError, 则返回: message: cause.ErrorWithoutStack() ;
// - 若 cause 不是 StackfulError, 则返回: message: cause.Error() 。
func (w *ErrorWrapper) ErrorWithoutStack() string {
c := w.Cause()
if c == nil {
return w.msg
}
prefix := w.msg
if w.msg != "" {
prefix += ": "
}
se, ok := w.Cause().(StackfulError)
if ok {
return prefix + se.ErrorWithoutStack()
}
return prefix + c.Error()
}
// Format 实现 fmt.Formatter.Formats() 。
// 支持:
//
// %s 输出 ErrorWithoutStack()
// %q 输出 strconv.Quote(ErrorWithoutStack())
// %v/%+v 输出 Error()
// other 输出 BADFORMAT: ErrorWithoutStack()
func (w *ErrorWrapper) Format(f fmt.State, verb rune) {
var out string
switch verb {
case 's':
out = w.ErrorWithoutStack()
case 'q':
out = strconv.Quote(w.ErrorWithoutStack())
case 'v':
out = w.Error()
default:
// 其他不支持的格式,输出: BADFORMAT:Message()
out = "BADFORMAT:" + w.ErrorWithoutStack()
}
io.WriteString(f, out)
}
// Wrap 封装给定的 error ,返回 StackfulError 。
// 错误信息的格式为: message: cause.Error() 。若 cause 为 nil,则仅返回 message 。
//
// 得到的 StackfulError.Stack() 有一个固定的开头“--- ”,末尾会有一个空行。格式为:
//
// --- stack text
func Wrap(message string, cause error) StackfulError {
return &ErrorWrapper{
ErrorCause: ErrorCause{cause},
ErrorStack: GetErrorStack(3), // 调用栈不包括当前函数。
msg: message,
}
}
// WrapWithoutStack 封装给定的 error 。和 Wrap() 类似,但不带有调用栈信息。
// 错误信息的格式为: message: cause.Error() 。若 cause 为 nil,则仅返回 message 。
func WrapWithoutStack(message string, cause error) StackfulError {
return &ErrorWrapper{
ErrorCause: ErrorCause{cause},
msg: message,
}
}
// PreserveRecover 用于封装从 panic 中 recover 的数据,返回 StackfulError 。
// 此方法的调用应放在 defer 过程里。
func PreserveRecover(message string, recovered interface{}) StackfulError {
if recovered == nil {
return nil
}
var cause error
switch e := recovered.(type) {
case error:
cause = e
case string:
cause = fmt.Errorf(e)
default:
// panic 的不是 error 和字符串也应该是个能转成字符串的东西。
cause = fmt.Errorf("%v", e)
}
return &ErrorWrapper{
ErrorCause: ErrorCause{cause},
ErrorStack: GetErrorStack(4), // 忽略当前函数、 panic 调用和 defer 的函数。
msg: message,
}
}
// Describe 返回一个字符串描述给定的错误。如果给定 nil ,返回空字符串。
//
// 递归使用 errors.Unwrap() 获取内部错误,并追加在描述信息上。如果错误是 StackfulError ,则描述携带调用栈信息。
// 若不能获取到对应的信息,则该部分省略。
//
// 可通过此方法获取完整的错误链信息。
//
// 输出格式为:
//
// 最外层错误描述
// --- 最外层错误的调用栈信息
// === 第1层内部错误的描述
// --- 第1层内部错误的调用栈信息
// === 第2层内部错误的描述
// --- 第2层内部错误的调用栈信息
// ...(逐层展示)
// === 最内层错误的描述
// --- 最内层错误的描述的调用栈信息
//
// 末尾总是一个空行。
func Describe(err error) string {
if err == nil {
return ""
}
var msg strings.Builder
for {
if msg.Len() > 0 {
msg.WriteString("=== ")
}
var buf string
switch e := err.(type) {
case StackfulError:
msg.WriteString(e.ErrorWithoutStack())
msg.WriteString("\n--- ")
buf = e.Stack()
default:
buf = e.Error()
}
if len(buf) > 0 {
msg.WriteString(buf)
if buf[len(buf)-1] != '\n' {
msg.WriteRune('\n')
}
}
err = errors.Unwrap(err)
if err == nil {
break
}
}
return msg.String()
}
// 执行给定的函数。
// 若函数成功执行,返回 nil ;若函数 panic ,则通过 [PreserveRecover] 捕获并返回对应的错误。
func Run(f func()) (err error) {
defer func() {
err = PreserveRecover("", recover())
}()
f()
return nil
}
// 执行带有一个 error 返回值的的函数。
// 若函数成功执行,返回函数的返回值;若函数 panic ,则通过 [PreserveRecover] 捕获并返回对应的错误。
func RunE(f func() error) (err error) {
defer func() {
if err == nil {
err = PreserveRecover("", recover())
}
}()
err = f()
return
}