1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package zapcore_test
22
23 import (
24 "encoding/json"
25 "strings"
26 "testing"
27 "time"
28
29 "github.com/stretchr/testify/assert"
30 "github.com/stretchr/testify/require"
31 "gopkg.in/yaml.v3"
32
33
34 . "go.uber.org/zap/zapcore"
35 )
36
37 var (
38 _epoch = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)
39 _testEntry = Entry{
40 LoggerName: "main",
41 Level: InfoLevel,
42 Message: `hello`,
43 Time: _epoch,
44 Stack: "fake-stack",
45 Caller: EntryCaller{Defined: true, File: "foo.go", Line: 42, Function: "foo.Foo"},
46 }
47 )
48
49 func testEncoderConfig() EncoderConfig {
50 return EncoderConfig{
51 MessageKey: "msg",
52 LevelKey: "level",
53 NameKey: "name",
54 TimeKey: "ts",
55 CallerKey: "caller",
56 FunctionKey: "func",
57 StacktraceKey: "stacktrace",
58 LineEnding: "\n",
59 EncodeTime: EpochTimeEncoder,
60 EncodeLevel: LowercaseLevelEncoder,
61 EncodeDuration: SecondsDurationEncoder,
62 EncodeCaller: ShortCallerEncoder,
63 }
64 }
65
66 func humanEncoderConfig() EncoderConfig {
67 cfg := testEncoderConfig()
68 cfg.EncodeTime = ISO8601TimeEncoder
69 cfg.EncodeLevel = CapitalLevelEncoder
70 cfg.EncodeDuration = StringDurationEncoder
71 return cfg
72 }
73
74 func capitalNameEncoder(loggerName string, enc PrimitiveArrayEncoder) {
75 enc.AppendString(strings.ToUpper(loggerName))
76 }
77
78 func TestEncoderConfiguration(t *testing.T) {
79 base := testEncoderConfig()
80
81 tests := []struct {
82 desc string
83 cfg EncoderConfig
84 amendEntry func(Entry) Entry
85 extra func(Encoder)
86 expectedJSON string
87 expectedConsole string
88 }{
89 {
90 desc: "messages to be escaped",
91 cfg: base,
92 amendEntry: func(ent Entry) Entry {
93 ent.Message = `hello\`
94 return ent
95 },
96 expectedJSON: `{"level":"info","ts":0,"name":"main","caller":"foo.go:42","func":"foo.Foo","msg":"hello\\","stacktrace":"fake-stack"}` + "\n",
97 expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\\\nfake-stack\n",
98 },
99 {
100 desc: "use custom entry keys in JSON output and ignore them in console output",
101 cfg: EncoderConfig{
102 LevelKey: "L",
103 TimeKey: "T",
104 MessageKey: "M",
105 NameKey: "N",
106 CallerKey: "C",
107 FunctionKey: "F",
108 StacktraceKey: "S",
109 LineEnding: base.LineEnding,
110 EncodeTime: base.EncodeTime,
111 EncodeDuration: base.EncodeDuration,
112 EncodeLevel: base.EncodeLevel,
113 EncodeCaller: base.EncodeCaller,
114 },
115 expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
116 expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
117 },
118 {
119 desc: "skip line ending if SkipLineEnding is 'true'",
120 cfg: EncoderConfig{
121 LevelKey: "L",
122 TimeKey: "T",
123 MessageKey: "M",
124 NameKey: "N",
125 CallerKey: "C",
126 FunctionKey: "F",
127 StacktraceKey: "S",
128 LineEnding: base.LineEnding,
129 SkipLineEnding: true,
130 EncodeTime: base.EncodeTime,
131 EncodeDuration: base.EncodeDuration,
132 EncodeLevel: base.EncodeLevel,
133 EncodeCaller: base.EncodeCaller,
134 },
135 expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}`,
136 expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack",
137 },
138 {
139 desc: "skip level if LevelKey is omitted",
140 cfg: EncoderConfig{
141 LevelKey: OmitKey,
142 TimeKey: "T",
143 MessageKey: "M",
144 NameKey: "N",
145 CallerKey: "C",
146 FunctionKey: "F",
147 StacktraceKey: "S",
148 LineEnding: base.LineEnding,
149 EncodeTime: base.EncodeTime,
150 EncodeDuration: base.EncodeDuration,
151 EncodeLevel: base.EncodeLevel,
152 EncodeCaller: base.EncodeCaller,
153 },
154 expectedJSON: `{"T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
155 expectedConsole: "0\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
156 },
157 {
158 desc: "skip timestamp if TimeKey is omitted",
159 cfg: EncoderConfig{
160 LevelKey: "L",
161 TimeKey: OmitKey,
162 MessageKey: "M",
163 NameKey: "N",
164 CallerKey: "C",
165 FunctionKey: "F",
166 StacktraceKey: "S",
167 LineEnding: base.LineEnding,
168 EncodeTime: base.EncodeTime,
169 EncodeDuration: base.EncodeDuration,
170 EncodeLevel: base.EncodeLevel,
171 EncodeCaller: base.EncodeCaller,
172 },
173 expectedJSON: `{"L":"info","N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
174 expectedConsole: "info\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
175 },
176 {
177 desc: "skip message if MessageKey is omitted",
178 cfg: EncoderConfig{
179 LevelKey: "L",
180 TimeKey: "T",
181 MessageKey: OmitKey,
182 NameKey: "N",
183 CallerKey: "C",
184 FunctionKey: "F",
185 StacktraceKey: "S",
186 LineEnding: base.LineEnding,
187 EncodeTime: base.EncodeTime,
188 EncodeDuration: base.EncodeDuration,
189 EncodeLevel: base.EncodeLevel,
190 EncodeCaller: base.EncodeCaller,
191 },
192 expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","S":"fake-stack"}` + "\n",
193 expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\nfake-stack\n",
194 },
195 {
196 desc: "skip name if NameKey is omitted",
197 cfg: EncoderConfig{
198 LevelKey: "L",
199 TimeKey: "T",
200 MessageKey: "M",
201 NameKey: OmitKey,
202 CallerKey: "C",
203 FunctionKey: "F",
204 StacktraceKey: "S",
205 LineEnding: base.LineEnding,
206 EncodeTime: base.EncodeTime,
207 EncodeDuration: base.EncodeDuration,
208 EncodeLevel: base.EncodeLevel,
209 EncodeCaller: base.EncodeCaller,
210 },
211 expectedJSON: `{"L":"info","T":0,"C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
212 expectedConsole: "0\tinfo\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
213 },
214 {
215 desc: "skip caller if CallerKey is omitted",
216 cfg: EncoderConfig{
217 LevelKey: "L",
218 TimeKey: "T",
219 MessageKey: "M",
220 NameKey: "N",
221 CallerKey: OmitKey,
222 FunctionKey: "F",
223 StacktraceKey: "S",
224 LineEnding: base.LineEnding,
225 EncodeTime: base.EncodeTime,
226 EncodeDuration: base.EncodeDuration,
227 EncodeLevel: base.EncodeLevel,
228 EncodeCaller: base.EncodeCaller,
229 },
230 expectedJSON: `{"L":"info","T":0,"N":"main","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
231 expectedConsole: "0\tinfo\tmain\tfoo.Foo\thello\nfake-stack\n",
232 },
233 {
234 desc: "skip function if FunctionKey is omitted",
235 cfg: EncoderConfig{
236 LevelKey: "L",
237 TimeKey: "T",
238 MessageKey: "M",
239 NameKey: "N",
240 CallerKey: "C",
241 FunctionKey: OmitKey,
242 StacktraceKey: "S",
243 LineEnding: base.LineEnding,
244 EncodeTime: base.EncodeTime,
245 EncodeDuration: base.EncodeDuration,
246 EncodeLevel: base.EncodeLevel,
247 EncodeCaller: base.EncodeCaller,
248 },
249 expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
250 expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack\n",
251 },
252 {
253 desc: "skip stacktrace if StacktraceKey is omitted",
254 cfg: EncoderConfig{
255 LevelKey: "L",
256 TimeKey: "T",
257 MessageKey: "M",
258 NameKey: "N",
259 CallerKey: "C",
260 FunctionKey: "F",
261 StacktraceKey: OmitKey,
262 LineEnding: base.LineEnding,
263 EncodeTime: base.EncodeTime,
264 EncodeDuration: base.EncodeDuration,
265 EncodeLevel: base.EncodeLevel,
266 EncodeCaller: base.EncodeCaller,
267 },
268 expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello"}` + "\n",
269 expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\n",
270 },
271 {
272 desc: "use the supplied EncodeTime, for both the entry and any times added",
273 cfg: EncoderConfig{
274 LevelKey: "L",
275 TimeKey: "T",
276 MessageKey: "M",
277 NameKey: "N",
278 CallerKey: "C",
279 FunctionKey: "F",
280 StacktraceKey: "S",
281 LineEnding: base.LineEnding,
282 EncodeTime: func(t time.Time, enc PrimitiveArrayEncoder) { enc.AppendString(t.String()) },
283 EncodeDuration: base.EncodeDuration,
284 EncodeLevel: base.EncodeLevel,
285 EncodeCaller: base.EncodeCaller,
286 },
287 extra: func(enc Encoder) {
288 enc.AddTime("extra", _epoch)
289 err := enc.AddArray("extras", ArrayMarshalerFunc(func(enc ArrayEncoder) error {
290 enc.AppendTime(_epoch)
291 return nil
292 }))
293 assert.NoError(t, err)
294 },
295 expectedJSON: `{"L":"info","T":"1970-01-01 00:00:00 +0000 UTC","N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","extra":"1970-01-01 00:00:00 +0000 UTC","extras":["1970-01-01 00:00:00 +0000 UTC"],"S":"fake-stack"}` + "\n",
296 expectedConsole: "1970-01-01 00:00:00 +0000 UTC\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\t" +
297 `{"extra": "1970-01-01 00:00:00 +0000 UTC", "extras": ["1970-01-01 00:00:00 +0000 UTC"]}` +
298 "\nfake-stack\n",
299 },
300 {
301 desc: "use the supplied EncodeDuration for any durations added",
302 cfg: EncoderConfig{
303 LevelKey: "L",
304 TimeKey: "T",
305 MessageKey: "M",
306 NameKey: "N",
307 CallerKey: "C",
308 FunctionKey: "F",
309 StacktraceKey: "S",
310 LineEnding: base.LineEnding,
311 EncodeTime: base.EncodeTime,
312 EncodeDuration: StringDurationEncoder,
313 EncodeLevel: base.EncodeLevel,
314 EncodeCaller: base.EncodeCaller,
315 },
316 extra: func(enc Encoder) {
317 enc.AddDuration("extra", time.Second)
318 err := enc.AddArray("extras", ArrayMarshalerFunc(func(enc ArrayEncoder) error {
319 enc.AppendDuration(time.Minute)
320 return nil
321 }))
322 assert.NoError(t, err)
323 },
324 expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","extra":"1s","extras":["1m0s"],"S":"fake-stack"}` + "\n",
325 expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\t" +
326 `{"extra": "1s", "extras": ["1m0s"]}` +
327 "\nfake-stack\n",
328 },
329 {
330 desc: "use the supplied EncodeLevel",
331 cfg: EncoderConfig{
332 LevelKey: "L",
333 TimeKey: "T",
334 MessageKey: "M",
335 NameKey: "N",
336 CallerKey: "C",
337 FunctionKey: "F",
338 StacktraceKey: "S",
339 LineEnding: base.LineEnding,
340 EncodeTime: base.EncodeTime,
341 EncodeDuration: base.EncodeDuration,
342 EncodeLevel: CapitalLevelEncoder,
343 EncodeCaller: base.EncodeCaller,
344 },
345 expectedJSON: `{"L":"INFO","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
346 expectedConsole: "0\tINFO\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
347 },
348 {
349 desc: "use the supplied EncodeName",
350 cfg: EncoderConfig{
351 LevelKey: "L",
352 TimeKey: "T",
353 MessageKey: "M",
354 NameKey: "N",
355 CallerKey: "C",
356 FunctionKey: "F",
357 StacktraceKey: "S",
358 LineEnding: base.LineEnding,
359 EncodeTime: base.EncodeTime,
360 EncodeDuration: base.EncodeDuration,
361 EncodeLevel: base.EncodeLevel,
362 EncodeCaller: base.EncodeCaller,
363 EncodeName: capitalNameEncoder,
364 },
365 expectedJSON: `{"L":"info","T":0,"N":"MAIN","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
366 expectedConsole: "0\tinfo\tMAIN\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
367 },
368 {
369 desc: "close all open namespaces",
370 cfg: EncoderConfig{
371 LevelKey: "L",
372 TimeKey: "T",
373 MessageKey: "M",
374 NameKey: "N",
375 CallerKey: "C",
376 FunctionKey: "F",
377 StacktraceKey: "S",
378 LineEnding: base.LineEnding,
379 EncodeTime: base.EncodeTime,
380 EncodeDuration: base.EncodeDuration,
381 EncodeLevel: base.EncodeLevel,
382 EncodeCaller: base.EncodeCaller,
383 },
384 extra: func(enc Encoder) {
385 enc.OpenNamespace("outer")
386 enc.OpenNamespace("inner")
387 enc.AddString("foo", "bar")
388 enc.OpenNamespace("innermost")
389 },
390 expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","outer":{"inner":{"foo":"bar","innermost":{}}},"S":"fake-stack"}` + "\n",
391 expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\t" +
392 `{"outer": {"inner": {"foo": "bar", "innermost": {}}}}` +
393 "\nfake-stack\n",
394 },
395 {
396 desc: "handle no-op EncodeTime",
397 cfg: EncoderConfig{
398 LevelKey: "L",
399 TimeKey: "T",
400 MessageKey: "M",
401 NameKey: "N",
402 CallerKey: "C",
403 FunctionKey: "F",
404 StacktraceKey: "S",
405 LineEnding: base.LineEnding,
406 EncodeTime: func(time.Time, PrimitiveArrayEncoder) {},
407 EncodeDuration: base.EncodeDuration,
408 EncodeLevel: base.EncodeLevel,
409 EncodeCaller: base.EncodeCaller,
410 },
411 extra: func(enc Encoder) { enc.AddTime("sometime", time.Unix(0, 100)) },
412 expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","sometime":100,"S":"fake-stack"}` + "\n",
413 expectedConsole: "info\tmain\tfoo.go:42\tfoo.Foo\thello\t" + `{"sometime": 100}` + "\nfake-stack\n",
414 },
415 {
416 desc: "handle no-op EncodeDuration",
417 cfg: EncoderConfig{
418 LevelKey: "L",
419 TimeKey: "T",
420 MessageKey: "M",
421 NameKey: "N",
422 CallerKey: "C",
423 FunctionKey: "F",
424 StacktraceKey: "S",
425 LineEnding: base.LineEnding,
426 EncodeTime: base.EncodeTime,
427 EncodeDuration: func(time.Duration, PrimitiveArrayEncoder) {},
428 EncodeLevel: base.EncodeLevel,
429 EncodeCaller: base.EncodeCaller,
430 },
431 extra: func(enc Encoder) { enc.AddDuration("someduration", time.Microsecond) },
432 expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","someduration":1000,"S":"fake-stack"}` + "\n",
433 expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\t" + `{"someduration": 1000}` + "\nfake-stack\n",
434 },
435 {
436 desc: "handle no-op EncodeLevel",
437 cfg: EncoderConfig{
438 LevelKey: "L",
439 TimeKey: "T",
440 MessageKey: "M",
441 NameKey: "N",
442 CallerKey: "C",
443 FunctionKey: "F",
444 StacktraceKey: "S",
445 LineEnding: base.LineEnding,
446 EncodeTime: base.EncodeTime,
447 EncodeDuration: base.EncodeDuration,
448 EncodeLevel: func(Level, PrimitiveArrayEncoder) {},
449 EncodeCaller: base.EncodeCaller,
450 },
451 expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
452 expectedConsole: "0\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
453 },
454 {
455 desc: "handle no-op EncodeCaller",
456 cfg: EncoderConfig{
457 LevelKey: "L",
458 TimeKey: "T",
459 MessageKey: "M",
460 NameKey: "N",
461 CallerKey: "C",
462 FunctionKey: "F",
463 StacktraceKey: "S",
464 LineEnding: base.LineEnding,
465 EncodeTime: base.EncodeTime,
466 EncodeDuration: base.EncodeDuration,
467 EncodeLevel: base.EncodeLevel,
468 EncodeCaller: func(EntryCaller, PrimitiveArrayEncoder) {},
469 },
470 expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
471 expectedConsole: "0\tinfo\tmain\tfoo.Foo\thello\nfake-stack\n",
472 },
473 {
474 desc: "handle no-op EncodeName",
475 cfg: EncoderConfig{
476 LevelKey: "L",
477 TimeKey: "T",
478 MessageKey: "M",
479 NameKey: "N",
480 CallerKey: "C",
481 FunctionKey: "F",
482 StacktraceKey: "S",
483 LineEnding: base.LineEnding,
484 EncodeTime: base.EncodeTime,
485 EncodeDuration: base.EncodeDuration,
486 EncodeLevel: base.EncodeLevel,
487 EncodeCaller: base.EncodeCaller,
488 EncodeName: func(string, PrimitiveArrayEncoder) {},
489 },
490 expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
491 expectedConsole: "0\tinfo\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
492 },
493 {
494 desc: "use custom line separator",
495 cfg: EncoderConfig{
496 LevelKey: "L",
497 TimeKey: "T",
498 MessageKey: "M",
499 NameKey: "N",
500 CallerKey: "C",
501 FunctionKey: "F",
502 StacktraceKey: "S",
503 LineEnding: "\r\n",
504 EncodeTime: base.EncodeTime,
505 EncodeDuration: base.EncodeDuration,
506 EncodeLevel: base.EncodeLevel,
507 EncodeCaller: base.EncodeCaller,
508 },
509 expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\r\n",
510 expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\r\n",
511 },
512 {
513 desc: "omit line separator definition - fall back to default",
514 cfg: EncoderConfig{
515 LevelKey: "L",
516 TimeKey: "T",
517 MessageKey: "M",
518 NameKey: "N",
519 CallerKey: "C",
520 FunctionKey: "F",
521 StacktraceKey: "S",
522 EncodeTime: base.EncodeTime,
523 EncodeDuration: base.EncodeDuration,
524 EncodeLevel: base.EncodeLevel,
525 EncodeCaller: base.EncodeCaller,
526 },
527 expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + DefaultLineEnding,
528 expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack" + DefaultLineEnding,
529 },
530 }
531
532 for i, tt := range tests {
533 json := NewJSONEncoder(tt.cfg)
534 console := NewConsoleEncoder(tt.cfg)
535 if tt.extra != nil {
536 tt.extra(json)
537 tt.extra(console)
538 }
539 entry := _testEntry
540 if tt.amendEntry != nil {
541 entry = tt.amendEntry(_testEntry)
542 }
543 jsonOut, jsonErr := json.EncodeEntry(entry, nil)
544 if assert.NoError(t, jsonErr, "Unexpected error JSON-encoding entry in case #%d.", i) {
545 assert.Equal(
546 t,
547 tt.expectedJSON,
548 jsonOut.String(),
549 "Unexpected JSON output: expected to %v.", tt.desc,
550 )
551 }
552 consoleOut, consoleErr := console.EncodeEntry(entry, nil)
553 if assert.NoError(t, consoleErr, "Unexpected error console-encoding entry in case #%d.", i) {
554 assert.Equal(
555 t,
556 tt.expectedConsole,
557 consoleOut.String(),
558 "Unexpected console output: expected to %v.", tt.desc,
559 )
560 }
561 }
562 }
563
564 func TestLevelEncoders(t *testing.T) {
565 tests := []struct {
566 name string
567 expected interface{}
568 }{
569 {"capital", "INFO"},
570 {"lower", "info"},
571 {"", "info"},
572 {"something-random", "info"},
573 }
574
575 for _, tt := range tests {
576 var le LevelEncoder
577 require.NoError(t, le.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
578 assertAppended(
579 t,
580 tt.expected,
581 func(arr ArrayEncoder) { le(InfoLevel, arr) },
582 "Unexpected output serializing InfoLevel with %q.", tt.name,
583 )
584 }
585 }
586
587 func TestTimeEncoders(t *testing.T) {
588 moment := time.Unix(100, 50005000).UTC()
589 tests := []struct {
590 yamlDoc string
591 expected interface{}
592 }{
593 {"timeEncoder: iso8601", "1970-01-01T00:01:40.050Z"},
594 {"timeEncoder: ISO8601", "1970-01-01T00:01:40.050Z"},
595 {"timeEncoder: millis", 100050.005},
596 {"timeEncoder: nanos", int64(100050005000)},
597 {"timeEncoder: {layout: 06/01/02 03:04pm}", "70/01/01 12:01am"},
598 {"timeEncoder: ''", 100.050005},
599 {"timeEncoder: something-random", 100.050005},
600 {"timeEncoder: rfc3339", "1970-01-01T00:01:40Z"},
601 {"timeEncoder: RFC3339", "1970-01-01T00:01:40Z"},
602 {"timeEncoder: rfc3339nano", "1970-01-01T00:01:40.050005Z"},
603 {"timeEncoder: RFC3339Nano", "1970-01-01T00:01:40.050005Z"},
604 }
605
606 for _, tt := range tests {
607 cfg := EncoderConfig{}
608 require.NoError(t, yaml.Unmarshal([]byte(tt.yamlDoc), &cfg), "Unexpected error unmarshaling %q.", tt.yamlDoc)
609 require.NotNil(t, cfg.EncodeTime, "Unmashalled timeEncoder is nil for %q.", tt.yamlDoc)
610 assertAppended(
611 t,
612 tt.expected,
613 func(arr ArrayEncoder) { cfg.EncodeTime(moment, arr) },
614 "Unexpected output serializing %v with %q.", moment, tt.yamlDoc,
615 )
616 }
617 }
618
619 func TestTimeEncodersWrongYAML(t *testing.T) {
620 tests := []string{
621 "timeEncoder: [1, 2, 3]",
622 "timeEncoder: {foo:bar",
623 }
624 for _, tt := range tests {
625 cfg := EncoderConfig{}
626 assert.Error(t, yaml.Unmarshal([]byte(tt), &cfg), "Expected unmarshaling %q to become error, but not.", tt)
627 }
628 }
629
630 func TestTimeEncodersParseFromJSON(t *testing.T) {
631 moment := time.Unix(100, 50005000).UTC()
632 tests := []struct {
633 jsonDoc string
634 expected interface{}
635 }{
636 {`{"timeEncoder": "iso8601"}`, "1970-01-01T00:01:40.050Z"},
637 {`{"timeEncoder": {"layout": "06/01/02 03:04pm"}}`, "70/01/01 12:01am"},
638 }
639
640 for _, tt := range tests {
641 cfg := EncoderConfig{}
642 require.NoError(t, json.Unmarshal([]byte(tt.jsonDoc), &cfg), "Unexpected error unmarshaling %q.", tt.jsonDoc)
643 require.NotNil(t, cfg.EncodeTime, "Unmashalled timeEncoder is nil for %q.", tt.jsonDoc)
644 assertAppended(
645 t,
646 tt.expected,
647 func(arr ArrayEncoder) { cfg.EncodeTime(moment, arr) },
648 "Unexpected output serializing %v with %q.", moment, tt.jsonDoc,
649 )
650 }
651 }
652
653 func TestDurationEncoders(t *testing.T) {
654 elapsed := time.Second + 500*time.Nanosecond
655 tests := []struct {
656 name string
657 expected interface{}
658 }{
659 {"string", "1.0000005s"},
660 {"nanos", int64(1000000500)},
661 {"ms", int64(1000)},
662 {"", 1.0000005},
663 {"something-random", 1.0000005},
664 }
665
666 for _, tt := range tests {
667 var de DurationEncoder
668 require.NoError(t, de.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
669 assertAppended(
670 t,
671 tt.expected,
672 func(arr ArrayEncoder) { de(elapsed, arr) },
673 "Unexpected output serializing %v with %q.", elapsed, tt.name,
674 )
675 }
676 }
677
678 func TestCallerEncoders(t *testing.T) {
679 caller := EntryCaller{Defined: true, File: "/home/jack/src/github.com/foo/foo.go", Line: 42}
680 tests := []struct {
681 name string
682 expected interface{}
683 }{
684 {"", "foo/foo.go:42"},
685 {"something-random", "foo/foo.go:42"},
686 {"short", "foo/foo.go:42"},
687 {"full", "/home/jack/src/github.com/foo/foo.go:42"},
688 }
689
690 for _, tt := range tests {
691 var ce CallerEncoder
692 require.NoError(t, ce.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
693 assertAppended(
694 t,
695 tt.expected,
696 func(arr ArrayEncoder) { ce(caller, arr) },
697 "Unexpected output serializing file name as %v with %q.", tt.expected, tt.name,
698 )
699 }
700 }
701
702 func TestNameEncoders(t *testing.T) {
703 tests := []struct {
704 name string
705 expected interface{}
706 }{
707 {"", "main"},
708 {"full", "main"},
709 {"something-random", "main"},
710 }
711
712 for _, tt := range tests {
713 var ne NameEncoder
714 require.NoError(t, ne.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
715 assertAppended(
716 t,
717 tt.expected,
718 func(arr ArrayEncoder) { ne("main", arr) },
719 "Unexpected output serializing logger name with %q.", tt.name,
720 )
721 }
722 }
723
724 func assertAppended(t testing.TB, expected interface{}, f func(ArrayEncoder), msgAndArgs ...interface{}) {
725 mem := NewMapObjectEncoder()
726 err := mem.AddArray("k", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
727 f(arr)
728 return nil
729 }))
730 assert.NoError(t, err, msgAndArgs...)
731 arr := mem.Fields["k"].([]interface{})
732 require.Equal(t, 1, len(arr), "Expected to append exactly one element to array.")
733 assert.Equal(t, expected, arr[0], msgAndArgs...)
734 }
735
View as plain text