This repository was archived by the owner on Aug 31, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathdoc.go
More file actions
193 lines (155 loc) · 6.67 KB
/
doc.go
File metadata and controls
193 lines (155 loc) · 6.67 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
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
/*
Package errors provides simple error handling primitives that work well with
structured logging.
This package is inspired by the excellent
github.com/pkg/errors package. A significant
amount of code and documentation in this package has been adapted from that
source.
A key difference between this package and github.com/pkg/errors is that
this package has been designed to suit programs that make use of
structured logging.
Some of the ideas in this package were proposed for package
github.com/pkg/errors, but after a reasonable amount of consideration, were
ultimately not included in that package.
(See https://github.com/pkg/errors/issues/34 for details).
If you are not using structured logging in your application and have no intention
of doing so, you will probably be better off using the github.com/pkg/errors
package in preference to this one.
Background
The traditional error handling idiom in Go is roughly akin to
if err != nil {
return err
}
which applied recursively up the call stack results in error reports without
context or debugging information. The errors package allows programmers to
add context to the failure path in their code in a way that does not destroy
the original value of the error.
Creating errors
The `errors` package provides three operations which combine to form a simple
yet powerful system for enhancing the value of returned errors:
New create a new error
Wrap wrap an existing error with an optional message
With attach key/value pairs to an error
The `New` function is used to create an error. This function is compatible
with the Go standard library `errors` package:
err := errors.New("emit macho dwarf: elf header corrupted")
The `Wrap` function returns an error that adds a message to the original
error. This additional message can be useful for putting the original error in
context. For example:
err := errors.New("permission denied")
fmt.Println(err)
err = errors.Wrap(err, "cannot list directory contents")
fmt.Println(err)
// Output:
// permission denied
// cannot list directory contents: permission denied
The `With` function accepts a variadic list of alternating key/value pairs, and
returns an error context that can be used to create a new error or wrap an existing error.
// create new error
err = errors.With("file", "testrun", "line", 101).New("file locked")
fmt.Println(err)
// wrap existing error
err = errors.With("attempt", 3).Wrap(err, "retry failed")
fmt.Println(err)
// Output:
// file locked file=testrun line=101
// retry failed attempt=3: file locked file=testrun line=101
One useful pattern is to create an error context that is used for an entire
function scope:
func doSomethingWith(file string, line int) error {
// set error context
errors := errors.With("file", file, "line", line)
if number <= 0 {
// file and line will be attached to the error
return errors.New("invalid number")
}
// ... later ...
if err := doOneThing(); err != nil {
// file and line will be attached to the error
return errors.Wrap(err, "cannot do one thing")
}
// ... and so on until ...
return nil
}
The errors returned by `New` and `Wrap` provide a `With` method that enables
a fluent-style of error handling:
// create new error
err = errors.New("file locked").With(
"file", "testrun",
"line", 101,
)
fmt.Println(err)
// wrap existing error
err = errors.Wrap(err, "retry failed").With("attempt", 3)
fmt.Println(err)
// Output:
// file locked file=testrun line=101
// retry failed attempt=3: file locked file=testrun line=101
Retrieving the cause of an error
Using errors.Wrap constructs a stack of errors, adding context to the
preceding error. Depending on the nature of the error it may be necessary
to reverse the operation of errors.Wrap to retrieve the original error for
inspection. Any error value which implements this interface can be inspected
by errors.Cause.
type causer interface {
Cause() error
}
errors.Cause will recursively retrieve the topmost error which does not
implement causer, which is assumed to be the original cause. For example:
switch err := errors.Cause(err).(type) {
case *MyError:
// handle specifically
default:
// unknown error
}
Retrieving key value pairs for structured logging
Errors created by `errors.Wrap` and `errors.New` implement the following
interface:
type keyvalser interface {
Keyvals() []interface{}
}
The Keyvals method returns an array of alternating keys and values. The
first key will always be "msg" and its value will be a string containing
the message associated with the wrapped error.
Example using go-kit logging (https://github.com/go-kit/kit/tree/master/log):
// logError logs details of an error to a structured error log.
func logError(logger log.Logger, err error) {
// start with timestamp and error level
keyvals := []interface{}{
"ts", time.Now().Format(time.RFC3339Nano),
"level", "error",
}
type keyvalser interface {
Keyvals() []interface{}
}
if kv, ok := err.(keyvalser); ok {
// error contains structured information, first key/value
// pair will be "msg".
keyvals = append(keyvals, kv.Keyvals()...)
} else {
// error does not contain structured information, use the
// Error() string as the message.
keyvals = append(keyvals, "msg", err.Error())
}
logger.Log(keyvals...)
}
GOOD ADVICE: Do not use the Keyvals method on an error to retrieve the
individual key/value pairs associated with an error for processing by the
calling program.
*/
package errors
// BUG(jpj): This package makes use of a fluent API for attaching key/value
// pairs to an error. Dave Cheney has written up some good reasons to avoid
// this approach: see https://github.com/pkg/errors/issues/15#issuecomment-221194128.
// Experience will show if this presents a problem, but to date it has felt like
// it leads to simpler, more readable code.
// BUG(jpj): Attaching key/value pairs to an error was considered for package
// github.com/pkg/errors, but in the end it was not implemented because
// of the potential for abusing the information in the error. See Dave Cheney's
// comment at https://github.com/pkg/errors/issues/34#issuecomment-228231192.
// This package has used the `keyvalser` interface as a mechanism for extracting
// key/value pairs from an error. In practice this seems to work quite well, but
// it would be possible to write code that abuses this interface by extractng
// information from the error for use by the program. The thinking is that the
// keyvalser interface is not an obvious part of the API as documented by GoDoc,
// and that should help minimize abuse.