blob: a67c7bacc985c3caac79528907d29f19b1a44f4f [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"
25 "sync"
26)
27
28// Encodes the given error into fields of an object. A field with the given
29// name is added for the error message.
30//
31// If the error implements fmt.Formatter, a field with the name ${key}Verbose
32// is also added with the full verbose error message.
33//
34// Finally, if the error implements errorGroup (from go.uber.org/multierr) or
35// causer (from github.com/pkg/errors), a ${key}Causes field is added with an
36// array of objects containing the errors this error was comprised of.
37//
38// {
39// "error": err.Error(),
40// "errorVerbose": fmt.Sprintf("%+v", err),
41// "errorCauses": [
42// ...
43// ],
44// }
45func encodeError(key string, err error, enc ObjectEncoder) error {
46 basic := err.Error()
47 enc.AddString(key, basic)
48
49 switch e := err.(type) {
50 case errorGroup:
51 return enc.AddArray(key+"Causes", errArray(e.Errors()))
52 case fmt.Formatter:
53 verbose := fmt.Sprintf("%+v", e)
54 if verbose != basic {
55 // This is a rich error type, like those produced by
56 // github.com/pkg/errors.
57 enc.AddString(key+"Verbose", verbose)
58 }
59 }
60 return nil
61}
62
63type errorGroup interface {
64 // Provides read-only access to the underlying list of errors, preferably
65 // without causing any allocs.
66 Errors() []error
67}
68
69type causer interface {
70 // Provides access to the error that caused this error.
71 Cause() error
72}
73
74// Note that errArry and errArrayElem are very similar to the version
75// implemented in the top-level error.go file. We can't re-use this because
76// that would require exporting errArray as part of the zapcore API.
77
78// Encodes a list of errors using the standard error encoding logic.
79type errArray []error
80
81func (errs errArray) MarshalLogArray(arr ArrayEncoder) error {
82 for i := range errs {
83 if errs[i] == nil {
84 continue
85 }
86
87 el := newErrArrayElem(errs[i])
88 arr.AppendObject(el)
89 el.Free()
90 }
91 return nil
92}
93
94var _errArrayElemPool = sync.Pool{New: func() interface{} {
95 return &errArrayElem{}
96}}
97
98// Encodes any error into a {"error": ...} re-using the same errors logic.
99//
100// May be passed in place of an array to build a single-element array.
101type errArrayElem struct{ err error }
102
103func newErrArrayElem(err error) *errArrayElem {
104 e := _errArrayElemPool.Get().(*errArrayElem)
105 e.err = err
106 return e
107}
108
109func (e *errArrayElem) MarshalLogArray(arr ArrayEncoder) error {
110 return arr.AppendObject(e)
111}
112
113func (e *errArrayElem) MarshalLogObject(enc ObjectEncoder) error {
114 return encodeError("error", e.err, enc)
115}
116
117func (e *errArrayElem) Free() {
118 e.err = nil
119 _errArrayElemPool.Put(e)
120}