Skip to content

Commit d113925

Browse files
committed
ktesting: capture log entries only if requested
Most users won't need this feature. It was enabled by default to keep the API simple and because the primary goal was unit testing, but benchmarks also need this and there unnecessary overhead needs to be avoided.
1 parent 8b4cfd2 commit d113925

File tree

5 files changed

+78
-5
lines changed

5 files changed

+78
-5
lines changed

ktesting/contextual_test.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import (
1616
)
1717

1818
func TestContextual(t *testing.T) {
19-
logger, ctx := ktesting.NewTestContext(t)
19+
var buffer ktesting.BufferTL
20+
logger, ctx := ktesting.NewTestContext(&buffer)
2021

2122
doSomething(ctx)
2223

@@ -33,8 +34,14 @@ func TestContextual(t *testing.T) {
3334
}
3435

3536
actual := testingLogger.GetBuffer().String()
36-
expected := `INFO hello world
37-
INFO foo: hello also from me
37+
if actual != "" {
38+
t.Errorf("testinglogger should not have buffered, got:\n%s", actual)
39+
}
40+
41+
actual = buffer.String()
42+
actual = headerRe.ReplaceAllString(actual, "${1}xxx] ")
43+
expected := `Ixxx] hello world
44+
Ixxx] foo: hello also from me
3845
`
3946
if actual != expected {
4047
t.Errorf("mismatch in captured output, expected:\n%s\ngot:\n%s\n", expected, actual)

ktesting/example_test.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@ import (
2525
)
2626

2727
func ExampleUnderlier() {
28-
logger := ktesting.NewLogger(ktesting.NopTL{}, ktesting.NewConfig(ktesting.Verbosity(4)))
28+
logger := ktesting.NewLogger(ktesting.NopTL{},
29+
ktesting.NewConfig(
30+
ktesting.Verbosity(4),
31+
ktesting.BufferLogs(true),
32+
),
33+
)
2934

3035
logger.Error(errors.New("failure"), "I failed", "what", "something")
3136
logger.WithValues("request", 42).WithValues("anotherValue", "fish").Info("hello world")
@@ -69,3 +74,24 @@ func ExampleUnderlier() {
6974
// log entry #3: {Timestamp:0001-01-01 00:00:00 +0000 UTC Type:INFO Prefix:example Message:with name Verbosity:0 Err:<nil> WithKVList:[] ParameterKVList:[]}
7075
// log entry #4: {Timestamp:0001-01-01 00:00:00 +0000 UTC Type:INFO Prefix: Message:higher verbosity Verbosity:4 Err:<nil> WithKVList:[] ParameterKVList:[]}
7176
}
77+
78+
func ExampleDefaults() {
79+
var buffer ktesting.BufferTL
80+
logger := ktesting.NewLogger(&buffer, ktesting.NewConfig())
81+
82+
logger.Error(errors.New("failure"), "I failed", "what", "something")
83+
logger.V(5).Info("Logged at level 5.")
84+
logger.V(6).Info("Not logged at level 6.")
85+
86+
testingLogger, ok := logger.GetSink().(ktesting.Underlier)
87+
if !ok {
88+
panic("Should have had a ktesting LogSink!?")
89+
}
90+
fmt.Printf(">> %s <<\n", testingLogger.GetBuffer().String()) // Should be empty.
91+
fmt.Print(headerRe.ReplaceAllString(buffer.String(), "${1}...] ")) // Should not be empty.
92+
93+
// Output:
94+
// >> <<
95+
// E...] I failed err="failure" what="something"
96+
// I...] Logged at level 5.
97+
}

ktesting/options.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type configOptions struct {
5050
verbosityFlagName string
5151
vmoduleFlagName string
5252
verbosityDefault int
53+
bufferLogs bool
5354
}
5455

5556
// VerbosityFlagName overrides the default -testing.v for the verbosity level.
@@ -94,6 +95,21 @@ func Verbosity(level int) ConfigOption {
9495
}
9596
}
9697

98+
// BufferLogs controls whether log entries are captured in memory in addition
99+
// to being printed. Off by default. Unit tests that want to verify that
100+
// log entries are emitted as expected can turn this on and then retrieve
101+
// the captured log through the Underlier LogSink interface.
102+
//
103+
// # Experimental
104+
//
105+
// Notice: This function is EXPERIMENTAL and may be changed or removed in a
106+
// later release.
107+
func BufferLogs(enabled bool) ConfigOption {
108+
return func(co *configOptions) {
109+
co.bufferLogs = enabled
110+
}
111+
}
112+
97113
// NewConfig returns a configuration with recommended defaults and optional
98114
// modifications. Command line flags are not bound to any FlagSet yet.
99115
//

ktesting/testinglogger.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ limitations under the License.
4242
package ktesting
4343

4444
import (
45+
"fmt"
4546
"strings"
4647
"sync"
4748
"time"
@@ -81,6 +82,23 @@ func (n NopTL) Log(args ...interface{}) {}
8182

8283
var _ TL = NopTL{}
8384

85+
// BufferTL implements TL with an in-memory buffer.
86+
//
87+
// # Experimental
88+
//
89+
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
90+
// later release.
91+
type BufferTL struct {
92+
strings.Builder
93+
}
94+
95+
func (n *BufferTL) Helper() {}
96+
func (n *BufferTL) Log(args ...interface{}) {
97+
n.Builder.WriteString(fmt.Sprintln(args...))
98+
}
99+
100+
var _ TL = &BufferTL{}
101+
84102
// NewLogger constructs a new logger for the given test interface.
85103
//
86104
// Beware that testing.T does not support logging after the test that
@@ -362,6 +380,10 @@ func (l tlogger) log(what LogType, msg string, level int, buf *buffer.Buffer, er
362380
}
363381
l.shared.t.Log(args...)
364382

383+
if !l.shared.config.co.bufferLogs {
384+
return
385+
}
386+
365387
l.shared.buffer.mutex.Lock()
366388
defer l.shared.buffer.mutex.Unlock()
367389

ktesting/testinglogger_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"k8s.io/klog/v2/ktesting"
2222
)
2323

24+
var headerRe = regexp.MustCompile(`([IE])[[:digit:]]{4} [[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\.[[:digit:]]{6}\] `)
25+
2426
func TestInfo(t *testing.T) {
2527
tests := map[string]struct {
2628
text string
@@ -124,7 +126,7 @@ func TestInfo(t *testing.T) {
124126
}
125127

126128
actual := buffer.String()
127-
actual = regexp.MustCompile(`([IE])[[:digit:]]{4} [[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\.[[:digit:]]{6}\] `).ReplaceAllString(actual, `${1}xxx `)
129+
actual = headerRe.ReplaceAllString(actual, `${1}xxx `)
128130
if actual != test.expectedOutput {
129131
t.Errorf("Expected:\n%sActual:\n%s\n", test.expectedOutput, actual)
130132
}

0 commit comments

Comments
 (0)