Skip to content

Commit 4da5305

Browse files
pohlythockin
authored andcommitted
make Discard logger equal to null logger
The original intention was to no treat the discard log sink in a special way. But it needs special treatment and lacking that in V() led to a bug: Discard() became different from Discard().V(1). Adding special detection of the discard logger also helps with the future logging of context values, because that extra work can be skipped for the discard logger. The distinction between null logger (from #153) and logr.Discard() was very minor. Instead of fixing the issue above with checks for both cases, Discard() now simply returns the null logger. Performance is a bit better: name old time/op new time/op delta DiscardLogInfoOneArg-36 92.7ns ± 5% 88.2ns ± 3% ~ (p=0.056 n=5+5) DiscardLogInfoSeveralArgs-36 239ns ± 0% 231ns ± 1% -3.40% (p=0.008 n=5+5) DiscardLogInfoWithValues-36 240ns ± 1% 236ns ± 3% ~ (p=0.889 n=5+5) DiscardLogV0Info-36 234ns ± 1% 228ns ± 0% -2.62% (p=0.008 n=5+5) DiscardLogV9Info-36 241ns ± 2% 228ns ± 0% -5.23% (p=0.008 n=5+5) DiscardLogError-36 264ns ± 1% 230ns ± 0% -12.78% (p=0.008 n=5+5) DiscardWithValues-36 116ns ± 1% 110ns ± 1% -5.35% (p=0.008 n=5+5) DiscardWithName-36 2.25ns ± 0% 0.72ns ± 0% -68.12% (p=0.008 n=5+5) FuncrLogInfoOneArg-36 2.13µs ± 2% 2.16µs ± 1% ~ (p=0.222 n=5+5) FuncrJSONLogInfoOneArg-36 2.41µs ± 1% 2.42µs ± 1% ~ (p=0.246 n=5+5) FuncrLogInfoSeveralArgs-36 4.53µs ± 4% 4.40µs ± 4% ~ (p=0.151 n=5+5) FuncrJSONLogInfoSeveralArgs-36 4.71µs ± 8% 4.67µs ± 2% ~ (p=0.310 n=5+5) FuncrLogInfoWithValues-36 4.60µs ± 2% 4.61µs ± 4% ~ (p=0.595 n=5+5) FuncrJSONLogInfoWithValues-36 4.81µs ± 3% 4.84µs ± 3% ~ (p=1.000 n=5+5) FuncrLogV0Info-36 4.45µs ± 3% 4.55µs ± 3% ~ (p=0.222 n=5+5) FuncrJSONLogV0Info-36 4.83µs ± 2% 4.78µs ± 3% ~ (p=0.548 n=5+5) FuncrLogV9Info-36 259ns ± 1% 252ns ± 0% -2.48% (p=0.008 n=5+5) FuncrJSONLogV9Info-36 258ns ± 1% 252ns ± 1% -2.52% (p=0.008 n=5+5) FuncrLogError-36 4.97µs ± 1% 4.78µs ± 3% -3.77% (p=0.032 n=5+5) FuncrJSONLogError-36 5.20µs ± 3% 5.13µs ± 2% ~ (p=0.548 n=5+5) FuncrWithValues-36 1.39µs ± 3% 1.38µs ± 3% ~ (p=0.690 n=5+5) FuncrWithName-36 217ns ± 1% 215ns ± 1% -0.62% (p=0.040 n=5+5) FuncrWithCallDepth-36 206ns ± 1% 210ns ± 1% +1.92% (p=0.008 n=5+5) FuncrJSONLogInfoStringerValue-36 2.59µs ± 2% 2.59µs ± 2% ~ (p=1.000 n=5+5) FuncrJSONLogInfoErrorValue-36 2.61µs ± 2% 2.63µs ± 2% ~ (p=0.310 n=5+5) FuncrJSONLogInfoMarshalerValue-36 2.63µs ± 2% 2.63µs ± 1% ~ (p=0.841 n=5+5) There's no difference in allocations. Co-authored-by: Tim Hockin <thockin@google.com> See #158 (comment)
1 parent 4d25940 commit 4da5305

File tree

4 files changed

+32
-43
lines changed

4 files changed

+32
-43
lines changed

discard.go

+1-31
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,5 @@ package logr
2020
// used whenever the caller is not interested in the logs. Logger instances
2121
// produced by this function always compare as equal.
2222
func Discard() Logger {
23-
return Logger{
24-
level: 0,
25-
sink: discardLogSink{},
26-
}
27-
}
28-
29-
// discardLogSink is a LogSink that discards all messages.
30-
type discardLogSink struct{}
31-
32-
// Verify that it actually implements the interface
33-
var _ LogSink = discardLogSink{}
34-
35-
func (l discardLogSink) Init(RuntimeInfo) {
36-
}
37-
38-
func (l discardLogSink) Enabled(int) bool {
39-
return false
40-
}
41-
42-
func (l discardLogSink) Info(int, string, ...interface{}) {
43-
}
44-
45-
func (l discardLogSink) Error(error, string, ...interface{}) {
46-
}
47-
48-
func (l discardLogSink) WithValues(...interface{}) LogSink {
49-
return l
50-
}
51-
52-
func (l discardLogSink) WithName(string) LogSink {
53-
return l
23+
return New(nil)
5424
}

discard_test.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,31 @@ import (
2424

2525
func TestDiscard(t *testing.T) {
2626
l := Discard()
27-
if _, ok := l.GetSink().(discardLogSink); !ok {
27+
if l.GetSink() != nil {
2828
t.Error("did not return the expected underlying type")
2929
}
3030
// Verify that none of the methods panic, there is not more we can test.
3131
l.WithName("discard").WithValues("z", 5).Info("Hello world")
3232
l.Info("Hello world", "x", 1, "y", 2)
3333
l.V(1).Error(errors.New("foo"), "a", 123)
3434
if l.Enabled() {
35-
t.Error("discardLogSink must always say it is disabled")
35+
t.Error("discard loggers must always be disabled")
3636
}
3737
}
3838

3939
func TestComparable(t *testing.T) {
4040
a := Discard()
4141
if !reflect.TypeOf(a).Comparable() {
42-
t.Fatal("discardLogSink must be comparable")
42+
t.Fatal("discard loggers must be comparable")
4343
}
4444

4545
b := Discard()
4646
if a != b {
47-
t.Fatal("any two discardLogSink must be equal")
47+
t.Fatal("any two discard Loggers must be equal")
48+
}
49+
50+
c := Discard().V(2)
51+
if b != c {
52+
t.Fatal("any two discard Loggers must be equal")
4853
}
4954
}

logr.go

+17-2
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,14 @@ import (
212212
)
213213

214214
// New returns a new Logger instance. This is primarily used by libraries
215-
// implementing LogSink, rather than end users.
215+
// implementing LogSink, rather than end users. Passing a nil sink will create
216+
// a Logger which discards all log lines.
216217
func New(sink LogSink) Logger {
217218
logger := Logger{}
218219
logger.setSink(sink)
219-
sink.Init(runtimeInfo)
220+
if sink != nil {
221+
sink.Init(runtimeInfo)
222+
}
220223
return logger
221224
}
222225

@@ -265,6 +268,9 @@ func (l Logger) Enabled() bool {
265268
// information. The key/value pairs must alternate string keys and arbitrary
266269
// values.
267270
func (l Logger) Info(msg string, keysAndValues ...interface{}) {
271+
if l.sink == nil {
272+
return
273+
}
268274
if l.Enabled() {
269275
if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
270276
withHelper.GetCallStackHelper()()
@@ -298,6 +304,9 @@ func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) {
298304
// level means a log message is less important. Negative V-levels are treated
299305
// as 0.
300306
func (l Logger) V(level int) Logger {
307+
if l.sink == nil {
308+
return l
309+
}
301310
if level < 0 {
302311
level = 0
303312
}
@@ -344,6 +353,9 @@ func (l Logger) WithName(name string) Logger {
344353
// WithCallDepth(1) because it works with implementions that support the
345354
// CallDepthLogSink and/or CallStackHelperLogSink interfaces.
346355
func (l Logger) WithCallDepth(depth int) Logger {
356+
if l.sink == nil {
357+
return l
358+
}
347359
if withCallDepth, ok := l.sink.(CallDepthLogSink); ok {
348360
l.setSink(withCallDepth.WithCallDepth(depth))
349361
}
@@ -365,6 +377,9 @@ func (l Logger) WithCallDepth(depth int) Logger {
365377
// implementation does not support either of these, the original Logger will be
366378
// returned.
367379
func (l Logger) WithCallStackHelper() (func(), Logger) {
380+
if l.sink == nil {
381+
return func() {}, l
382+
}
368383
var helper func()
369384
if withCallDepth, ok := l.sink.(CallDepthLogSink); ok {
370385
l.setSink(withCallDepth.WithCallDepth(1))

logr_test.go

+5-6
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,8 @@ func TestContext(t *testing.T) {
384384
}
385385

386386
out := FromContextOrDiscard(ctx)
387-
if _, ok := out.sink.(discardLogSink); !ok {
388-
t.Errorf("expected a discardLogSink, got %#v", out)
387+
if out.sink != nil {
388+
t.Errorf("expected a nil sink, got %#v", out)
389389
}
390390

391391
sink := &testLogSink{}
@@ -412,11 +412,10 @@ func TestIsZero(t *testing.T) {
412412
if l.IsZero() {
413413
t.Errorf("expected not IsZero")
414414
}
415-
// Discard is not considered a zero unset logger, but an intentional choice
416-
// to ignore messages that should not be overridden by a component.
415+
// Discard is the same as a nil sink.
417416
l = Discard()
418-
if l.IsZero() {
419-
t.Errorf("expected not IsZero")
417+
if !l.IsZero() {
418+
t.Errorf("expected IsZero")
420419
}
421420
}
422421

0 commit comments

Comments
 (0)