Repository: kubernetes/klog
Pull request #396 (2024-01-03 15:00:53)

logr v1.4.1 + SetSlogLogger
**What this PR does / why we need it**:

logr v1.4.1 moved the slog support code into the main package, which is now used by klog instead of the deprecated slogr package.

SetSlogLogger syntactic sugar, but it's still useful because it hides logr from developers who only care about klog and slog.

**Which issue(s) this PR fixes** 

Related-to: https://github.com/kubernetes/kubernetes/pull/120696#discussion_r1417844378

/cc @thockin

**Release note**:
```release-note
add SetSlogLogger (available with Go >= 1.21)
```

k8s-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes/klog/pull/396#" title="Author self-approved">pohly</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes%2Fklog).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/kubernetes/klog/blob/main/OWNERS)~~ [pohly]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
k8s-ci-robot: This issue is currently awaiting triage.

If klog contributors determine this is a relevant issue, they will accept it by applying the `triage/accepted` label and provide further guidance.

The `triage/accepted` label can be added by org members by writing `/triage accepted` in a comment.


<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

Patch:
diff --git a/contextual_slog.go b/contextual_slog.go
new file mode 100644
index 0000000..d3b5625
--- /dev/null
+++ b/contextual_slog.go
@@ -0,0 +1,31 @@
+//go:build go1.21
+// +build go1.21
+
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package klog
+
+import (
+	"log/slog"
+
+	"github.com/go-logr/logr"
+)
+
+// SetSlogLogger reconfigures klog to log through the slog logger. The logger must not be nil.
+func SetSlogLogger(logger *slog.Logger) {
+	SetLoggerWithOptions(logr.FromSlogHandler(logger.Handler()), ContextualLogger(true))
+}
diff --git a/examples/go.mod b/examples/go.mod
index b4ef2a6..be4bff1 100644
--- a/examples/go.mod
+++ b/examples/go.mod
@@ -3,7 +3,7 @@ module k8s.io/klog/examples
 go 1.13
 
 require (
-	github.com/go-logr/logr v1.3.0
+	github.com/go-logr/logr v1.4.1
 	github.com/go-logr/zapr v1.2.3
 	github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
 	go.uber.org/goleak v1.1.12
diff --git a/examples/go.sum b/examples/go.sum
index 96988ba..c3ac409 100644
--- a/examples/go.sum
+++ b/examples/go.sum
@@ -4,8 +4,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
-github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A=
 github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
diff --git a/go.mod b/go.mod
index 15f54c2..c0a0678 100644
--- a/go.mod
+++ b/go.mod
@@ -1,5 +1,5 @@
 module k8s.io/klog/v2
 
-go 1.13
+go 1.18
 
-require github.com/go-logr/logr v1.3.0
+require github.com/go-logr/logr v1.4.1
diff --git a/go.sum b/go.sum
index 391a9f5..0934eaa 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +1,2 @@
-github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
-github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
diff --git a/klogr_slog.go b/klogr_slog.go
index f7bf740..c77d7ba 100644
--- a/klogr_slog.go
+++ b/klogr_slog.go
@@ -25,7 +25,7 @@ import (
 	"strconv"
 	"time"
 
-	"github.com/go-logr/logr/slogr"
+	"github.com/go-logr/logr"
 
 	"k8s.io/klog/v2/internal/buffer"
 	"k8s.io/klog/v2/internal/serialize"
@@ -35,7 +35,7 @@ import (
 
 func (l *klogger) Handle(ctx context.Context, record slog.Record) error {
 	if logging.logger != nil {
-		if slogSink, ok := logging.logger.GetSink().(slogr.SlogSink); ok {
+		if slogSink, ok := logging.logger.GetSink().(logr.SlogSink); ok {
 			// Let that logger do the work.
 			return slogSink.Handle(ctx, record)
 		}
@@ -77,13 +77,13 @@ func slogOutput(file string, line int, now time.Time, err error, s severity.Seve
 	buffer.PutBuffer(b)
 }
 
-func (l *klogger) WithAttrs(attrs []slog.Attr) slogr.SlogSink {
+func (l *klogger) WithAttrs(attrs []slog.Attr) logr.SlogSink {
 	clone := *l
 	clone.values = serialize.WithValues(l.values, sloghandler.Attrs2KVList(l.groups, attrs))
 	return &clone
 }
 
-func (l *klogger) WithGroup(name string) slogr.SlogSink {
+func (l *klogger) WithGroup(name string) logr.SlogSink {
 	clone := *l
 	if clone.groups != "" {
 		clone.groups += "." + name
@@ -93,4 +93,4 @@ func (l *klogger) WithGroup(name string) slogr.SlogSink {
 	return &clone
 }
 
-var _ slogr.SlogSink = &klogger{}
+var _ logr.SlogSink = &klogger{}
diff --git a/textlogger/textlogger_slog.go b/textlogger/textlogger_slog.go
index af0fab0..c888ef8 100644
--- a/textlogger/textlogger_slog.go
+++ b/textlogger/textlogger_slog.go
@@ -23,7 +23,7 @@ import (
 	"context"
 	"log/slog"
 
-	"github.com/go-logr/logr/slogr"
+	"github.com/go-logr/logr"
 
 	"k8s.io/klog/v2/internal/serialize"
 	"k8s.io/klog/v2/internal/sloghandler"
@@ -33,13 +33,13 @@ func (l *tlogger) Handle(ctx context.Context, record slog.Record) error {
 	return sloghandler.Handle(ctx, record, l.groups, l.printWithInfos)
 }
 
-func (l *tlogger) WithAttrs(attrs []slog.Attr) slogr.SlogSink {
+func (l *tlogger) WithAttrs(attrs []slog.Attr) logr.SlogSink {
 	clone := *l
 	clone.values = serialize.WithValues(l.values, sloghandler.Attrs2KVList(l.groups, attrs))
 	return &clone
 }
 
-func (l *tlogger) WithGroup(name string) slogr.SlogSink {
+func (l *tlogger) WithGroup(name string) logr.SlogSink {
 	clone := *l
 	if clone.groups != "" {
 		clone.groups += "." + name
@@ -49,4 +49,4 @@ func (l *tlogger) WithGroup(name string) slogr.SlogSink {
 	return &clone
 }
 
-var _ slogr.SlogSink = &tlogger{}
+var _ logr.SlogSink = &tlogger{}


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index b798c1b..09e62ea 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -4,7 +4,7 @@ jobs:
   test:
     strategy:
       matrix:
-        go-version: [1.18, 1.19, 1.20, 1.21]
+        go-version: ["1.18", "1.19", "1.20", "1.21"]
         platform: [ubuntu-latest, macos-latest, windows-latest]
     runs-on: ${{ matrix.platform }}
     steps:
@@ -27,7 +27,7 @@ jobs:
       - name: Install Go
         uses: actions/setup-go@v1
         with:
-          go-version: 1.18
+          go-version: 1.21
       - name: Add GOBIN to PATH
         run: echo "PATH=$(go env GOPATH)/bin:$PATH" >>$GITHUB_ENV
       - name: Install dependencies
diff --git a/contextual_slog_example_test.go b/contextual_slog_example_test.go
new file mode 100644
index 0000000..0c2ba41
--- /dev/null
+++ b/contextual_slog_example_test.go
@@ -0,0 +1,48 @@
+//go:build go1.21
+// +build go1.21
+
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package klog_test
+
+import (
+	"log/slog"
+	"os"
+
+	"k8s.io/klog/v2"
+)
+
+func ExampleSetSlogLogger() {
+	state := klog.CaptureState()
+	defer state.Restore()
+
+	handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
+		ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
+			if a.Key == slog.TimeKey {
+				// Avoid non-deterministic output.
+				return slog.Attr{}
+			}
+			return a
+		},
+	})
+	logger := slog.New(handler)
+	klog.SetSlogLogger(logger)
+	klog.Info("hello world")
+
+	// Output:
+	// level=INFO msg="hello world"
+}
diff --git a/klogr_slog_test.go b/klogr_slog_test.go
index b6431a7..d112b0f 100644
--- a/klogr_slog_test.go
+++ b/klogr_slog_test.go
@@ -27,7 +27,8 @@ import (
 	"os"
 	"time"
 
-	"github.com/go-logr/logr/slogr"
+	"github.com/go-logr/logr"
+
 	"k8s.io/klog/v2"
 	internal "k8s.io/klog/v2/internal/buffer"
 )
@@ -71,7 +72,7 @@ func ExampleBackground_Slog() {
 	internal.Pid = 123
 
 	logrLogger := klog.Background()
-	slogHandler := slogr.NewSlogHandler(logrLogger)
+	slogHandler := logr.ToSlogHandler(logrLogger)
 	slogLogger := slog.New(slogHandler)
 
 	// Note that -vmodule does not work when using the slog API because
@@ -107,10 +108,10 @@ func ExampleBackground_Slog() {
 	)
 
 	// Output:
-	// I1224 12:30:40.000000     123 klogr_slog_test.go:80] "A debug message"
-	// I1224 12:30:40.000000     123 klogr_slog_test.go:82] "An info message"
-	// W1224 12:30:40.000000     123 klogr_slog_test.go:83] "A warning"
-	// E1224 12:30:40.000000     123 klogr_slog_test.go:84] "An error" err="fake error"
-	// I1224 12:30:40.000000     123 klogr_slog_test.go:87] "Grouping" top.sub={"str":"abc","bool":true,"bottom":{"coordinates":{"X":-1,"Y":-2}}} top.duration="1s" top.pi=3.12 top.e=2.71 top.moreCoordinates={"X":100,"Y":200}
-	// I1224 12:30:40.000000     123 klogr_slog_test.go:103] "slog values" variables={"a":1,"b":2} duration="1s" coordinates={"X":100,"Y":200}
+	// I1224 12:30:40.000000     123 klogr_slog_test.go:81] "A debug message"
+	// I1224 12:30:40.000000     123 klogr_slog_test.go:83] "An info message"
+	// W1224 12:30:40.000000     123 klogr_slog_test.go:84] "A warning"
+	// E1224 12:30:40.000000     123 klogr_slog_test.go:85] "An error" err="fake error"
+	// I1224 12:30:40.000000     123 klogr_slog_test.go:88] "Grouping" top.sub={"str":"abc","bool":true,"bottom":{"coordinates":{"X":-1,"Y":-2}}} top.duration="1s" top.pi=3.12 top.e=2.71 top.moreCoordinates={"X":100,"Y":200}
+	// I1224 12:30:40.000000     123 klogr_slog_test.go:104] "slog values" variables={"a":1,"b":2} duration="1s" coordinates={"X":100,"Y":200}
 }