github.com/Laisky/zap@v1.27.0/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 zap 22 23 import ( 24 "math" 25 "net" 26 "regexp" 27 "sync" 28 "testing" 29 "time" 30 31 "github.com/Laisky/zap/internal/stacktrace" 32 "github.com/Laisky/zap/zapcore" 33 "github.com/stretchr/testify/assert" 34 ) 35 36 type username string 37 38 func (n username) MarshalLogObject(enc zapcore.ObjectEncoder) error { 39 enc.AddString("username", string(n)) 40 return nil 41 } 42 43 func assertCanBeReused(t testing.TB, field Field) { 44 var wg sync.WaitGroup 45 46 for i := 0; i < 100; i++ { 47 enc := zapcore.NewMapObjectEncoder() 48 49 // Ensure using the field in multiple encoders in separate goroutines 50 // does not cause any races or panics. 51 wg.Add(1) 52 go func() { 53 defer wg.Done() 54 assert.NotPanics(t, func() { 55 field.AddTo(enc) 56 }, "Reusing a field should not cause issues") 57 }() 58 } 59 60 wg.Wait() 61 } 62 63 func TestFieldConstructors(t *testing.T) { 64 // Interface types. 65 addr := net.ParseIP("1.2.3.4") 66 name := username("phil") 67 ints := []int{5, 6} 68 69 // Helpful values for use in constructing pointers to primitives below. 70 var ( 71 boolVal = bool(true) 72 complex128Val = complex128(complex(0, 0)) 73 complex64Val = complex64(complex(0, 0)) 74 durationVal = time.Duration(time.Second) 75 float64Val = float64(1.0) 76 float32Val = float32(1.0) 77 intVal = int(1) 78 int64Val = int64(1) 79 int32Val = int32(1) 80 int16Val = int16(1) 81 int8Val = int8(1) 82 stringVal = string("hello") 83 timeVal = time.Unix(100000, 0) 84 uintVal = uint(1) 85 uint64Val = uint64(1) 86 uint32Val = uint32(1) 87 uint16Val = uint16(1) 88 uint8Val = uint8(1) 89 uintptrVal = uintptr(1) 90 nilErr error 91 ) 92 93 tests := []struct { 94 name string 95 field Field 96 expect Field 97 }{ 98 {"Skip", Field{Type: zapcore.SkipType}, Skip()}, 99 {"Binary", Field{Key: "k", Type: zapcore.BinaryType, Interface: []byte("ab12")}, Binary("k", []byte("ab12"))}, 100 {"Bool", Field{Key: "k", Type: zapcore.BoolType, Integer: 1}, Bool("k", true)}, 101 {"Bool", Field{Key: "k", Type: zapcore.BoolType, Integer: 1}, Bool("k", true)}, 102 {"ByteString", Field{Key: "k", Type: zapcore.ByteStringType, Interface: []byte("ab12")}, ByteString("k", []byte("ab12"))}, 103 {"Complex128", Field{Key: "k", Type: zapcore.Complex128Type, Interface: 1 + 2i}, Complex128("k", 1+2i)}, 104 {"Complex64", Field{Key: "k", Type: zapcore.Complex64Type, Interface: complex64(1 + 2i)}, Complex64("k", 1+2i)}, 105 {"Duration", Field{Key: "k", Type: zapcore.DurationType, Integer: 1}, Duration("k", 1)}, 106 {"Int", Field{Key: "k", Type: zapcore.Int64Type, Integer: 1}, Int("k", 1)}, 107 {"Int64", Field{Key: "k", Type: zapcore.Int64Type, Integer: 1}, Int64("k", 1)}, 108 {"Int32", Field{Key: "k", Type: zapcore.Int32Type, Integer: 1}, Int32("k", 1)}, 109 {"Int16", Field{Key: "k", Type: zapcore.Int16Type, Integer: 1}, Int16("k", 1)}, 110 {"Int8", Field{Key: "k", Type: zapcore.Int8Type, Integer: 1}, Int8("k", 1)}, 111 {"String", Field{Key: "k", Type: zapcore.StringType, String: "foo"}, String("k", "foo")}, 112 {"Time", Field{Key: "k", Type: zapcore.TimeType, Integer: 0, Interface: time.UTC}, Time("k", time.Unix(0, 0).In(time.UTC))}, 113 {"Time", Field{Key: "k", Type: zapcore.TimeType, Integer: 1000, Interface: time.UTC}, Time("k", time.Unix(0, 1000).In(time.UTC))}, 114 {"Time", Field{Key: "k", Type: zapcore.TimeType, Integer: math.MinInt64, Interface: time.UTC}, Time("k", time.Unix(0, math.MinInt64).In(time.UTC))}, 115 {"Time", Field{Key: "k", Type: zapcore.TimeType, Integer: math.MaxInt64, Interface: time.UTC}, Time("k", time.Unix(0, math.MaxInt64).In(time.UTC))}, 116 {"Time", Field{Key: "k", Type: zapcore.TimeFullType, Interface: time.Time{}}, Time("k", time.Time{})}, 117 {"Time", Field{Key: "k", Type: zapcore.TimeFullType, Interface: time.Unix(math.MaxInt64, 0)}, Time("k", time.Unix(math.MaxInt64, 0))}, 118 {"Uint", Field{Key: "k", Type: zapcore.Uint64Type, Integer: 1}, Uint("k", 1)}, 119 {"Uint64", Field{Key: "k", Type: zapcore.Uint64Type, Integer: 1}, Uint64("k", 1)}, 120 {"Uint32", Field{Key: "k", Type: zapcore.Uint32Type, Integer: 1}, Uint32("k", 1)}, 121 {"Uint16", Field{Key: "k", Type: zapcore.Uint16Type, Integer: 1}, Uint16("k", 1)}, 122 {"Uint8", Field{Key: "k", Type: zapcore.Uint8Type, Integer: 1}, Uint8("k", 1)}, 123 {"Uintptr", Field{Key: "k", Type: zapcore.UintptrType, Integer: 10}, Uintptr("k", 0xa)}, 124 {"Reflect", Field{Key: "k", Type: zapcore.ReflectType, Interface: ints}, Reflect("k", ints)}, 125 {"Reflect", Field{Key: "k", Type: zapcore.ReflectType}, Reflect("k", nil)}, 126 {"Stringer", Field{Key: "k", Type: zapcore.StringerType, Interface: addr}, Stringer("k", addr)}, 127 {"Object", Field{Key: "k", Type: zapcore.ObjectMarshalerType, Interface: name}, Object("k", name)}, 128 {"Inline", Field{Type: zapcore.InlineMarshalerType, Interface: name}, Inline(name)}, 129 {"Any:ObjectMarshaler", Any("k", name), Object("k", name)}, 130 {"Any:ArrayMarshaler", Any("k", bools([]bool{true})), Array("k", bools([]bool{true}))}, 131 {"Any:Dict", Any("k", []Field{String("k", "v")}), Dict("k", String("k", "v"))}, 132 {"Any:Stringer", Any("k", addr), Stringer("k", addr)}, 133 {"Any:Bool", Any("k", true), Bool("k", true)}, 134 {"Any:Bools", Any("k", []bool{true}), Bools("k", []bool{true})}, 135 {"Any:Byte", Any("k", byte(1)), Uint8("k", 1)}, 136 {"Any:Bytes", Any("k", []byte{1}), Binary("k", []byte{1})}, 137 {"Any:Complex128", Any("k", 1+2i), Complex128("k", 1+2i)}, 138 {"Any:Complex128s", Any("k", []complex128{1 + 2i}), Complex128s("k", []complex128{1 + 2i})}, 139 {"Any:Complex64", Any("k", complex64(1+2i)), Complex64("k", 1+2i)}, 140 {"Any:Complex64s", Any("k", []complex64{1 + 2i}), Complex64s("k", []complex64{1 + 2i})}, 141 {"Any:Float64", Any("k", 3.14), Float64("k", 3.14)}, 142 {"Any:Float64s", Any("k", []float64{3.14}), Float64s("k", []float64{3.14})}, 143 {"Any:Float32", Any("k", float32(3.14)), Float32("k", 3.14)}, 144 {"Any:Float32s", Any("k", []float32{3.14}), Float32s("k", []float32{3.14})}, 145 {"Any:Int", Any("k", 1), Int("k", 1)}, 146 {"Any:Ints", Any("k", []int{1}), Ints("k", []int{1})}, 147 {"Any:Int64", Any("k", int64(1)), Int64("k", 1)}, 148 {"Any:Int64s", Any("k", []int64{1}), Int64s("k", []int64{1})}, 149 {"Any:Int32", Any("k", int32(1)), Int32("k", 1)}, 150 {"Any:Int32s", Any("k", []int32{1}), Int32s("k", []int32{1})}, 151 {"Any:Int16", Any("k", int16(1)), Int16("k", 1)}, 152 {"Any:Int16s", Any("k", []int16{1}), Int16s("k", []int16{1})}, 153 {"Any:Int8", Any("k", int8(1)), Int8("k", 1)}, 154 {"Any:Int8s", Any("k", []int8{1}), Int8s("k", []int8{1})}, 155 {"Any:Rune", Any("k", rune(1)), Int32("k", 1)}, 156 {"Any:Runes", Any("k", []rune{1}), Int32s("k", []int32{1})}, 157 {"Any:String", Any("k", "v"), String("k", "v")}, 158 {"Any:Strings", Any("k", []string{"v"}), Strings("k", []string{"v"})}, 159 {"Any:Uint", Any("k", uint(1)), Uint("k", 1)}, 160 {"Any:Uints", Any("k", []uint{1}), Uints("k", []uint{1})}, 161 {"Any:Uint64", Any("k", uint64(1)), Uint64("k", 1)}, 162 {"Any:Uint64s", Any("k", []uint64{1}), Uint64s("k", []uint64{1})}, 163 {"Any:Uint32", Any("k", uint32(1)), Uint32("k", 1)}, 164 {"Any:Uint32s", Any("k", []uint32{1}), Uint32s("k", []uint32{1})}, 165 {"Any:Uint16", Any("k", uint16(1)), Uint16("k", 1)}, 166 {"Any:Uint16s", Any("k", []uint16{1}), Uint16s("k", []uint16{1})}, 167 {"Any:Uint8", Any("k", uint8(1)), Uint8("k", 1)}, 168 {"Any:Uint8s", Any("k", []uint8{1}), Binary("k", []uint8{1})}, 169 {"Any:Uintptr", Any("k", uintptr(1)), Uintptr("k", 1)}, 170 {"Any:Uintptrs", Any("k", []uintptr{1}), Uintptrs("k", []uintptr{1})}, 171 {"Any:Time", Any("k", time.Unix(0, 0)), Time("k", time.Unix(0, 0))}, 172 {"Any:TimeFullType", Any("k", time.Time{}), Time("k", time.Time{})}, 173 {"Any:Times", Any("k", []time.Time{time.Unix(0, 0)}), Times("k", []time.Time{time.Unix(0, 0)})}, 174 {"Any:Duration", Any("k", time.Second), Duration("k", time.Second)}, 175 {"Any:Durations", Any("k", []time.Duration{time.Second}), Durations("k", []time.Duration{time.Second})}, 176 {"Any:Fallback", Any("k", struct{}{}), Reflect("k", struct{}{})}, 177 {"Ptr:Bool", Boolp("k", nil), nilField("k")}, 178 {"Ptr:Bool", Boolp("k", &boolVal), Bool("k", boolVal)}, 179 {"Any:PtrBool", Any("k", (*bool)(nil)), nilField("k")}, 180 {"Any:PtrBool", Any("k", &boolVal), Bool("k", boolVal)}, 181 {"Ptr:Complex128", Complex128p("k", nil), nilField("k")}, 182 {"Ptr:Complex128", Complex128p("k", &complex128Val), Complex128("k", complex128Val)}, 183 {"Any:PtrComplex128", Any("k", (*complex128)(nil)), nilField("k")}, 184 {"Any:PtrComplex128", Any("k", &complex128Val), Complex128("k", complex128Val)}, 185 {"Ptr:Complex64", Complex64p("k", nil), nilField("k")}, 186 {"Ptr:Complex64", Complex64p("k", &complex64Val), Complex64("k", complex64Val)}, 187 {"Any:PtrComplex64", Any("k", (*complex64)(nil)), nilField("k")}, 188 {"Any:PtrComplex64", Any("k", &complex64Val), Complex64("k", complex64Val)}, 189 {"Ptr:Duration", Durationp("k", nil), nilField("k")}, 190 {"Ptr:Duration", Durationp("k", &durationVal), Duration("k", durationVal)}, 191 {"Any:PtrDuration", Any("k", (*time.Duration)(nil)), nilField("k")}, 192 {"Any:PtrDuration", Any("k", &durationVal), Duration("k", durationVal)}, 193 {"Ptr:Float64", Float64p("k", nil), nilField("k")}, 194 {"Ptr:Float64", Float64p("k", &float64Val), Float64("k", float64Val)}, 195 {"Any:PtrFloat64", Any("k", (*float64)(nil)), nilField("k")}, 196 {"Any:PtrFloat64", Any("k", &float64Val), Float64("k", float64Val)}, 197 {"Ptr:Float32", Float32p("k", nil), nilField("k")}, 198 {"Ptr:Float32", Float32p("k", &float32Val), Float32("k", float32Val)}, 199 {"Any:PtrFloat32", Any("k", (*float32)(nil)), nilField("k")}, 200 {"Any:PtrFloat32", Any("k", &float32Val), Float32("k", float32Val)}, 201 {"Ptr:Int", Intp("k", nil), nilField("k")}, 202 {"Ptr:Int", Intp("k", &intVal), Int("k", intVal)}, 203 {"Any:PtrInt", Any("k", (*int)(nil)), nilField("k")}, 204 {"Any:PtrInt", Any("k", &intVal), Int("k", intVal)}, 205 {"Ptr:Int64", Int64p("k", nil), nilField("k")}, 206 {"Ptr:Int64", Int64p("k", &int64Val), Int64("k", int64Val)}, 207 {"Any:PtrInt64", Any("k", (*int64)(nil)), nilField("k")}, 208 {"Any:PtrInt64", Any("k", &int64Val), Int64("k", int64Val)}, 209 {"Ptr:Int32", Int32p("k", nil), nilField("k")}, 210 {"Ptr:Int32", Int32p("k", &int32Val), Int32("k", int32Val)}, 211 {"Any:PtrInt32", Any("k", (*int32)(nil)), nilField("k")}, 212 {"Any:PtrInt32", Any("k", &int32Val), Int32("k", int32Val)}, 213 {"Ptr:Int16", Int16p("k", nil), nilField("k")}, 214 {"Ptr:Int16", Int16p("k", &int16Val), Int16("k", int16Val)}, 215 {"Any:PtrInt16", Any("k", (*int16)(nil)), nilField("k")}, 216 {"Any:PtrInt16", Any("k", &int16Val), Int16("k", int16Val)}, 217 {"Ptr:Int8", Int8p("k", nil), nilField("k")}, 218 {"Ptr:Int8", Int8p("k", &int8Val), Int8("k", int8Val)}, 219 {"Any:PtrInt8", Any("k", (*int8)(nil)), nilField("k")}, 220 {"Any:PtrInt8", Any("k", &int8Val), Int8("k", int8Val)}, 221 {"Ptr:String", Stringp("k", nil), nilField("k")}, 222 {"Ptr:String", Stringp("k", &stringVal), String("k", stringVal)}, 223 {"Any:PtrString", Any("k", (*string)(nil)), nilField("k")}, 224 {"Any:PtrString", Any("k", &stringVal), String("k", stringVal)}, 225 {"Ptr:Time", Timep("k", nil), nilField("k")}, 226 {"Ptr:Time", Timep("k", &timeVal), Time("k", timeVal)}, 227 {"Any:PtrTime", Any("k", (*time.Time)(nil)), nilField("k")}, 228 {"Any:PtrTime", Any("k", &timeVal), Time("k", timeVal)}, 229 {"Any:PtrTimeFullType", Any("k", &time.Time{}), Time("k", time.Time{})}, 230 {"Ptr:Uint", Uintp("k", nil), nilField("k")}, 231 {"Ptr:Uint", Uintp("k", &uintVal), Uint("k", uintVal)}, 232 {"Any:PtrUint", Any("k", (*uint)(nil)), nilField("k")}, 233 {"Any:PtrUint", Any("k", &uintVal), Uint("k", uintVal)}, 234 {"Ptr:Uint64", Uint64p("k", nil), nilField("k")}, 235 {"Ptr:Uint64", Uint64p("k", &uint64Val), Uint64("k", uint64Val)}, 236 {"Any:PtrUint64", Any("k", (*uint64)(nil)), nilField("k")}, 237 {"Any:PtrUint64", Any("k", &uint64Val), Uint64("k", uint64Val)}, 238 {"Ptr:Uint32", Uint32p("k", nil), nilField("k")}, 239 {"Ptr:Uint32", Uint32p("k", &uint32Val), Uint32("k", uint32Val)}, 240 {"Any:PtrUint32", Any("k", (*uint32)(nil)), nilField("k")}, 241 {"Any:PtrUint32", Any("k", &uint32Val), Uint32("k", uint32Val)}, 242 {"Ptr:Uint16", Uint16p("k", nil), nilField("k")}, 243 {"Ptr:Uint16", Uint16p("k", &uint16Val), Uint16("k", uint16Val)}, 244 {"Any:PtrUint16", Any("k", (*uint16)(nil)), nilField("k")}, 245 {"Any:PtrUint16", Any("k", &uint16Val), Uint16("k", uint16Val)}, 246 {"Ptr:Uint8", Uint8p("k", nil), nilField("k")}, 247 {"Ptr:Uint8", Uint8p("k", &uint8Val), Uint8("k", uint8Val)}, 248 {"Any:PtrUint8", Any("k", (*uint8)(nil)), nilField("k")}, 249 {"Any:PtrUint8", Any("k", &uint8Val), Uint8("k", uint8Val)}, 250 {"Ptr:Uintptr", Uintptrp("k", nil), nilField("k")}, 251 {"Ptr:Uintptr", Uintptrp("k", &uintptrVal), Uintptr("k", uintptrVal)}, 252 {"Any:PtrUintptr", Any("k", (*uintptr)(nil)), nilField("k")}, 253 {"Any:PtrUintptr", Any("k", &uintptrVal), Uintptr("k", uintptrVal)}, 254 {"Any:ErrorNil", Any("k", nilErr), nilField("k")}, 255 {"Namespace", Namespace("k"), Field{Key: "k", Type: zapcore.NamespaceType}}, 256 } 257 258 for _, tt := range tests { 259 t.Run(tt.name, func(t *testing.T) { 260 if !assert.Equal(t, tt.expect, tt.field, "Unexpected output from convenience field constructor") { 261 t.Logf("type expected: %T\nGot: %T", tt.expect.Interface, tt.field.Interface) 262 } 263 assertCanBeReused(t, tt.field) 264 }) 265 } 266 } 267 268 func TestStackField(t *testing.T) { 269 f := Stack("stacktrace") 270 assert.Equal(t, "stacktrace", f.Key, "Unexpected field key.") 271 assert.Equal(t, zapcore.StringType, f.Type, "Unexpected field type.") 272 r := regexp.MustCompile(`field_test.go:(\d+)`) 273 assert.Equal(t, r.ReplaceAllString(stacktrace.Take(0), "field_test.go"), r.ReplaceAllString(f.String, "field_test.go"), "Unexpected stack trace") 274 assertCanBeReused(t, f) 275 } 276 277 func TestStackSkipField(t *testing.T) { 278 f := StackSkip("stacktrace", 0) 279 assert.Equal(t, "stacktrace", f.Key, "Unexpected field key.") 280 assert.Equal(t, zapcore.StringType, f.Type, "Unexpected field type.") 281 r := regexp.MustCompile(`field_test.go:(\d+)`) 282 assert.Equal(t, r.ReplaceAllString(stacktrace.Take(0), "field_test.go"), r.ReplaceAllString(f.String, "field_test.go"), f.String, "Unexpected stack trace") 283 assertCanBeReused(t, f) 284 } 285 286 func TestStackSkipFieldWithSkip(t *testing.T) { 287 f := StackSkip("stacktrace", 1) 288 assert.Equal(t, "stacktrace", f.Key, "Unexpected field key.") 289 assert.Equal(t, zapcore.StringType, f.Type, "Unexpected field type.") 290 assert.Equal(t, stacktrace.Take(1), f.String, "Unexpected stack trace") 291 assertCanBeReused(t, f) 292 } 293 294 func TestDict(t *testing.T) { 295 tests := []struct { 296 desc string 297 field Field 298 expected any 299 }{ 300 {"empty", Dict(""), map[string]any{}}, 301 {"single", Dict("", String("k", "v")), map[string]any{"k": "v"}}, 302 {"multiple", Dict("", String("k", "v"), String("k2", "v2")), map[string]any{"k": "v", "k2": "v2"}}, 303 } 304 305 for _, tt := range tests { 306 t.Run(tt.desc, func(t *testing.T) { 307 enc := zapcore.NewMapObjectEncoder() 308 tt.field.Key = "k" 309 tt.field.AddTo(enc) 310 assert.Equal(t, tt.expected, enc.Fields["k"], "unexpected map contents") 311 assert.Len(t, enc.Fields, 1, "found extra keys in map: %v", enc.Fields) 312 313 assertCanBeReused(t, tt.field) 314 }) 315 } 316 }