-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patherror.go
243 lines (211 loc) · 7.2 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
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
package errors
import (
"fmt"
errs "github.com/pkg/errors"
"regexp"
)
// New create custom error with the provided params && error message
func New(
errType ErrorType, errLevel ErrorLevel, baggage ErrorBaggage,
severity ErrorSeverity, message ErrorMessage,
) CustomError {
// Initialize empty ErrorBaggage map to prevent panics
if baggage == nil {
baggage = make(ErrorBaggage)
}
return newCustomErr(errType, baggage, errLevel, severity, errs.New(message.String()))
}
// NewF create custom error with params && error message that formats according to a format specifier
func NewF(
errType ErrorType, errLevel ErrorLevel, baggage ErrorBaggage,
severity ErrorSeverity, format string, args ...interface{},
) CustomError {
// Initialize empty ErrorBaggage map to prevent panics
if baggage == nil {
baggage = make(ErrorBaggage)
}
return newCustomErr(errType, baggage, errLevel, severity, errs.Errorf(format, args...))
}
// NewBase create custom error with specified message
// also all error attributes are set to default values
func NewBase(message ErrorMessage) CustomError {
return newCustomErr(DefaultType, make(ErrorBaggage), DefaultLevel, DefaultSeverity, errs.New(message.String()))
}
// NewBaseF create custom error with error message that formats according to a format specifier
// also all error attributes are set to default values
func NewBaseF(format string, args ...interface{}) CustomError {
return newCustomErr(DefaultType, make(ErrorBaggage), DefaultLevel, DefaultSeverity, errs.Errorf(format, args...))
}
// Wrap error with message. If wrapped error implements CustomError interface than all semantic data such
// a severity, error level, error type will be copied into result error
func Wrap(err error, message ErrorMessage) CustomError {
wrappedErr := errs.Wrap(err, message.String())
if customErr, ok := err.(CustomError); ok {
return newCustomErr(customErr.GetType(), make(ErrorBaggage), customErr.GetLevel(), customErr.GetSeverity(), wrappedErr)
}
return newCustomErr(DefaultType, make(ErrorBaggage), DefaultLevel, DefaultSeverity, wrappedErr)
}
// WrapF is analogous to the Wrap method, except that instead of ErrorMessage there are formatting arguments for the message
func WrapF(err error, format string, args ...interface{}) CustomError {
wrappedErr := errs.Wrapf(err, format, args...)
if customErr, ok := err.(CustomError); ok {
return newCustomErr(customErr.GetType(), make(ErrorBaggage), customErr.GetLevel(), customErr.GetSeverity(), wrappedErr)
}
return newCustomErr(DefaultType, make(ErrorBaggage), DefaultLevel, DefaultSeverity, wrappedErr)
}
const callerSkip = 1
type ErrorLevel int
type ErrorSeverity int
type ErrorBaggage map[string]interface{}
// customErr provides custom err struct type
type customErr struct {
// Describes an error in the form of a code, analogous to http error
errType ErrorType
// Map of interface{} values that related to error
baggage ErrorBaggage
// Describes the error from the data level where the error occurred
level ErrorLevel
// It is used to indicate the severity of errors and can be used
// For example, to determine whether a given error should be recorded in the debug log
severity ErrorSeverity
// Original/Wrapped error
//wrappedErr Unwrapped
wrappedErr error
}
type ErrorMessage string
func (m ErrorMessage) String() string {
return string(m)
}
type ErrorPath string
func (p ErrorPath) String() string {
return string(p)
}
// newCustomErr is constructor for customErr struct
func newCustomErr(errType ErrorType, baggage ErrorBaggage, dataLayer ErrorLevel, severity ErrorSeverity, originalErr error) *customErr {
return &customErr{errType: errType, baggage: baggage, level: dataLayer, severity: severity, wrappedErr: originalErr}
}
// GetLevel returns the error level based on the data level at which the error occurred
func (e *customErr) GetLevel() ErrorLevel {
return e.level
}
// SetLevel set data layer level
func (e *customErr) SetLevel(dataLayer ErrorLevel) CustomError {
e.level = dataLayer
return e
}
// GetType returns a code of customErr
func (e *customErr) GetType() ErrorType {
return e.errType
}
// GetSeverity return severity value
func (e *customErr) GetSeverity() ErrorSeverity {
return e.severity
}
// SetSeverity set severity value
func (e *customErr) SetSeverity(severity ErrorSeverity) CustomError {
e.severity = severity
return e
}
// GetMessage returns a message of error with path && errorMessage
func (e *customErr) GetMessage() ErrorMessage {
re := regexp.MustCompile(`(?U)(.+):`)
errMessage := e.wrappedErr.Error()
if result := re.FindStringSubmatch(errMessage); len(result) > 0 {
return ErrorMessage(result[1])
}
return ErrorMessage(errMessage)
}
// GetBaggage return baggage of error
func (e *customErr) GetBaggage() ErrorBaggage {
return e.baggage
}
// AddBaggage add fields with values to err
func (e *customErr) AddBaggage(baggage ErrorBaggage) CustomError {
for k, v := range baggage {
e.baggage[k] = v
}
return e
}
// SetBaggage set baggage of error
func (e *customErr) SetBaggage(baggage ErrorBaggage) CustomError {
// do not allow using nil pointer as a storage
if baggage == nil {
e.baggage = make(ErrorBaggage)
return e
}
e.baggage = baggage
return e
}
// GetPath return path of error
func (e *customErr) GetPath() ErrorPath {
if err, ok := e.wrappedErr.(stackTracer); ok {
st := err.StackTrace()
if len(st) == 0 {
return ""
}
if len(st) <= callerSkip {
return ErrorPath(fmt.Sprintf("%+s:%d", st[0], st[0]))
} else {
return ErrorPath(fmt.Sprintf("%+s:%d", st[callerSkip], st[callerSkip]))
}
}
return ""
}
// Error represent string value of customErr
func (e *customErr) Error() string {
return e.wrappedErr.Error()
}
func (e *customErr) GetTraceSlice() (trace []string) {
stack := make([]CustomError, 0)
e.getStack(&stack)
for _, v := range stack {
trace = append(trace, fmt.Sprintf("Message: %s, Path: %s", v.GetMessage().String(), v.GetPath().String()))
}
trace = append(trace, fmt.Sprintf("Cause: %+v", Cause(e)))
return trace
}
// Unwrap return wrapped error with standard error interface
func (e *customErr) Unwrap() error {
if pkgErr, ok := e.wrappedErr.(Unwrapped); ok {
// Unwrap wrapped Err and get StackErr
if pkgWithStack, ok := pkgErr.Unwrap().(Unwrapped); ok {
// Unwrap pkg StackErr
return pkgWithStack.Unwrap()
}
}
return e.wrappedErr
}
func Cause(e CustomError) error {
if val, ok := e.Unwrap().(CustomError); ok {
return Cause(val)
} else {
return e.Unwrap()
}
}
// Is - is the function that is used to compare errors by ErrorType
func (e *customErr) Is(target error) bool {
if err, ok := target.(CustomError); ok && err.GetType() == e.errType {
return true
}
return false
}
// IsMessageExistInStack checks if there is an error with the specified parameters in the error stack
func (e *customErr) IsMessageExistInStack(message ErrorMessage) bool {
stack := make([]CustomError, 0)
e.getStack(&stack)
for k, v := range stack {
if v.GetMessage() == message {
return true
} else if k == (len(stack)-1) && v.Unwrap().Error() == message.String() {
return true
}
}
return false
}
// getStack return slice of errors
func (e *customErr) getStack(result *[]CustomError) {
*result = append(*result, e)
if val, ok := e.Unwrap().(CustomError); ok {
val.getStack(result)
}
}