Skip to content

Commit 1b27ee8

Browse files
committed
ktesting: allow overriding default formatter
The intended usage is to replace fmt.Sprintf("%+v") with gomega.format.Object + YAML support, therefore the only public API change is in the (still experimental) ktesting. Internally the additional function pointer gets passed through via a new Formatter struct. To minimize the impact on klog and textlogger, the package-level functions still exist and use an empty Formatter.
1 parent d113925 commit 1b27ee8

File tree

4 files changed

+70
-21
lines changed

4 files changed

+70
-21
lines changed

internal/serialize/keyvalues.go

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,15 @@ func MergeKVs(first, second []interface{}) []interface{} {
9595
return merged
9696
}
9797

98+
type Formatter struct {
99+
AnyToStringHook AnyToStringFunc
100+
}
101+
102+
type AnyToStringFunc func(v interface{}) string
103+
98104
// MergeKVsInto is a variant of MergeKVs which directly formats the key/value
99105
// pairs into a buffer.
100-
func MergeAndFormatKVs(b *bytes.Buffer, first, second []interface{}) {
106+
func (f Formatter) MergeAndFormatKVs(b *bytes.Buffer, first, second []interface{}) {
101107
if len(first) == 0 && len(second) == 0 {
102108
// Nothing to do at all.
103109
return
@@ -107,7 +113,7 @@ func MergeAndFormatKVs(b *bytes.Buffer, first, second []interface{}) {
107113
// Nothing to be overridden, second slice is well-formed
108114
// and can be used directly.
109115
for i := 0; i < len(second); i += 2 {
110-
KVFormat(b, second[i], second[i+1])
116+
f.KVFormat(b, second[i], second[i+1])
111117
}
112118
return
113119
}
@@ -127,24 +133,28 @@ func MergeAndFormatKVs(b *bytes.Buffer, first, second []interface{}) {
127133
if overrides[key] {
128134
continue
129135
}
130-
KVFormat(b, key, first[i+1])
136+
f.KVFormat(b, key, first[i+1])
131137
}
132138
// Round down.
133139
l := len(second)
134140
l = l / 2 * 2
135141
for i := 1; i < l; i += 2 {
136-
KVFormat(b, second[i-1], second[i])
142+
f.KVFormat(b, second[i-1], second[i])
137143
}
138144
if len(second)%2 == 1 {
139-
KVFormat(b, second[len(second)-1], missingValue)
145+
f.KVFormat(b, second[len(second)-1], missingValue)
140146
}
141147
}
142148

149+
func MergeAndFormatKVs(b *bytes.Buffer, first, second []interface{}) {
150+
Formatter{}.MergeAndFormatKVs(b, first, second)
151+
}
152+
143153
const missingValue = "(MISSING)"
144154

145155
// KVListFormat serializes all key/value pairs into the provided buffer.
146156
// A space gets inserted before the first pair and between each pair.
147-
func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
157+
func (f Formatter) KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
148158
for i := 0; i < len(keysAndValues); i += 2 {
149159
var v interface{}
150160
k := keysAndValues[i]
@@ -153,13 +163,17 @@ func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
153163
} else {
154164
v = missingValue
155165
}
156-
KVFormat(b, k, v)
166+
f.KVFormat(b, k, v)
157167
}
158168
}
159169

170+
func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
171+
Formatter{}.KVListFormat(b, keysAndValues...)
172+
}
173+
160174
// KVFormat serializes one key/value pair into the provided buffer.
161175
// A space gets inserted before the pair.
162-
func KVFormat(b *bytes.Buffer, k, v interface{}) {
176+
func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
163177
b.WriteByte(' ')
164178
// Keys are assumed to be well-formed according to
165179
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments
@@ -203,7 +217,7 @@ func KVFormat(b *bytes.Buffer, k, v interface{}) {
203217
case string:
204218
writeStringValue(b, true, value)
205219
default:
206-
writeStringValue(b, false, fmt.Sprintf("%+v", value))
220+
writeStringValue(b, false, f.AnyToString(value))
207221
}
208222
case []byte:
209223
// In https://github.com/kubernetes/klog/pull/237 it was decided
@@ -220,8 +234,20 @@ func KVFormat(b *bytes.Buffer, k, v interface{}) {
220234
b.WriteByte('=')
221235
b.WriteString(fmt.Sprintf("%+q", v))
222236
default:
223-
writeStringValue(b, false, fmt.Sprintf("%+v", v))
237+
writeStringValue(b, false, f.AnyToString(v))
238+
}
239+
}
240+
241+
func KVFormat(b *bytes.Buffer, k, v interface{}) {
242+
Formatter{}.KVFormat(b, k, v)
243+
}
244+
245+
// AnyToString is the historic fallback formatter.
246+
func (f Formatter) AnyToString(v interface{}) string {
247+
if f.AnyToStringHook != nil {
248+
return f.AnyToStringHook(v)
224249
}
250+
return fmt.Sprintf("%+v", v)
225251
}
226252

227253
// StringerToString converts a Stringer to a string,

ktesting/example_test.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ func ExampleUnderlier() {
2929
ktesting.NewConfig(
3030
ktesting.Verbosity(4),
3131
ktesting.BufferLogs(true),
32+
ktesting.AnyToString(func(value interface{}) string {
33+
return fmt.Sprintf("### %+v ###", value)
34+
}),
3235
),
3336
)
3437

35-
logger.Error(errors.New("failure"), "I failed", "what", "something")
38+
logger.Error(errors.New("failure"), "I failed", "what", "something", "data", struct{ field int }{field: 1})
3639
logger.WithValues("request", 42).WithValues("anotherValue", "fish").Info("hello world")
3740
logger.WithValues("request", 42, "anotherValue", "fish").Info("hello world 2", "yetAnotherValue", "thanks")
3841
logger.WithName("example").Info("with name")
@@ -62,24 +65,24 @@ func ExampleUnderlier() {
6265
}
6366

6467
// Output:
65-
// ERROR I failed err="failure" what="something"
66-
// INFO hello world request=42 anotherValue="fish"
67-
// INFO hello world 2 request=42 anotherValue="fish" yetAnotherValue="thanks"
68+
// ERROR I failed err="failure" what="something" data=### {field:1} ###
69+
// INFO hello world request=### 42 ### anotherValue="fish"
70+
// INFO hello world 2 request=### 42 ### anotherValue="fish" yetAnotherValue="thanks"
6871
// INFO example: with name
6972
// INFO higher verbosity
7073
//
71-
// log entry #0: {Timestamp:0001-01-01 00:00:00 +0000 UTC Type:ERROR Prefix: Message:I failed Verbosity:0 Err:failure WithKVList:[] ParameterKVList:[what something]}
74+
// log entry #0: {Timestamp:0001-01-01 00:00:00 +0000 UTC Type:ERROR Prefix: Message:I failed Verbosity:0 Err:failure WithKVList:[] ParameterKVList:[what something data {field:1}]}
7275
// log entry #1: {Timestamp:0001-01-01 00:00:00 +0000 UTC Type:INFO Prefix: Message:hello world Verbosity:0 Err:<nil> WithKVList:[request 42 anotherValue fish] ParameterKVList:[]}
7376
// log entry #2: {Timestamp:0001-01-01 00:00:00 +0000 UTC Type:INFO Prefix: Message:hello world 2 Verbosity:0 Err:<nil> WithKVList:[request 42 anotherValue fish] ParameterKVList:[yetAnotherValue thanks]}
7477
// 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:[]}
7578
// log entry #4: {Timestamp:0001-01-01 00:00:00 +0000 UTC Type:INFO Prefix: Message:higher verbosity Verbosity:4 Err:<nil> WithKVList:[] ParameterKVList:[]}
7679
}
7780

78-
func ExampleDefaults() {
81+
func ExampleNewLogger() {
7982
var buffer ktesting.BufferTL
8083
logger := ktesting.NewLogger(&buffer, ktesting.NewConfig())
8184

82-
logger.Error(errors.New("failure"), "I failed", "what", "something")
85+
logger.Error(errors.New("failure"), "I failed", "what", "something", "data", struct{ field int }{field: 1})
8386
logger.V(5).Info("Logged at level 5.")
8487
logger.V(6).Info("Not logged at level 6.")
8588

@@ -92,6 +95,6 @@ func ExampleDefaults() {
9295

9396
// Output:
9497
// >> <<
95-
// E...] I failed err="failure" what="something"
98+
// E...] I failed err="failure" what="something" data={field:1}
9699
// I...] Logged at level 5.
97100
}

ktesting/options.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"flag"
2121
"strconv"
2222

23+
"k8s.io/klog/v2/internal/serialize"
2324
"k8s.io/klog/v2/internal/verbosity"
2425
)
2526

@@ -47,12 +48,27 @@ type Config struct {
4748
type ConfigOption func(co *configOptions)
4849

4950
type configOptions struct {
51+
anyToString serialize.AnyToStringFunc
5052
verbosityFlagName string
5153
vmoduleFlagName string
5254
verbosityDefault int
5355
bufferLogs bool
5456
}
5557

58+
// AnyToString overrides the default formatter for values that are not
59+
// supported directly by klog. The default is `fmt.Sprintf("%+v")`.
60+
// The formatter must not panic.
61+
//
62+
// # Experimental
63+
//
64+
// Notice: This function is EXPERIMENTAL and may be changed or removed in a
65+
// later release.
66+
func AnyToString(anyToString func(value interface{}) string) ConfigOption {
67+
return func(co *configOptions) {
68+
co.anyToString = anyToString
69+
}
70+
}
71+
5672
// VerbosityFlagName overrides the default -testing.v for the verbosity level.
5773
//
5874
// # Experimental

ktesting/testinglogger.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ func NewLogger(t TL, c *Config) logr.Logger {
118118
config: c,
119119
},
120120
}
121+
if c.co.anyToString != nil {
122+
l.shared.formatter.AnyToStringHook = c.co.anyToString
123+
}
121124

122125
type testCleanup interface {
123126
Cleanup(func())
@@ -280,6 +283,7 @@ type tloggerShared struct {
280283
// it logs after test completion.
281284
goroutineWarningDone bool
282285

286+
formatter serialize.Formatter
283287
testName string
284288
config *Config
285289
buffer logBuffer
@@ -338,7 +342,7 @@ func (l tlogger) Info(level int, msg string, kvList ...interface{}) {
338342

339343
l.shared.t.Helper()
340344
buf := buffer.GetBuffer()
341-
serialize.MergeAndFormatKVs(&buf.Buffer, l.values, kvList)
345+
l.shared.formatter.MergeAndFormatKVs(&buf.Buffer, l.values, kvList)
342346
l.log(LogInfo, msg, level, buf, nil, kvList)
343347
}
344348

@@ -357,9 +361,9 @@ func (l tlogger) Error(err error, msg string, kvList ...interface{}) {
357361
l.shared.t.Helper()
358362
buf := buffer.GetBuffer()
359363
if err != nil {
360-
serialize.KVFormat(&buf.Buffer, "err", err)
364+
l.shared.formatter.KVFormat(&buf.Buffer, "err", err)
361365
}
362-
serialize.MergeAndFormatKVs(&buf.Buffer, l.values, kvList)
366+
l.shared.formatter.MergeAndFormatKVs(&buf.Buffer, l.values, kvList)
363367
l.log(LogError, msg, 0, buf, err, kvList)
364368
}
365369

0 commit comments

Comments
 (0)