github.com/Laisky/zap@v1.27.0/zapcore/field_test.go (about) 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_test 22 23 import ( 24 "errors" 25 "fmt" 26 "math" 27 "net/url" 28 "testing" 29 "time" 30 31 "github.com/Laisky/zap" 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/require" 34 //revive:disable:dot-imports 35 . "github.com/Laisky/zap/zapcore" 36 ) 37 38 type users int 39 40 func (u users) String() string { 41 return fmt.Sprintf("%d users", int(u)) 42 } 43 44 func (u users) MarshalLogObject(enc ObjectEncoder) error { 45 if int(u) < 0 { 46 return errors.New("too few users") 47 } 48 enc.AddInt("users", int(u)) 49 return nil 50 } 51 52 func (u users) MarshalLogArray(enc ArrayEncoder) error { 53 if int(u) < 0 { 54 return errors.New("too few users") 55 } 56 for i := 0; i < int(u); i++ { 57 enc.AppendString("user") 58 } 59 return nil 60 } 61 62 type obj struct { 63 kind int 64 } 65 66 func (o *obj) String() string { 67 if o == nil { 68 return "nil obj" 69 } 70 71 if o.kind == 1 { 72 panic("panic with string") 73 } else if o.kind == 2 { 74 panic(errors.New("panic with error")) 75 } else if o.kind == 3 { 76 // panic with an arbitrary object that causes a panic itself 77 // when being converted to a string 78 panic((*url.URL)(nil)) 79 } 80 81 return "obj" 82 } 83 84 type errObj struct { 85 kind int 86 errMsg string 87 } 88 89 func (eobj *errObj) Error() string { 90 if eobj.kind == 1 { 91 panic("panic in Error() method") 92 } 93 return eobj.errMsg 94 } 95 96 func TestUnknownFieldType(t *testing.T) { 97 unknown := Field{Key: "k", String: "foo"} 98 assert.Equal(t, UnknownType, unknown.Type, "Expected zero value of FieldType to be UnknownType.") 99 assert.Panics(t, func() { 100 unknown.AddTo(NewMapObjectEncoder()) 101 }, "Expected using a field with unknown type to panic.") 102 } 103 104 func TestFieldAddingError(t *testing.T) { 105 var empty interface{} 106 tests := []struct { 107 t FieldType 108 iface interface{} 109 want interface{} 110 err string 111 }{ 112 {t: ArrayMarshalerType, iface: users(-1), want: []interface{}{}, err: "too few users"}, 113 {t: ObjectMarshalerType, iface: users(-1), want: map[string]interface{}{}, err: "too few users"}, 114 {t: InlineMarshalerType, iface: users(-1), want: nil, err: "too few users"}, 115 {t: StringerType, iface: obj{}, want: empty, err: "PANIC=interface conversion: zapcore_test.obj is not fmt.Stringer: missing method String"}, 116 {t: StringerType, iface: &obj{1}, want: empty, err: "PANIC=panic with string"}, 117 {t: StringerType, iface: &obj{2}, want: empty, err: "PANIC=panic with error"}, 118 {t: StringerType, iface: &obj{3}, want: empty, err: "PANIC=<nil>"}, 119 {t: ErrorType, iface: &errObj{kind: 1}, want: empty, err: "PANIC=panic in Error() method"}, 120 } 121 for _, tt := range tests { 122 f := Field{Key: "k", Interface: tt.iface, Type: tt.t} 123 enc := NewMapObjectEncoder() 124 assert.NotPanics(t, func() { f.AddTo(enc) }, "Unexpected panic when adding fields returns an error.") 125 assert.Equal(t, tt.want, enc.Fields["k"], "On error, expected zero value in field.Key.") 126 assert.Equal(t, tt.err, enc.Fields["kError"], "Expected error message in log context.") 127 } 128 } 129 130 func TestFields(t *testing.T) { 131 tests := []struct { 132 t FieldType 133 i int64 134 s string 135 iface interface{} 136 want interface{} 137 }{ 138 {t: ArrayMarshalerType, iface: users(2), want: []interface{}{"user", "user"}}, 139 {t: ObjectMarshalerType, iface: users(2), want: map[string]interface{}{"users": 2}}, 140 {t: BoolType, i: 0, want: false}, 141 {t: ByteStringType, iface: []byte("foo"), want: "foo"}, 142 {t: Complex128Type, iface: 1 + 2i, want: 1 + 2i}, 143 {t: Complex64Type, iface: complex64(1 + 2i), want: complex64(1 + 2i)}, 144 {t: DurationType, i: 1000, want: time.Microsecond}, 145 {t: Float64Type, i: int64(math.Float64bits(3.14)), want: 3.14}, 146 {t: Float32Type, i: int64(math.Float32bits(3.14)), want: float32(3.14)}, 147 {t: Int64Type, i: 42, want: int64(42)}, 148 {t: Int32Type, i: 42, want: int32(42)}, 149 {t: Int16Type, i: 42, want: int16(42)}, 150 {t: Int8Type, i: 42, want: int8(42)}, 151 {t: StringType, s: "foo", want: "foo"}, 152 {t: TimeType, i: 1000, iface: time.UTC, want: time.Unix(0, 1000).In(time.UTC)}, 153 {t: TimeType, i: 1000, want: time.Unix(0, 1000)}, 154 {t: Uint64Type, i: 42, want: uint64(42)}, 155 {t: Uint32Type, i: 42, want: uint32(42)}, 156 {t: Uint16Type, i: 42, want: uint16(42)}, 157 {t: Uint8Type, i: 42, want: uint8(42)}, 158 {t: UintptrType, i: 42, want: uintptr(42)}, 159 {t: ReflectType, iface: users(2), want: users(2)}, 160 {t: NamespaceType, want: map[string]interface{}{}}, 161 {t: StringerType, iface: users(2), want: "2 users"}, 162 {t: StringerType, iface: &obj{}, want: "obj"}, 163 {t: StringerType, iface: (*obj)(nil), want: "nil obj"}, 164 {t: SkipType, want: interface{}(nil)}, 165 {t: StringerType, iface: (*url.URL)(nil), want: "<nil>"}, 166 {t: StringerType, iface: (*users)(nil), want: "<nil>"}, 167 {t: ErrorType, iface: (*errObj)(nil), want: "<nil>"}, 168 } 169 170 for _, tt := range tests { 171 enc := NewMapObjectEncoder() 172 f := Field{Key: "k", Type: tt.t, Integer: tt.i, Interface: tt.iface, String: tt.s} 173 f.AddTo(enc) 174 assert.Equal(t, tt.want, enc.Fields["k"], "Unexpected output from field %+v.", f) 175 176 delete(enc.Fields, "k") 177 assert.Equal(t, 0, len(enc.Fields), "Unexpected extra fields present.") 178 179 assert.True(t, f.Equals(f), "Field does not equal itself") 180 } 181 } 182 183 func TestInlineMarshaler(t *testing.T) { 184 enc := NewMapObjectEncoder() 185 186 topLevelStr := Field{Key: "k", Type: StringType, String: "s"} 187 topLevelStr.AddTo(enc) 188 189 inlineObj := Field{Key: "ignored", Type: InlineMarshalerType, Interface: users(10)} 190 inlineObj.AddTo(enc) 191 192 nestedObj := Field{Key: "nested", Type: ObjectMarshalerType, Interface: users(11)} 193 nestedObj.AddTo(enc) 194 195 assert.Equal(t, map[string]interface{}{ 196 "k": "s", 197 "users": 10, 198 "nested": map[string]interface{}{ 199 "users": 11, 200 }, 201 }, enc.Fields) 202 } 203 204 func TestEquals(t *testing.T) { 205 // Values outside the UnixNano range were encoded incorrectly (#737, #803). 206 timeOutOfRangeHigh := time.Unix(0, math.MaxInt64).Add(time.Nanosecond) 207 timeOutOfRangeLow := time.Unix(0, math.MinInt64).Add(-time.Nanosecond) 208 timeOutOfRangeHighNano := time.Unix(0, timeOutOfRangeHigh.UnixNano()) 209 timeOutOfRangeLowNano := time.Unix(0, timeOutOfRangeLow.UnixNano()) 210 require.False(t, timeOutOfRangeHigh.Equal(timeOutOfRangeHighNano), "should be different as value is > UnixNano range") 211 require.False(t, timeOutOfRangeHigh.Equal(timeOutOfRangeHighNano), "should be different as value is < UnixNano range") 212 213 tests := []struct { 214 a, b Field 215 want bool 216 }{ 217 { 218 a: zap.Int16("a", 1), 219 b: zap.Int32("a", 1), 220 want: false, 221 }, 222 { 223 a: zap.String("k", "a"), 224 b: zap.String("k", "a"), 225 want: true, 226 }, 227 { 228 a: zap.String("k", "a"), 229 b: zap.String("k2", "a"), 230 want: false, 231 }, 232 { 233 a: zap.String("k", "a"), 234 b: zap.String("k", "b"), 235 want: false, 236 }, 237 { 238 a: zap.Time("k", time.Unix(1000, 1000)), 239 b: zap.Time("k", time.Unix(1000, 1000)), 240 want: true, 241 }, 242 { 243 a: zap.Time("k", time.Unix(1000, 1000).In(time.UTC)), 244 b: zap.Time("k", time.Unix(1000, 1000).In(time.FixedZone("TEST", -8))), 245 want: false, 246 }, 247 { 248 a: zap.Time("k", timeOutOfRangeLow), 249 b: zap.Time("k", timeOutOfRangeLowNano), 250 want: false, 251 }, 252 { 253 a: zap.Time("k", timeOutOfRangeHigh), 254 b: zap.Time("k", timeOutOfRangeHighNano), 255 want: false, 256 }, 257 { 258 a: zap.Time("k", time.Unix(1000, 1000)), 259 b: zap.Time("k", time.Unix(1000, 2000)), 260 want: false, 261 }, 262 { 263 a: zap.Binary("k", []byte{1, 2}), 264 b: zap.Binary("k", []byte{1, 2}), 265 want: true, 266 }, 267 { 268 a: zap.Binary("k", []byte{1, 2}), 269 b: zap.Binary("k", []byte{1, 3}), 270 want: false, 271 }, 272 { 273 a: zap.ByteString("k", []byte("abc")), 274 b: zap.ByteString("k", []byte("abc")), 275 want: true, 276 }, 277 { 278 a: zap.ByteString("k", []byte("abc")), 279 b: zap.ByteString("k", []byte("abd")), 280 want: false, 281 }, 282 { 283 a: zap.Ints("k", []int{1, 2}), 284 b: zap.Ints("k", []int{1, 2}), 285 want: true, 286 }, 287 { 288 a: zap.Ints("k", []int{1, 2}), 289 b: zap.Ints("k", []int{1, 3}), 290 want: false, 291 }, 292 { 293 a: zap.Object("k", users(10)), 294 b: zap.Object("k", users(10)), 295 want: true, 296 }, 297 { 298 a: zap.Object("k", users(10)), 299 b: zap.Object("k", users(20)), 300 want: false, 301 }, 302 { 303 a: zap.Any("k", map[string]string{"a": "b"}), 304 b: zap.Any("k", map[string]string{"a": "b"}), 305 want: true, 306 }, 307 { 308 a: zap.Any("k", map[string]string{"a": "b"}), 309 b: zap.Any("k", map[string]string{"a": "d"}), 310 want: false, 311 }, 312 { 313 a: zap.Dict("k", zap.String("a", "b")), 314 b: zap.Dict("k", zap.String("a", "b")), 315 want: true, 316 }, 317 { 318 a: zap.Dict("k", zap.String("a", "b")), 319 b: zap.Dict("k", zap.String("a", "d")), 320 want: false, 321 }, 322 } 323 324 for _, tt := range tests { 325 assert.Equal(t, tt.want, tt.a.Equals(tt.b), "a.Equals(b) a: %#v b: %#v", tt.a, tt.b) 326 assert.Equal(t, tt.want, tt.b.Equals(tt.a), "b.Equals(a) a: %#v b: %#v", tt.a, tt.b) 327 } 328 }