-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherror.go
More file actions
157 lines (137 loc) · 3.46 KB
/
error.go
File metadata and controls
157 lines (137 loc) · 3.46 KB
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
package errors
// Error represents a structured error with metadata, custom fields, stack trace, and optional wrapping.
type Error struct {
metadata
fields map[string]any
stack []StackFrame
pureWrapper bool
err error
}
// Error returns the error message, including any wrapped error messages.
func (e *Error) Error() string {
res := e.message
if e.err != nil {
prev := e.err.Error()
if prev != "" {
if res != "" {
res += ": " + prev
} else {
res = prev
}
}
}
return res
}
// WrappedErrors returns a slice of all wrapped errors, including the current one if it's not a pure wrapper.
func (err *Error) WrappedErrors() []Error {
var res []Error
if !err.pureWrapper {
res = []Error{*err}
}
e := err.err
for {
if e == nil {
break
}
switch x := e.(type) {
case *Error:
if !x.metadata.empty() {
res = append(res, *x)
}
e = x.err
default:
res = append(res, Error{err: e, pureWrapper: true})
e = nil
}
}
return res
}
// Wrap returns a new Error that wraps the given error while retaining the current error's metadata and fields.
// It also preserves the stack trace of the wrapped error if available.
// The new error is marked as a pure wrapper if the original error is of type ErrorTemplate or Error.
// If the original error is nil, it returns the current error.
// This method is useful for chaining errors and maintaining context.
// It supports wrapping both ErrorTemplate and Error types, preserving their fields and stack trace.
func (e *Error) Wrap(err error) *Error {
if err == nil {
return e
}
switch x := err.(type) {
case *ErrorTemplate:
return &Error{
metadata: e.metadata,
fields: cloneMap(e.fields),
pureWrapper: true,
err: err,
stack: DefaultCallerFrames(3),
}
case *Error:
res := Error{
metadata: e.metadata,
fields: cloneMap(e.fields),
pureWrapper: true,
err: err,
}
if x.stack != nil {
res.stack = x.stack
} else {
res.stack = DefaultCallerFrames(3)
}
if len(x.fields) > 0 {
if res.fields == nil {
res.fields = make(map[string]any, len(x.fields))
}
for k, v := range x.fields {
res.fields[k] = v
}
}
return &res
}
return &Error{
metadata: e.metadata,
err: err,
fields: cloneMap(e.fields),
pureWrapper: true,
stack: DefaultCallerFrames(3),
}
}
// Set adds or updates a custom key-value pair in the error's fields.
func (e *Error) Set(key string, value any) *Error {
if e.fields == nil {
e.fields = make(map[string]any)
}
e.fields[key] = value
return e
}
// Code sets a custom application-specific code for the error.
func (e *Error) Code(code string) *Error {
e.code = code
return e
}
// Severity sets the severity level for the error.
func (e *Error) Severity(severity SeverityLevel) *Error {
e.severity = severity
return e
}
// StatusCode sets the associated HTTP status code for the error.
func (e *Error) StatusCode(statusCode int) *Error {
e.statusCode = statusCode
return e
}
// Protected marks the error as protected to prevent certain modifications or exposure.
func (e *Error) Protected(protected bool) *Error {
e.protected = protected
return e
}
// Msg sets the error message and marks the error as not being a pure wrapper.
func (e *Error) Msg(s string) *Error {
e.message = s
e.pureWrapper = false
return e
}
// Alarm triggers an alert for the error if an alarmer is configured.
func (e *Error) Alarm() {
if alarmer != nil {
alarmer.Alarm(e)
}
}