blob: f2a07d7864128aacbc061d8cb476e9ebb27f0096 [file] [log] [blame]
Don Newton7577f072020-01-06 12:41:11 -05001// Copyright (c) 2017 Uber Technologies, Inc.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20
21package zapcore
22
23import (
24 "fmt"
David K. Bainbridgee05cf0c2021-08-19 03:16:50 +000025 "reflect"
Don Newton7577f072020-01-06 12:41:11 -050026 "sync"
27)
28
29// Encodes the given error into fields of an object. A field with the given
30// name is added for the error message.
31//
32// If the error implements fmt.Formatter, a field with the name ${key}Verbose
33// is also added with the full verbose error message.
34//
35// Finally, if the error implements errorGroup (from go.uber.org/multierr) or
36// causer (from github.com/pkg/errors), a ${key}Causes field is added with an
37// array of objects containing the errors this error was comprised of.
38//
39// {
40// "error": err.Error(),
41// "errorVerbose": fmt.Sprintf("%+v", err),
42// "errorCauses": [
43// ...
44// ],
45// }
David K. Bainbridgee05cf0c2021-08-19 03:16:50 +000046func encodeError(key string, err error, enc ObjectEncoder) (retErr error) {
47 // Try to capture panics (from nil references or otherwise) when calling
48 // the Error() method
49 defer func() {
50 if rerr := recover(); rerr != nil {
51 // If it's a nil pointer, just say "<nil>". The likeliest causes are a
52 // error that fails to guard against nil or a nil pointer for a
53 // value receiver, and in either case, "<nil>" is a nice result.
54 if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {
55 enc.AddString(key, "<nil>")
56 return
57 }
58
59 retErr = fmt.Errorf("PANIC=%v", rerr)
60 }
61 }()
62
Don Newton7577f072020-01-06 12:41:11 -050063 basic := err.Error()
64 enc.AddString(key, basic)
65
66 switch e := err.(type) {
67 case errorGroup:
68 return enc.AddArray(key+"Causes", errArray(e.Errors()))
69 case fmt.Formatter:
70 verbose := fmt.Sprintf("%+v", e)
71 if verbose != basic {
72 // This is a rich error type, like those produced by
73 // github.com/pkg/errors.
74 enc.AddString(key+"Verbose", verbose)
75 }
76 }
77 return nil
78}
79
80type errorGroup interface {
81 // Provides read-only access to the underlying list of errors, preferably
82 // without causing any allocs.
83 Errors() []error
84}
85
Don Newton7577f072020-01-06 12:41:11 -050086// Note that errArry and errArrayElem are very similar to the version
87// implemented in the top-level error.go file. We can't re-use this because
88// that would require exporting errArray as part of the zapcore API.
89
90// Encodes a list of errors using the standard error encoding logic.
91type errArray []error
92
93func (errs errArray) MarshalLogArray(arr ArrayEncoder) error {
94 for i := range errs {
95 if errs[i] == nil {
96 continue
97 }
98
99 el := newErrArrayElem(errs[i])
100 arr.AppendObject(el)
101 el.Free()
102 }
103 return nil
104}
105
106var _errArrayElemPool = sync.Pool{New: func() interface{} {
107 return &errArrayElem{}
108}}
109
110// Encodes any error into a {"error": ...} re-using the same errors logic.
111//
112// May be passed in place of an array to build a single-element array.
113type errArrayElem struct{ err error }
114
115func newErrArrayElem(err error) *errArrayElem {
116 e := _errArrayElemPool.Get().(*errArrayElem)
117 e.err = err
118 return e
119}
120
121func (e *errArrayElem) MarshalLogArray(arr ArrayEncoder) error {
122 return arr.AppendObject(e)
123}
124
125func (e *errArrayElem) MarshalLogObject(enc ObjectEncoder) error {
126 return encodeError("error", e.err, enc)
127}
128
129func (e *errArrayElem) Free() {
130 e.err = nil
131 _errArrayElemPool.Put(e)
132}