blob: 95bdb0a126f493284f63c8da32eea5fddc5c0bc0 [file] [log] [blame]
Don Newton7577f072020-01-06 12:41:11 -05001// Copyright (c) 2016 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 "bytes"
25 "fmt"
26 "math"
27 "reflect"
28 "time"
29)
30
31// A FieldType indicates which member of the Field union struct should be used
32// and how it should be serialized.
33type FieldType uint8
34
35const (
36 // UnknownType is the default field type. Attempting to add it to an encoder will panic.
37 UnknownType FieldType = iota
38 // ArrayMarshalerType indicates that the field carries an ArrayMarshaler.
39 ArrayMarshalerType
40 // ObjectMarshalerType indicates that the field carries an ObjectMarshaler.
41 ObjectMarshalerType
42 // BinaryType indicates that the field carries an opaque binary blob.
43 BinaryType
44 // BoolType indicates that the field carries a bool.
45 BoolType
46 // ByteStringType indicates that the field carries UTF-8 encoded bytes.
47 ByteStringType
48 // Complex128Type indicates that the field carries a complex128.
49 Complex128Type
50 // Complex64Type indicates that the field carries a complex128.
51 Complex64Type
52 // DurationType indicates that the field carries a time.Duration.
53 DurationType
54 // Float64Type indicates that the field carries a float64.
55 Float64Type
56 // Float32Type indicates that the field carries a float32.
57 Float32Type
58 // Int64Type indicates that the field carries an int64.
59 Int64Type
60 // Int32Type indicates that the field carries an int32.
61 Int32Type
62 // Int16Type indicates that the field carries an int16.
63 Int16Type
64 // Int8Type indicates that the field carries an int8.
65 Int8Type
66 // StringType indicates that the field carries a string.
67 StringType
David K. Bainbridgee05cf0c2021-08-19 03:16:50 +000068 // TimeType indicates that the field carries a time.Time that is
69 // representable by a UnixNano() stored as an int64.
Don Newton7577f072020-01-06 12:41:11 -050070 TimeType
David K. Bainbridgee05cf0c2021-08-19 03:16:50 +000071 // TimeFullType indicates that the field carries a time.Time stored as-is.
72 TimeFullType
Don Newton7577f072020-01-06 12:41:11 -050073 // Uint64Type indicates that the field carries a uint64.
74 Uint64Type
75 // Uint32Type indicates that the field carries a uint32.
76 Uint32Type
77 // Uint16Type indicates that the field carries a uint16.
78 Uint16Type
79 // Uint8Type indicates that the field carries a uint8.
80 Uint8Type
81 // UintptrType indicates that the field carries a uintptr.
82 UintptrType
83 // ReflectType indicates that the field carries an interface{}, which should
84 // be serialized using reflection.
85 ReflectType
86 // NamespaceType signals the beginning of an isolated namespace. All
87 // subsequent fields should be added to the new namespace.
88 NamespaceType
89 // StringerType indicates that the field carries a fmt.Stringer.
90 StringerType
91 // ErrorType indicates that the field carries an error.
92 ErrorType
93 // SkipType indicates that the field is a no-op.
94 SkipType
David K. Bainbridgee05cf0c2021-08-19 03:16:50 +000095
96 // InlineMarshalerType indicates that the field carries an ObjectMarshaler
97 // that should be inlined.
98 InlineMarshalerType
Don Newton7577f072020-01-06 12:41:11 -050099)
100
101// A Field is a marshaling operation used to add a key-value pair to a logger's
102// context. Most fields are lazily marshaled, so it's inexpensive to add fields
103// to disabled debug-level log statements.
104type Field struct {
105 Key string
106 Type FieldType
107 Integer int64
108 String string
109 Interface interface{}
110}
111
112// AddTo exports a field through the ObjectEncoder interface. It's primarily
113// useful to library authors, and shouldn't be necessary in most applications.
114func (f Field) AddTo(enc ObjectEncoder) {
115 var err error
116
117 switch f.Type {
118 case ArrayMarshalerType:
119 err = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler))
120 case ObjectMarshalerType:
121 err = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler))
David K. Bainbridgee05cf0c2021-08-19 03:16:50 +0000122 case InlineMarshalerType:
123 err = f.Interface.(ObjectMarshaler).MarshalLogObject(enc)
Don Newton7577f072020-01-06 12:41:11 -0500124 case BinaryType:
125 enc.AddBinary(f.Key, f.Interface.([]byte))
126 case BoolType:
127 enc.AddBool(f.Key, f.Integer == 1)
128 case ByteStringType:
129 enc.AddByteString(f.Key, f.Interface.([]byte))
130 case Complex128Type:
131 enc.AddComplex128(f.Key, f.Interface.(complex128))
132 case Complex64Type:
133 enc.AddComplex64(f.Key, f.Interface.(complex64))
134 case DurationType:
135 enc.AddDuration(f.Key, time.Duration(f.Integer))
136 case Float64Type:
137 enc.AddFloat64(f.Key, math.Float64frombits(uint64(f.Integer)))
138 case Float32Type:
139 enc.AddFloat32(f.Key, math.Float32frombits(uint32(f.Integer)))
140 case Int64Type:
141 enc.AddInt64(f.Key, f.Integer)
142 case Int32Type:
143 enc.AddInt32(f.Key, int32(f.Integer))
144 case Int16Type:
145 enc.AddInt16(f.Key, int16(f.Integer))
146 case Int8Type:
147 enc.AddInt8(f.Key, int8(f.Integer))
148 case StringType:
149 enc.AddString(f.Key, f.String)
150 case TimeType:
151 if f.Interface != nil {
152 enc.AddTime(f.Key, time.Unix(0, f.Integer).In(f.Interface.(*time.Location)))
153 } else {
154 // Fall back to UTC if location is nil.
155 enc.AddTime(f.Key, time.Unix(0, f.Integer))
156 }
David K. Bainbridgee05cf0c2021-08-19 03:16:50 +0000157 case TimeFullType:
158 enc.AddTime(f.Key, f.Interface.(time.Time))
Don Newton7577f072020-01-06 12:41:11 -0500159 case Uint64Type:
160 enc.AddUint64(f.Key, uint64(f.Integer))
161 case Uint32Type:
162 enc.AddUint32(f.Key, uint32(f.Integer))
163 case Uint16Type:
164 enc.AddUint16(f.Key, uint16(f.Integer))
165 case Uint8Type:
166 enc.AddUint8(f.Key, uint8(f.Integer))
167 case UintptrType:
168 enc.AddUintptr(f.Key, uintptr(f.Integer))
169 case ReflectType:
170 err = enc.AddReflected(f.Key, f.Interface)
171 case NamespaceType:
172 enc.OpenNamespace(f.Key)
173 case StringerType:
174 err = encodeStringer(f.Key, f.Interface, enc)
175 case ErrorType:
David K. Bainbridgee05cf0c2021-08-19 03:16:50 +0000176 err = encodeError(f.Key, f.Interface.(error), enc)
Don Newton7577f072020-01-06 12:41:11 -0500177 case SkipType:
178 break
179 default:
180 panic(fmt.Sprintf("unknown field type: %v", f))
181 }
182
183 if err != nil {
184 enc.AddString(fmt.Sprintf("%sError", f.Key), err.Error())
185 }
186}
187
188// Equals returns whether two fields are equal. For non-primitive types such as
189// errors, marshalers, or reflect types, it uses reflect.DeepEqual.
190func (f Field) Equals(other Field) bool {
191 if f.Type != other.Type {
192 return false
193 }
194 if f.Key != other.Key {
195 return false
196 }
197
198 switch f.Type {
199 case BinaryType, ByteStringType:
200 return bytes.Equal(f.Interface.([]byte), other.Interface.([]byte))
201 case ArrayMarshalerType, ObjectMarshalerType, ErrorType, ReflectType:
202 return reflect.DeepEqual(f.Interface, other.Interface)
203 default:
204 return f == other
205 }
206}
207
208func addFields(enc ObjectEncoder, fields []Field) {
209 for i := range fields {
210 fields[i].AddTo(enc)
211 }
212}
213
David K. Bainbridgee05cf0c2021-08-19 03:16:50 +0000214func encodeStringer(key string, stringer interface{}, enc ObjectEncoder) (retErr error) {
215 // Try to capture panics (from nil references or otherwise) when calling
216 // the String() method, similar to https://golang.org/src/fmt/print.go#L540
Don Newton7577f072020-01-06 12:41:11 -0500217 defer func() {
David K. Bainbridgee05cf0c2021-08-19 03:16:50 +0000218 if err := recover(); err != nil {
219 // If it's a nil pointer, just say "<nil>". The likeliest causes are a
220 // Stringer that fails to guard against nil or a nil pointer for a
221 // value receiver, and in either case, "<nil>" is a nice result.
222 if v := reflect.ValueOf(stringer); v.Kind() == reflect.Ptr && v.IsNil() {
223 enc.AddString(key, "<nil>")
224 return
225 }
226
227 retErr = fmt.Errorf("PANIC=%v", err)
Don Newton7577f072020-01-06 12:41:11 -0500228 }
229 }()
230
231 enc.AddString(key, stringer.(fmt.Stringer).String())
David K. Bainbridgee05cf0c2021-08-19 03:16:50 +0000232 return nil
Don Newton7577f072020-01-06 12:41:11 -0500233}