blob: ae772e4a170762a6dfe1d74d9ee3be0fa9be1126 [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
68 // TimeType indicates that the field carries a time.Time.
69 TimeType
70 // Uint64Type indicates that the field carries a uint64.
71 Uint64Type
72 // Uint32Type indicates that the field carries a uint32.
73 Uint32Type
74 // Uint16Type indicates that the field carries a uint16.
75 Uint16Type
76 // Uint8Type indicates that the field carries a uint8.
77 Uint8Type
78 // UintptrType indicates that the field carries a uintptr.
79 UintptrType
80 // ReflectType indicates that the field carries an interface{}, which should
81 // be serialized using reflection.
82 ReflectType
83 // NamespaceType signals the beginning of an isolated namespace. All
84 // subsequent fields should be added to the new namespace.
85 NamespaceType
86 // StringerType indicates that the field carries a fmt.Stringer.
87 StringerType
88 // ErrorType indicates that the field carries an error.
89 ErrorType
90 // SkipType indicates that the field is a no-op.
91 SkipType
92)
93
94// A Field is a marshaling operation used to add a key-value pair to a logger's
95// context. Most fields are lazily marshaled, so it's inexpensive to add fields
96// to disabled debug-level log statements.
97type Field struct {
98 Key string
99 Type FieldType
100 Integer int64
101 String string
102 Interface interface{}
103}
104
105// AddTo exports a field through the ObjectEncoder interface. It's primarily
106// useful to library authors, and shouldn't be necessary in most applications.
107func (f Field) AddTo(enc ObjectEncoder) {
108 var err error
109
110 switch f.Type {
111 case ArrayMarshalerType:
112 err = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler))
113 case ObjectMarshalerType:
114 err = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler))
115 case BinaryType:
116 enc.AddBinary(f.Key, f.Interface.([]byte))
117 case BoolType:
118 enc.AddBool(f.Key, f.Integer == 1)
119 case ByteStringType:
120 enc.AddByteString(f.Key, f.Interface.([]byte))
121 case Complex128Type:
122 enc.AddComplex128(f.Key, f.Interface.(complex128))
123 case Complex64Type:
124 enc.AddComplex64(f.Key, f.Interface.(complex64))
125 case DurationType:
126 enc.AddDuration(f.Key, time.Duration(f.Integer))
127 case Float64Type:
128 enc.AddFloat64(f.Key, math.Float64frombits(uint64(f.Integer)))
129 case Float32Type:
130 enc.AddFloat32(f.Key, math.Float32frombits(uint32(f.Integer)))
131 case Int64Type:
132 enc.AddInt64(f.Key, f.Integer)
133 case Int32Type:
134 enc.AddInt32(f.Key, int32(f.Integer))
135 case Int16Type:
136 enc.AddInt16(f.Key, int16(f.Integer))
137 case Int8Type:
138 enc.AddInt8(f.Key, int8(f.Integer))
139 case StringType:
140 enc.AddString(f.Key, f.String)
141 case TimeType:
142 if f.Interface != nil {
143 enc.AddTime(f.Key, time.Unix(0, f.Integer).In(f.Interface.(*time.Location)))
144 } else {
145 // Fall back to UTC if location is nil.
146 enc.AddTime(f.Key, time.Unix(0, f.Integer))
147 }
148 case Uint64Type:
149 enc.AddUint64(f.Key, uint64(f.Integer))
150 case Uint32Type:
151 enc.AddUint32(f.Key, uint32(f.Integer))
152 case Uint16Type:
153 enc.AddUint16(f.Key, uint16(f.Integer))
154 case Uint8Type:
155 enc.AddUint8(f.Key, uint8(f.Integer))
156 case UintptrType:
157 enc.AddUintptr(f.Key, uintptr(f.Integer))
158 case ReflectType:
159 err = enc.AddReflected(f.Key, f.Interface)
160 case NamespaceType:
161 enc.OpenNamespace(f.Key)
162 case StringerType:
163 err = encodeStringer(f.Key, f.Interface, enc)
164 case ErrorType:
165 encodeError(f.Key, f.Interface.(error), enc)
166 case SkipType:
167 break
168 default:
169 panic(fmt.Sprintf("unknown field type: %v", f))
170 }
171
172 if err != nil {
173 enc.AddString(fmt.Sprintf("%sError", f.Key), err.Error())
174 }
175}
176
177// Equals returns whether two fields are equal. For non-primitive types such as
178// errors, marshalers, or reflect types, it uses reflect.DeepEqual.
179func (f Field) Equals(other Field) bool {
180 if f.Type != other.Type {
181 return false
182 }
183 if f.Key != other.Key {
184 return false
185 }
186
187 switch f.Type {
188 case BinaryType, ByteStringType:
189 return bytes.Equal(f.Interface.([]byte), other.Interface.([]byte))
190 case ArrayMarshalerType, ObjectMarshalerType, ErrorType, ReflectType:
191 return reflect.DeepEqual(f.Interface, other.Interface)
192 default:
193 return f == other
194 }
195}
196
197func addFields(enc ObjectEncoder, fields []Field) {
198 for i := range fields {
199 fields[i].AddTo(enc)
200 }
201}
202
203func encodeStringer(key string, stringer interface{}, enc ObjectEncoder) (err error) {
204 defer func() {
205 if v := recover(); v != nil {
206 err = fmt.Errorf("PANIC=%v", v)
207 }
208 }()
209
210 enc.AddString(key, stringer.(fmt.Stringer).String())
211 return
212}