Girish Gowdra | 6450343 | 2020-01-07 10:59:10 +0530 | [diff] [blame] | 1 | // 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 | |
| 21 | package zapcore |
| 22 | |
| 23 | import ( |
| 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. |
| 33 | type FieldType uint8 |
| 34 | |
| 35 | const ( |
| 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 |
Girish Gowdra | 390f12f | 2021-07-01 15:53:49 -0700 | [diff] [blame^] | 68 | // TimeType indicates that the field carries a time.Time that is |
| 69 | // representable by a UnixNano() stored as an int64. |
Girish Gowdra | 6450343 | 2020-01-07 10:59:10 +0530 | [diff] [blame] | 70 | TimeType |
Girish Gowdra | 390f12f | 2021-07-01 15:53:49 -0700 | [diff] [blame^] | 71 | // TimeFullType indicates that the field carries a time.Time stored as-is. |
| 72 | TimeFullType |
Girish Gowdra | 6450343 | 2020-01-07 10:59:10 +0530 | [diff] [blame] | 73 | // 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 |
Girish Gowdra | 390f12f | 2021-07-01 15:53:49 -0700 | [diff] [blame^] | 95 | |
| 96 | // InlineMarshalerType indicates that the field carries an ObjectMarshaler |
| 97 | // that should be inlined. |
| 98 | InlineMarshalerType |
Girish Gowdra | 6450343 | 2020-01-07 10:59:10 +0530 | [diff] [blame] | 99 | ) |
| 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. |
| 104 | type 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. |
| 114 | func (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)) |
Girish Gowdra | 390f12f | 2021-07-01 15:53:49 -0700 | [diff] [blame^] | 122 | case InlineMarshalerType: |
| 123 | err = f.Interface.(ObjectMarshaler).MarshalLogObject(enc) |
Girish Gowdra | 6450343 | 2020-01-07 10:59:10 +0530 | [diff] [blame] | 124 | 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 | } |
Girish Gowdra | 390f12f | 2021-07-01 15:53:49 -0700 | [diff] [blame^] | 157 | case TimeFullType: |
| 158 | enc.AddTime(f.Key, f.Interface.(time.Time)) |
Girish Gowdra | 6450343 | 2020-01-07 10:59:10 +0530 | [diff] [blame] | 159 | 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: |
Girish Gowdra | 390f12f | 2021-07-01 15:53:49 -0700 | [diff] [blame^] | 176 | err = encodeError(f.Key, f.Interface.(error), enc) |
Girish Gowdra | 6450343 | 2020-01-07 10:59:10 +0530 | [diff] [blame] | 177 | 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. |
| 190 | func (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 | |
| 208 | func addFields(enc ObjectEncoder, fields []Field) { |
| 209 | for i := range fields { |
| 210 | fields[i].AddTo(enc) |
| 211 | } |
| 212 | } |
| 213 | |
Girish Gowdra | 390f12f | 2021-07-01 15:53:49 -0700 | [diff] [blame^] | 214 | func 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 |
Girish Gowdra | 6450343 | 2020-01-07 10:59:10 +0530 | [diff] [blame] | 217 | defer func() { |
Girish Gowdra | 390f12f | 2021-07-01 15:53:49 -0700 | [diff] [blame^] | 218 | 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) |
Girish Gowdra | 6450343 | 2020-01-07 10:59:10 +0530 | [diff] [blame] | 228 | } |
| 229 | }() |
| 230 | |
| 231 | enc.AddString(key, stringer.(fmt.Stringer).String()) |
Girish Gowdra | 390f12f | 2021-07-01 15:53:49 -0700 | [diff] [blame^] | 232 | return nil |
Girish Gowdra | 6450343 | 2020-01-07 10:59:10 +0530 | [diff] [blame] | 233 | } |