Skip to content

Commit e3f75b8

Browse files
authored
Merge pull request #384 from pohly/slog
slog support + logr 1.3.0 update
2 parents 02e7b69 + 44eadc3 commit e3f75b8

23 files changed

+1027
-93
lines changed

contextual_test.go

+44-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ limitations under the License.
1717
package klog_test
1818

1919
import (
20+
"context"
2021
"fmt"
22+
"runtime"
23+
"testing"
2124

2225
"github.com/go-logr/logr"
2326
"k8s.io/klog/v2"
@@ -41,7 +44,7 @@ func ExampleSetLogger() {
4144
// Output:
4245
// logger after SetLogger: *klog.klogger
4346
// logger after SetLoggerWithOptions with ContextualLogger(false): *klog.klogger
44-
// logger after SetLoggerWithOptions with ContextualLogger(true): logr.discardLogSink
47+
// logger after SetLoggerWithOptions with ContextualLogger(true): <nil>
4548
}
4649

4750
func ExampleFlushLogger() {
@@ -56,3 +59,43 @@ func ExampleFlushLogger() {
5659
// Output:
5760
// flushing...
5861
}
62+
63+
func BenchmarkPassingLogger(b *testing.B) {
64+
b.Run("with context", func(b *testing.B) {
65+
ctx := klog.NewContext(context.Background(), klog.Background())
66+
var finalCtx context.Context
67+
for n := b.N; n > 0; n-- {
68+
finalCtx = passCtx(ctx)
69+
}
70+
runtime.KeepAlive(finalCtx)
71+
})
72+
73+
b.Run("without context", func(b *testing.B) {
74+
logger := klog.Background()
75+
var finalLogger klog.Logger
76+
for n := b.N; n > 0; n-- {
77+
finalLogger = passLogger(logger)
78+
}
79+
runtime.KeepAlive(finalLogger)
80+
})
81+
}
82+
83+
func BenchmarkExtractLogger(b *testing.B) {
84+
b.Run("from context", func(b *testing.B) {
85+
ctx := klog.NewContext(context.Background(), klog.Background())
86+
var finalLogger klog.Logger
87+
for n := b.N; n > 0; n-- {
88+
finalLogger = extractCtx(ctx)
89+
}
90+
runtime.KeepAlive(finalLogger)
91+
})
92+
}
93+
94+
//go:noinline
95+
func passCtx(ctx context.Context) context.Context { return ctx }
96+
97+
//go:noinline
98+
func extractCtx(ctx context.Context) klog.Logger { return klog.FromContext(ctx) }
99+
100+
//go:noinline
101+
func passLogger(logger klog.Logger) klog.Logger { return logger }

examples/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module k8s.io/klog/examples
33
go 1.13
44

55
require (
6-
github.com/go-logr/logr v1.2.2
6+
github.com/go-logr/logr v1.3.0
77
github.com/go-logr/zapr v1.2.3
88
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
99
go.uber.org/goleak v1.1.12

examples/go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZx
33
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
44
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
55
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6-
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
7-
github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
86
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
7+
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
8+
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
99
github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A=
1010
github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4=
1111
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ module k8s.io/klog/v2
22

33
go 1.13
44

5-
require github.com/go-logr/logr v1.2.0
5+
require github.com/go-logr/logr v1.3.0

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE=
2-
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
1+
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
2+
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=

internal/buffer/buffer.go

+9
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ import (
3030
var (
3131
// Pid is inserted into log headers. Can be overridden for tests.
3232
Pid = os.Getpid()
33+
34+
// Time, if set, will be used instead of the actual current time.
35+
Time *time.Time
3336
)
3437

3538
// Buffer holds a single byte.Buffer for reuse. The zero value is ready for
@@ -121,6 +124,9 @@ func (buf *Buffer) FormatHeader(s severity.Severity, file string, line int, now
121124

122125
// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
123126
// It's worth about 3X. Fprintf is hard.
127+
if Time != nil {
128+
now = *Time
129+
}
124130
_, month, day := now.Date()
125131
hour, minute, second := now.Clock()
126132
// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
@@ -156,6 +162,9 @@ func (buf *Buffer) SprintHeader(s severity.Severity, now time.Time) string {
156162

157163
// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
158164
// It's worth about 3X. Fprintf is hard.
165+
if Time != nil {
166+
now = *Time
167+
}
159168
_, month, day := now.Date()
160169
hour, minute, second := now.Clock()
161170
// Lmmdd hh:mm:ss.uuuuuu threadid file:line]

internal/serialize/keyvalues.go

+4-67
Original file line numberDiff line numberDiff line change
@@ -172,73 +172,6 @@ func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
172172
Formatter{}.KVListFormat(b, keysAndValues...)
173173
}
174174

175-
// KVFormat serializes one key/value pair into the provided buffer.
176-
// A space gets inserted before the pair.
177-
func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
178-
b.WriteByte(' ')
179-
// Keys are assumed to be well-formed according to
180-
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments
181-
// for the sake of performance. Keys with spaces,
182-
// special characters, etc. will break parsing.
183-
if sK, ok := k.(string); ok {
184-
// Avoid one allocation when the key is a string, which
185-
// normally it should be.
186-
b.WriteString(sK)
187-
} else {
188-
b.WriteString(fmt.Sprintf("%s", k))
189-
}
190-
191-
// The type checks are sorted so that more frequently used ones
192-
// come first because that is then faster in the common
193-
// cases. In Kubernetes, ObjectRef (a Stringer) is more common
194-
// than plain strings
195-
// (https://github.com/kubernetes/kubernetes/pull/106594#issuecomment-975526235).
196-
switch v := v.(type) {
197-
case textWriter:
198-
writeTextWriterValue(b, v)
199-
case fmt.Stringer:
200-
writeStringValue(b, StringerToString(v))
201-
case string:
202-
writeStringValue(b, v)
203-
case error:
204-
writeStringValue(b, ErrorToString(v))
205-
case logr.Marshaler:
206-
value := MarshalerToValue(v)
207-
// A marshaler that returns a string is useful for
208-
// delayed formatting of complex values. We treat this
209-
// case like a normal string. This is useful for
210-
// multi-line support.
211-
//
212-
// We could do this by recursively formatting a value,
213-
// but that comes with the risk of infinite recursion
214-
// if a marshaler returns itself. Instead we call it
215-
// only once and rely on it returning the intended
216-
// value directly.
217-
switch value := value.(type) {
218-
case string:
219-
writeStringValue(b, value)
220-
default:
221-
f.formatAny(b, value)
222-
}
223-
case []byte:
224-
// In https://github.com/kubernetes/klog/pull/237 it was decided
225-
// to format byte slices with "%+q". The advantages of that are:
226-
// - readable output if the bytes happen to be printable
227-
// - non-printable bytes get represented as unicode escape
228-
// sequences (\uxxxx)
229-
//
230-
// The downsides are that we cannot use the faster
231-
// strconv.Quote here and that multi-line output is not
232-
// supported. If developers know that a byte array is
233-
// printable and they want multi-line output, they can
234-
// convert the value to string before logging it.
235-
b.WriteByte('=')
236-
b.WriteString(fmt.Sprintf("%+q", v))
237-
default:
238-
f.formatAny(b, v)
239-
}
240-
}
241-
242175
func KVFormat(b *bytes.Buffer, k, v interface{}) {
243176
Formatter{}.KVFormat(b, k, v)
244177
}
@@ -251,6 +184,10 @@ func (f Formatter) formatAny(b *bytes.Buffer, v interface{}) {
251184
b.WriteString(f.AnyToStringHook(v))
252185
return
253186
}
187+
formatAsJSON(b, v)
188+
}
189+
190+
func formatAsJSON(b *bytes.Buffer, v interface{}) {
254191
encoder := json.NewEncoder(b)
255192
l := b.Len()
256193
if err := encoder.Encode(v); err != nil {
+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//go:build !go1.21
2+
// +build !go1.21
3+
4+
/*
5+
Copyright 2023 The Kubernetes Authors.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
*/
19+
20+
package serialize
21+
22+
import (
23+
"bytes"
24+
"fmt"
25+
26+
"github.com/go-logr/logr"
27+
)
28+
29+
// KVFormat serializes one key/value pair into the provided buffer.
30+
// A space gets inserted before the pair.
31+
func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
32+
// This is the version without slog support. Must be kept in sync with
33+
// the version in keyvalues_slog.go.
34+
35+
b.WriteByte(' ')
36+
// Keys are assumed to be well-formed according to
37+
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments
38+
// for the sake of performance. Keys with spaces,
39+
// special characters, etc. will break parsing.
40+
if sK, ok := k.(string); ok {
41+
// Avoid one allocation when the key is a string, which
42+
// normally it should be.
43+
b.WriteString(sK)
44+
} else {
45+
b.WriteString(fmt.Sprintf("%s", k))
46+
}
47+
48+
// The type checks are sorted so that more frequently used ones
49+
// come first because that is then faster in the common
50+
// cases. In Kubernetes, ObjectRef (a Stringer) is more common
51+
// than plain strings
52+
// (https://github.com/kubernetes/kubernetes/pull/106594#issuecomment-975526235).
53+
switch v := v.(type) {
54+
case textWriter:
55+
writeTextWriterValue(b, v)
56+
case fmt.Stringer:
57+
writeStringValue(b, StringerToString(v))
58+
case string:
59+
writeStringValue(b, v)
60+
case error:
61+
writeStringValue(b, ErrorToString(v))
62+
case logr.Marshaler:
63+
value := MarshalerToValue(v)
64+
// A marshaler that returns a string is useful for
65+
// delayed formatting of complex values. We treat this
66+
// case like a normal string. This is useful for
67+
// multi-line support.
68+
//
69+
// We could do this by recursively formatting a value,
70+
// but that comes with the risk of infinite recursion
71+
// if a marshaler returns itself. Instead we call it
72+
// only once and rely on it returning the intended
73+
// value directly.
74+
switch value := value.(type) {
75+
case string:
76+
writeStringValue(b, value)
77+
default:
78+
f.formatAny(b, value)
79+
}
80+
case []byte:
81+
// In https://github.com/kubernetes/klog/pull/237 it was decided
82+
// to format byte slices with "%+q". The advantages of that are:
83+
// - readable output if the bytes happen to be printable
84+
// - non-printable bytes get represented as unicode escape
85+
// sequences (\uxxxx)
86+
//
87+
// The downsides are that we cannot use the faster
88+
// strconv.Quote here and that multi-line output is not
89+
// supported. If developers know that a byte array is
90+
// printable and they want multi-line output, they can
91+
// convert the value to string before logging it.
92+
b.WriteByte('=')
93+
b.WriteString(fmt.Sprintf("%+q", v))
94+
default:
95+
f.formatAny(b, v)
96+
}
97+
}

0 commit comments

Comments
 (0)