From 7a5918ba4fc74887024144dc52beb09ea3f8b630 Mon Sep 17 00:00:00 2001 From: Konrad Wojas Date: Fri, 28 Oct 2022 13:57:23 +0800 Subject: [PATCH] Make zero value useful and add .IsZero() To accomodate optional loggers, make the zero value of a logger useful and not trigger panics when its methods are called. --- logr.go | 16 +++++++++++++++- logr_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/logr.go b/logr.go index 25c2884..f720386 100644 --- a/logr.go +++ b/logr.go @@ -244,7 +244,7 @@ type Logger struct { // Enabled tests whether this Logger is enabled. For example, commandline // flags might be used to set the logging verbosity and disable some info logs. func (l Logger) Enabled() bool { - return l.sink.Enabled(l.level) + return l.sink != nil && l.sink.Enabled(l.level) } // Info logs a non-error message with the given key/value pairs as context. @@ -273,6 +273,9 @@ func (l Logger) Info(msg string, keysAndValues ...interface{}) { // triggered this log line, if present. The err parameter is optional // and nil may be passed instead of an error instance. func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) { + if l.sink == nil { + return + } if withHelper, ok := l.sink.(CallStackHelperLogSink); ok { withHelper.GetCallStackHelper()() } @@ -294,6 +297,9 @@ func (l Logger) V(level int) Logger { // WithValues returns a new Logger instance with additional key/value pairs. // See Info for documentation on how key/value pairs work. func (l Logger) WithValues(keysAndValues ...interface{}) Logger { + if l.sink == nil { + return l + } l.setSink(l.sink.WithValues(keysAndValues...)) return l } @@ -304,6 +310,9 @@ func (l Logger) WithValues(keysAndValues ...interface{}) Logger { // contain only letters, digits, and hyphens (see the package documentation for // more information). func (l Logger) WithName(name string) Logger { + if l.sink == nil { + return l + } l.setSink(l.sink.WithName(name)) return l } @@ -357,6 +366,11 @@ func (l Logger) WithCallStackHelper() (func(), Logger) { return helper, l } +// IsZero returns true if this logger is an uninitialized zero value +func (l Logger) IsZero() bool { + return l.sink == nil +} + // contextKey is how we find Loggers in a context.Context. type contextKey struct{} diff --git a/logr_test.go b/logr_test.go index 3f63818..3ca0c7e 100644 --- a/logr_test.go +++ b/logr_test.go @@ -18,6 +18,7 @@ package logr import ( "context" + "errors" "fmt" "reflect" "testing" @@ -400,3 +401,42 @@ func TestContext(t *testing.T) { t.Errorf("expected output to be the same as input, got in=%p, out=%p", sink, p) } } + +func TestIsZero(t *testing.T) { + var l Logger + if !l.IsZero() { + t.Errorf("expected IsZero") + } + sink := &testLogSink{} + l = New(sink) + if l.IsZero() { + t.Errorf("expected not IsZero") + } + // Discard is not considered a zero unset logger, but an intentional choice + // to ignore messages that should not be overridden by a component. + l = Discard() + if l.IsZero() { + t.Errorf("expected not IsZero") + } +} + +func TestZeroValue(t *testing.T) { + // Make sure that the zero value is useful and equivalent to a Discard logger. + var l Logger + if l.Enabled() { + t.Errorf("expected not Enabled") + } + if !l.IsZero() { + t.Errorf("expected IsZero") + } + // Make sure that none of these methods cause a crash + l.Info("foo") + l.Error(errors.New("bar"), "some error") + if l.GetSink() != nil { + t.Errorf("expected nil from GetSink") + } + l2 := l.WithName("some-name").V(2).WithValues("foo", 1).WithCallDepth(1) + l2.Info("foo") + l2.Error(errors.New("bar"), "some error") + _, _ = l.WithCallStackHelper() +}