forked from gookit/goutil
-
Notifications
You must be signed in to change notification settings - Fork 0
/
errorx.go
339 lines (291 loc) · 7.3 KB
/
errorx.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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
// Package errorx provide an enhanced error implements for go,
// allow with stacktraces and wrap another error.
package errorx
import (
"bytes"
"errors"
"fmt"
"io"
)
// Causer interface for get first cause error
type Causer interface {
// Cause returns the first cause error by call err.Cause().
// Otherwise, will returns current error.
Cause() error
}
// Unwrapper interface for get previous error
type Unwrapper interface {
// Unwrap returns previous error by call err.Unwrap().
// Otherwise, will returns nil.
Unwrap() error
}
// XErrorFace interface
type XErrorFace interface {
error
Causer
Unwrapper
}
// Exception interface
// type Exception interface {
// XErrorFace
// Code() string
// Message() string
// StackString() string
// }
/*************************************************************
* implements XErrorFace interface
*************************************************************/
// ErrorX struct
//
// TIPS:
//
// fmt pkg call order: Format > GoString > Error > String
type ErrorX struct {
// trace stack
*stack
prev error
msg string
}
// Cause implements Causer.
func (e *ErrorX) Cause() error {
if e.prev == nil {
return e
}
if ex, ok := e.prev.(*ErrorX); ok {
return ex.Cause()
}
return e.prev
}
// Unwrap implements Unwrapper.
func (e *ErrorX) Unwrap() error {
return e.prev
}
// Format error, will output stack information.
func (e *ErrorX) Format(s fmt.State, verb rune) {
// format current error: only output on have msg
if len(e.msg) > 0 {
_, _ = io.WriteString(s, e.msg)
if e.stack != nil {
e.stack.Format(s, verb)
}
}
// format prev error
if e.prev == nil {
return
}
_, _ = s.Write([]byte("\nPrevious: "))
if ex, ok := e.prev.(*ErrorX); ok {
ex.Format(s, verb)
} else {
_, _ = s.Write([]byte(e.prev.Error()))
}
}
// GoString to GO string, contains stack information.
// printing an error with %#v will produce useful information.
func (e *ErrorX) GoString() string {
// var sb strings.Builder
var buf bytes.Buffer
_, _ = e.WriteTo(&buf)
return buf.String()
}
// Error msg string, not contains stack information.
func (e *ErrorX) Error() string {
var buf bytes.Buffer
e.writeMsgTo(&buf)
return buf.String()
}
// String error to string, contains stack information.
func (e *ErrorX) String() string {
return e.GoString()
}
// WriteTo write the error to a writer, contains stack information.
func (e *ErrorX) WriteTo(w io.Writer) (n int64, err error) {
// current error: only output on have msg
if len(e.msg) > 0 {
_, _ = w.Write([]byte(e.msg))
// with stack
if e.stack != nil {
_, _ = e.stack.WriteTo(w)
}
}
// with prev error
if e.prev != nil {
_, _ = io.WriteString(w, "\nPrevious: ")
if ex, ok := e.prev.(*ErrorX); ok {
_, _ = ex.WriteTo(w)
} else {
_, _ = io.WriteString(w, e.prev.Error())
}
}
return
}
// Message error message of current
func (e *ErrorX) Message() string {
return e.msg
}
// StackString returns error stack string of current.
func (e *ErrorX) StackString() string {
if e.stack != nil {
return e.stack.String()
}
return ""
}
// writeMsgTo write the error msg to a writer
func (e *ErrorX) writeMsgTo(w io.Writer) {
// current error
if len(e.msg) > 0 {
_, _ = w.Write([]byte(e.msg))
}
// with prev error
if e.prev != nil {
_, _ = w.Write([]byte("; "))
if ex, ok := e.prev.(*ErrorX); ok {
ex.writeMsgTo(w)
} else {
_, _ = io.WriteString(w, e.prev.Error())
}
}
}
// CallerFunc returns the error caller func. if stack is nil, will return nil
func (e *ErrorX) CallerFunc() *Func {
if e.stack == nil {
return nil
}
return FuncForPC(e.stack.CallerPC())
}
// Location information for the caller func. more please see CallerFunc
//
// Returns eg:
//
// github.com/gookit/goutil/errorx_test.TestWithPrev(), errorx_test.go:34
func (e *ErrorX) Location() string {
if e.stack == nil {
return "unknown"
}
return e.CallerFunc().Location()
}
/*************************************************************
* new error with call stacks
*************************************************************/
// New error message and with caller stacks
func New(msg string) error {
return &ErrorX{
msg: msg,
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
}
}
// Newf error with format message, and with caller stacks.
// alias of Errorf()
func Newf(tpl string, vars ...any) error {
return &ErrorX{
msg: fmt.Sprintf(tpl, vars...),
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
}
}
// Errorf error with format message, and with caller stacks
func Errorf(tpl string, vars ...any) error {
return &ErrorX{
msg: fmt.Sprintf(tpl, vars...),
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
}
}
// With prev error and error message, and with caller stacks
func With(err error, msg string) error {
return &ErrorX{
msg: msg,
prev: err,
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
}
}
// Withf error and with format message, and with caller stacks
func Withf(err error, tpl string, vars ...any) error {
return &ErrorX{
msg: fmt.Sprintf(tpl, vars...),
prev: err,
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
}
}
// WithPrev error and message, and with caller stacks. alias of With()
func WithPrev(err error, msg string) error {
return &ErrorX{
msg: msg,
prev: err,
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
}
}
// WithPrevf error and with format message, and with caller stacks. alias of Withf()
func WithPrevf(err error, tpl string, vars ...any) error {
return &ErrorX{
msg: fmt.Sprintf(tpl, vars...),
prev: err,
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
}
}
/*************************************************************
* wrap go error with call stacks
*************************************************************/
// WithStack wrap a go error with a stacked trace. If err is nil, will return nil.
func WithStack(err error) error {
if err == nil {
return nil
}
return &ErrorX{
msg: err.Error(),
// prev: err,
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
}
}
// Traced warp a go error and with caller stacks. alias of WithStack()
func Traced(err error) error {
if err == nil {
return nil
}
return &ErrorX{
msg: err.Error(),
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
}
}
// Stacked warp a go error and with caller stacks. alias of WithStack()
func Stacked(err error) error {
if err == nil {
return nil
}
return &ErrorX{
msg: err.Error(),
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
}
}
// WithOptions new error with some option func
func WithOptions(msg string, fns ...func(opt *ErrStackOpt)) error {
opt := newErrOpt()
for _, fn := range fns {
fn(opt)
}
return &ErrorX{
msg: msg,
stack: callersStack(opt.SkipDepth, opt.TraceDepth),
}
}
/*************************************************************
* helper func for wrap error without stacks
*************************************************************/
// Wrap error and with message, but not with stack
func Wrap(err error, msg string) error {
if err == nil {
return errors.New(msg)
}
return &ErrorX{
msg: msg,
prev: err,
}
}
// Wrapf error with format message, but not with stack
func Wrapf(err error, tpl string, vars ...any) error {
if err == nil {
return fmt.Errorf(tpl, vars...)
}
return &ErrorX{
msg: fmt.Sprintf(tpl, vars...),
prev: err,
}
}