diff options
Diffstat (limited to '')
-rw-r--r-- | dependencies/pkg/mod/go.uber.org/zap@v1.23.0/sugar_test.go | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/sugar_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/sugar_test.go new file mode 100644 index 0000000..1824e34 --- /dev/null +++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/sugar_test.go @@ -0,0 +1,467 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "testing" + + "go.uber.org/zap/internal/exit" + "go.uber.org/zap/internal/ztest" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest/observer" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSugarWith(t *testing.T) { + // Convenience functions to create expected error logs. + ignored := func(msg interface{}) observer.LoggedEntry { + return observer.LoggedEntry{ + Entry: zapcore.Entry{Level: ErrorLevel, Message: _oddNumberErrMsg}, + Context: []Field{Any("ignored", msg)}, + } + } + nonString := func(pairs ...invalidPair) observer.LoggedEntry { + return observer.LoggedEntry{ + Entry: zapcore.Entry{Level: ErrorLevel, Message: _nonStringKeyErrMsg}, + Context: []Field{Array("invalid", invalidPairs(pairs))}, + } + } + + tests := []struct { + desc string + args []interface{} + expected []Field + errLogs []observer.LoggedEntry + }{ + { + desc: "nil args", + args: nil, + expected: []Field{}, + errLogs: nil, + }, + { + desc: "empty slice of args", + args: []interface{}{}, + expected: []Field{}, + errLogs: nil, + }, + { + desc: "just a dangling key", + args: []interface{}{"should ignore"}, + expected: []Field{}, + errLogs: []observer.LoggedEntry{ignored("should ignore")}, + }, + { + desc: "well-formed key-value pairs", + args: []interface{}{"foo", 42, "true", "bar"}, + expected: []Field{Int("foo", 42), String("true", "bar")}, + errLogs: nil, + }, + { + desc: "just a structured field", + args: []interface{}{Int("foo", 42)}, + expected: []Field{Int("foo", 42)}, + errLogs: nil, + }, + { + desc: "structured field and a dangling key", + args: []interface{}{Int("foo", 42), "dangling"}, + expected: []Field{Int("foo", 42)}, + errLogs: []observer.LoggedEntry{ignored("dangling")}, + }, + { + desc: "structured field and a dangling non-string key", + args: []interface{}{Int("foo", 42), 13}, + expected: []Field{Int("foo", 42)}, + errLogs: []observer.LoggedEntry{ignored(13)}, + }, + { + desc: "key-value pair and a dangling key", + args: []interface{}{"foo", 42, "dangling"}, + expected: []Field{Int("foo", 42)}, + errLogs: []observer.LoggedEntry{ignored("dangling")}, + }, + { + desc: "pairs, a structured field, and a dangling key", + args: []interface{}{"first", "field", Int("foo", 42), "baz", "quux", "dangling"}, + expected: []Field{String("first", "field"), Int("foo", 42), String("baz", "quux")}, + errLogs: []observer.LoggedEntry{ignored("dangling")}, + }, + { + desc: "one non-string key", + args: []interface{}{"foo", 42, true, "bar"}, + expected: []Field{Int("foo", 42)}, + errLogs: []observer.LoggedEntry{nonString(invalidPair{2, true, "bar"})}, + }, + { + desc: "pairs, structured fields, non-string keys, and a dangling key", + args: []interface{}{"foo", 42, true, "bar", Int("structure", 11), 42, "reversed", "baz", "quux", "dangling"}, + expected: []Field{Int("foo", 42), Int("structure", 11), String("baz", "quux")}, + errLogs: []observer.LoggedEntry{ + ignored("dangling"), + nonString(invalidPair{2, true, "bar"}, invalidPair{5, 42, "reversed"}), + }, + }, + } + + for _, tt := range tests { + withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger.With(tt.args...).Info("") + output := logs.AllUntimed() + if len(tt.errLogs) > 0 { + for i := range tt.errLogs { + assert.Equal(t, tt.errLogs[i], output[i], "Unexpected error log at position %d for scenario %s.", i, tt.desc) + } + } + assert.Equal(t, len(tt.errLogs)+1, len(output), "Expected only one non-error message to be logged in scenario %s.", tt.desc) + assert.Equal(t, tt.expected, output[len(tt.errLogs)].Context, "Unexpected message context in scenario %s.", tt.desc) + }) + } +} + +func TestSugarFieldsInvalidPairs(t *testing.T) { + withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger.With(42, "foo", []string{"bar"}, "baz").Info("") + output := logs.AllUntimed() + + // Double-check that the actual message was logged. + require.Equal(t, 2, len(output), "Unexpected number of entries logged.") + require.Equal(t, observer.LoggedEntry{Context: []Field{}}, output[1], "Unexpected non-error log entry.") + + // Assert that the error message's structured fields serialize properly. + require.Equal(t, 1, len(output[0].Context), "Expected one field in error entry context.") + enc := zapcore.NewMapObjectEncoder() + output[0].Context[0].AddTo(enc) + assert.Equal(t, []interface{}{ + map[string]interface{}{"position": int64(0), "key": int64(42), "value": "foo"}, + map[string]interface{}{"position": int64(2), "key": []interface{}{"bar"}, "value": "baz"}, + }, enc.Fields["invalid"], "Unexpected output when logging invalid key-value pairs.") + }) +} + +func TestSugarStructuredLogging(t *testing.T) { + tests := []struct { + msg string + expectMsg string + }{ + {"foo", "foo"}, + {"", ""}, + } + + // Common to all test cases. + context := []interface{}{"foo", "bar"} + extra := []interface{}{"baz", false} + expectedFields := []Field{String("foo", "bar"), Bool("baz", false)} + + for _, tt := range tests { + withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger.With(context...).Debugw(tt.msg, extra...) + logger.With(context...).Infow(tt.msg, extra...) + logger.With(context...).Warnw(tt.msg, extra...) + logger.With(context...).Errorw(tt.msg, extra...) + logger.With(context...).DPanicw(tt.msg, extra...) + + expected := make([]observer.LoggedEntry, 5) + for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} { + expected[i] = observer.LoggedEntry{ + Entry: zapcore.Entry{Message: tt.expectMsg, Level: lvl}, + Context: expectedFields, + } + } + assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.") + }) + } +} + +func TestSugarConcatenatingLogging(t *testing.T) { + tests := []struct { + args []interface{} + expect string + }{ + {[]interface{}{nil}, "<nil>"}, + } + + // Common to all test cases. + context := []interface{}{"foo", "bar"} + expectedFields := []Field{String("foo", "bar")} + + for _, tt := range tests { + withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger.With(context...).Debug(tt.args...) + logger.With(context...).Info(tt.args...) + logger.With(context...).Warn(tt.args...) + logger.With(context...).Error(tt.args...) + logger.With(context...).DPanic(tt.args...) + + expected := make([]observer.LoggedEntry, 5) + for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} { + expected[i] = observer.LoggedEntry{ + Entry: zapcore.Entry{Message: tt.expect, Level: lvl}, + Context: expectedFields, + } + } + assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.") + }) + } +} + +func TestSugarTemplatedLogging(t *testing.T) { + tests := []struct { + format string + args []interface{} + expect string + }{ + {"", nil, ""}, + {"foo", nil, "foo"}, + // If the user fails to pass a template, degrade to fmt.Sprint. + {"", []interface{}{"foo"}, "foo"}, + } + + // Common to all test cases. + context := []interface{}{"foo", "bar"} + expectedFields := []Field{String("foo", "bar")} + + for _, tt := range tests { + withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger.With(context...).Debugf(tt.format, tt.args...) + logger.With(context...).Infof(tt.format, tt.args...) + logger.With(context...).Warnf(tt.format, tt.args...) + logger.With(context...).Errorf(tt.format, tt.args...) + logger.With(context...).DPanicf(tt.format, tt.args...) + + expected := make([]observer.LoggedEntry, 5) + for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} { + expected[i] = observer.LoggedEntry{ + Entry: zapcore.Entry{Message: tt.expect, Level: lvl}, + Context: expectedFields, + } + } + assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.") + }) + } +} + +func TestSugarLnLogging(t *testing.T) { + tests := []struct { + args []interface{} + expect string + }{ + {nil, ""}, + {[]interface{}{}, ""}, + {[]interface{}{""}, ""}, + {[]interface{}{"foo"}, "foo"}, + {[]interface{}{"foo", "bar"}, "foo bar"}, + } + + // Common to all test cases. + context := []interface{}{"foo", "bar"} + expectedFields := []Field{String("foo", "bar")} + + for _, tt := range tests { + withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger.With(context...).Debugln(tt.args...) + logger.With(context...).Infoln(tt.args...) + logger.With(context...).Warnln(tt.args...) + logger.With(context...).Errorln(tt.args...) + logger.With(context...).DPanicln(tt.args...) + + expected := make([]observer.LoggedEntry, 5) + for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} { + expected[i] = observer.LoggedEntry{ + Entry: zapcore.Entry{Message: tt.expect, Level: lvl}, + Context: expectedFields, + } + } + assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.") + }) + } +} + +func TestSugarLnLoggingIgnored(t *testing.T) { + withSugar(t, WarnLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger.Infoln("hello") + assert.Zero(t, logs.Len(), "Expected zero log statements.") + }) +} + +func TestSugarPanicLogging(t *testing.T) { + tests := []struct { + loggerLevel zapcore.Level + f func(*SugaredLogger) + expectedMsg string + }{ + {FatalLevel, func(s *SugaredLogger) { s.Panic("foo") }, ""}, + {PanicLevel, func(s *SugaredLogger) { s.Panic("foo") }, "foo"}, + {DebugLevel, func(s *SugaredLogger) { s.Panic("foo") }, "foo"}, + {FatalLevel, func(s *SugaredLogger) { s.Panicf("%s", "foo") }, ""}, + {PanicLevel, func(s *SugaredLogger) { s.Panicf("%s", "foo") }, "foo"}, + {DebugLevel, func(s *SugaredLogger) { s.Panicf("%s", "foo") }, "foo"}, + {FatalLevel, func(s *SugaredLogger) { s.Panicw("foo") }, ""}, + {PanicLevel, func(s *SugaredLogger) { s.Panicw("foo") }, "foo"}, + {DebugLevel, func(s *SugaredLogger) { s.Panicw("foo") }, "foo"}, + {FatalLevel, func(s *SugaredLogger) { s.Panicln("foo") }, ""}, + {PanicLevel, func(s *SugaredLogger) { s.Panicln("foo") }, "foo"}, + {DebugLevel, func(s *SugaredLogger) { s.Panicln("foo") }, "foo"}, + } + + for _, tt := range tests { + withSugar(t, tt.loggerLevel, nil, func(sugar *SugaredLogger, logs *observer.ObservedLogs) { + assert.Panics(t, func() { tt.f(sugar) }, "Expected panic-level logger calls to panic.") + if tt.expectedMsg != "" { + assert.Equal(t, []observer.LoggedEntry{{ + Context: []Field{}, + Entry: zapcore.Entry{Message: tt.expectedMsg, Level: PanicLevel}, + }}, logs.AllUntimed(), "Unexpected log output.") + } else { + assert.Equal(t, 0, logs.Len(), "Didn't expect any log output.") + } + }) + } +} + +func TestSugarFatalLogging(t *testing.T) { + tests := []struct { + loggerLevel zapcore.Level + f func(*SugaredLogger) + expectedMsg string + }{ + {FatalLevel + 1, func(s *SugaredLogger) { s.Fatal("foo") }, ""}, + {FatalLevel, func(s *SugaredLogger) { s.Fatal("foo") }, "foo"}, + {DebugLevel, func(s *SugaredLogger) { s.Fatal("foo") }, "foo"}, + {FatalLevel + 1, func(s *SugaredLogger) { s.Fatalf("%s", "foo") }, ""}, + {FatalLevel, func(s *SugaredLogger) { s.Fatalf("%s", "foo") }, "foo"}, + {DebugLevel, func(s *SugaredLogger) { s.Fatalf("%s", "foo") }, "foo"}, + {FatalLevel + 1, func(s *SugaredLogger) { s.Fatalw("foo") }, ""}, + {FatalLevel, func(s *SugaredLogger) { s.Fatalw("foo") }, "foo"}, + {DebugLevel, func(s *SugaredLogger) { s.Fatalw("foo") }, "foo"}, + {FatalLevel + 1, func(s *SugaredLogger) { s.Fatalln("foo") }, ""}, + {FatalLevel, func(s *SugaredLogger) { s.Fatalln("foo") }, "foo"}, + {DebugLevel, func(s *SugaredLogger) { s.Fatalln("foo") }, "foo"}, + } + + for _, tt := range tests { + withSugar(t, tt.loggerLevel, nil, func(sugar *SugaredLogger, logs *observer.ObservedLogs) { + stub := exit.WithStub(func() { tt.f(sugar) }) + assert.True(t, stub.Exited, "Expected all calls to fatal logger methods to exit process.") + if tt.expectedMsg != "" { + assert.Equal(t, []observer.LoggedEntry{{ + Context: []Field{}, + Entry: zapcore.Entry{Message: tt.expectedMsg, Level: FatalLevel}, + }}, logs.AllUntimed(), "Unexpected log output.") + } else { + assert.Equal(t, 0, logs.Len(), "Didn't expect any log output.") + } + }) + } +} + +func TestSugarAddCaller(t *testing.T) { + tests := []struct { + options []Option + pat string + }{ + {opts(AddCaller()), `.+/sugar_test.go:[\d]+$`}, + {opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(-1)), `.+/sugar_test.go:[\d]+$`}, + {opts(AddCaller(), AddCallerSkip(1)), `.+/common_test.go:[\d]+$`}, + {opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(5)), `.+/src/runtime/.*:[\d]+$`}, + } + for _, tt := range tests { + withSugar(t, DebugLevel, tt.options, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger.Info("") + output := logs.AllUntimed() + assert.Equal(t, 1, len(output), "Unexpected number of logs written out.") + assert.Regexp( + t, + tt.pat, + output[0].Caller, + "Expected to find package name and file name in output.", + ) + }) + } +} + +func TestSugarAddCallerFail(t *testing.T) { + errBuf := &ztest.Buffer{} + withSugar(t, DebugLevel, opts(AddCaller(), AddCallerSkip(1e3), ErrorOutput(errBuf)), func(log *SugaredLogger, logs *observer.ObservedLogs) { + log.Info("Failure.") + assert.Regexp( + t, + `Logger.check error: failed to get caller`, + errBuf.String(), + "Didn't find expected failure message.", + ) + assert.Equal( + t, + logs.AllUntimed()[0].Message, + "Failure.", + "Expected original message to survive failures in runtime.Caller.") + }) +} + +func TestSugarWithOptionsIncreaseLevel(t *testing.T) { + withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger = logger.WithOptions(IncreaseLevel(WarnLevel)) + logger.Info("logger.Info") + logger.Warn("logger.Warn") + logger.Error("logger.Error") + require.Equal(t, 2, logs.Len(), "expected only warn + error logs due to IncreaseLevel.") + assert.Equal( + t, + logs.AllUntimed()[0].Message, + "logger.Warn", + "Expected first logged message to be warn level message", + ) + }) +} + +func TestSugarLnWithOptionsIncreaseLevel(t *testing.T) { + withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger = logger.WithOptions(IncreaseLevel(WarnLevel)) + logger.Infoln("logger.Infoln") + logger.Warnln("logger.Warnln") + logger.Errorln("logger.Errorln") + require.Equal(t, 2, logs.Len(), "expected only warn + error logs due to IncreaseLevel.") + assert.Equal( + t, + logs.AllUntimed()[0].Message, + "logger.Warnln", + "Expected first logged message to be warn level message", + ) + }) +} + +func BenchmarkSugarSingleStrArg(b *testing.B) { + withSugar(b, InfoLevel, nil /* opts* */, func(log *SugaredLogger, logs *observer.ObservedLogs) { + for i := 0; i < b.N; i++ { + log.Info("hello world") + } + }) +} + +func BenchmarkLnSugarSingleStrArg(b *testing.B) { + withSugar(b, InfoLevel, nil /* opts* */, func(log *SugaredLogger, logs *observer.ObservedLogs) { + for i := 0; i < b.N; i++ { + log.Infoln("hello world") + } + }) +} |