1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package zap
22
23 import (
24 "math"
25 "net"
26 "regexp"
27 "sync"
28 "testing"
29 "time"
30
31 "github.com/stretchr/testify/assert"
32 "go.uber.org/zap/internal/stacktrace"
33 "go.uber.org/zap/zapcore"
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
50
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
65 addr := net.ParseIP("1.2.3.4")
66 name := username("phil")
67 ints := []int{5, 6}
68
69
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 }
317
View as plain text