mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-05 18:24:07 +00:00
commit
fd8239ce8f
@ -282,6 +282,7 @@ func apply(c *LoggingConfiguration, options *LoggingOptions, featureGate feature
|
||||
if err := loggingFlags.Lookup("vmodule").Value.Set(VModuleConfigurationPflag(&c.VModule).String()); err != nil {
|
||||
return fmt.Errorf("internal error while setting klog vmodule: %v", err)
|
||||
}
|
||||
setSlogDefaultLogger()
|
||||
klog.StartFlushDaemon(c.FlushFrequency.Duration.Duration)
|
||||
klog.EnableContextualLogging(p.ContextualLoggingEnabled)
|
||||
return nil
|
||||
|
@ -0,0 +1,24 @@
|
||||
//go:build !go1.21
|
||||
// +build !go1.21
|
||||
|
||||
/*
|
||||
Copyright 2023 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 v1
|
||||
|
||||
func setSlogDefaultLogger() {
|
||||
// Do nothing when build with Go < 1.21.
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
/*
|
||||
Copyright 2023 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 v1
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// setSlogDefaultLogger sets the global slog default logger to the same default
|
||||
// that klog currently uses.
|
||||
func setSlogDefaultLogger() {
|
||||
// klog.Background() always returns a valid logr.Logger, regardless of
|
||||
// how logging was configured. We just need to turn it into a
|
||||
// slog.Handler. SetDefault then needs a slog.Logger.
|
||||
handler := logr.ToSlogHandler(klog.Background())
|
||||
slog.SetDefault(slog.New(handler))
|
||||
}
|
@ -5,6 +5,9 @@ component-base/logs and what effect the different command line options have.
|
||||
Like most Kubernetes components, `cmd` uses Cobra and pflags. `stdlib` uses
|
||||
just plain Go libraries. `test` contains a unit test with per-test output.
|
||||
|
||||
`slog2k8s` shows how an application using `log/slog` from Go 1.21 can include
|
||||
packages from Kubernetes. `k8s2slog` is the other direction.
|
||||
|
||||
Below we can see examples of how some features work.
|
||||
|
||||
## Default
|
||||
|
@ -0,0 +1,89 @@
|
||||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
/*
|
||||
Copyright 2018 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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/component-base/cli"
|
||||
"k8s.io/component-base/featuregate"
|
||||
"k8s.io/component-base/logs"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
_ "k8s.io/component-base/logs/json/register"
|
||||
)
|
||||
|
||||
var featureGate = featuregate.NewFeatureGate()
|
||||
|
||||
func main() {
|
||||
runtime.Must(logsapi.AddFeatureGates(featureGate))
|
||||
command := NewLoggerCommand()
|
||||
code := cli.Run(command)
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func NewLoggerCommand() *cobra.Command {
|
||||
c := logsapi.NewLoggingConfiguration()
|
||||
cmd := &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// This configures the global logger in klog *and* slog, if compiled
|
||||
// with Go >= 1.21.
|
||||
logs.InitLogs()
|
||||
if err := logsapi.ValidateAndApply(c, featureGate); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(args) > 0 {
|
||||
fmt.Fprintf(os.Stderr, "Unexpected additional command line arguments:\n %s\n", strings.Join(args, "\n "))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Produce some output. Special types used by Kubernetes work.
|
||||
podRef := klog.KObj(&metav1.ObjectMeta{Name: "some-pod", Namespace: "some-namespace"})
|
||||
podRefs := klog.KObjSlice([]interface{}{
|
||||
&metav1.ObjectMeta{Name: "some-pod", Namespace: "some-namespace"},
|
||||
nil,
|
||||
&metav1.ObjectMeta{Name: "other-pod"},
|
||||
})
|
||||
slog.Info("slog.Info", "pod", podRef, "pods", podRefs)
|
||||
klog.InfoS("klog.InfoS", "pod", podRef, "pods", podRefs)
|
||||
klog.Background().Info("klog.Background+logr.Logger.Info")
|
||||
klog.FromContext(context.Background()).Info("klog.FromContext+logr.Logger.Info")
|
||||
slogLogger := slog.Default()
|
||||
slogLogger.Info("slog.Default+slog.Logger.Info")
|
||||
},
|
||||
}
|
||||
if err := logsapi.AddFeatureGates(featureGate); err != nil {
|
||||
// Shouldn't happen.
|
||||
panic(err)
|
||||
}
|
||||
featureGate.AddFlag(cmd.Flags())
|
||||
logsapi.AddFlags(c, cmd.Flags())
|
||||
return cmd
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
/*
|
||||
Copyright 2023 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.
|
||||
*/
|
||||
|
||||
// slog2k8s demonstrates how an application using log/slog for logging
|
||||
// can include Kubernetes packages.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
options := slog.HandlerOptions{AddSource: true}
|
||||
textHandler := slog.NewTextHandler(os.Stderr, &options)
|
||||
textLogger := slog.New(textHandler)
|
||||
|
||||
// Use text output as default logger.
|
||||
slog.SetDefault(textLogger)
|
||||
|
||||
// This also needs to be done through klog to ensure that all code
|
||||
// using klog uses the text handler. klog.Background/TODO/FromContext
|
||||
// will return a thin wrapper around the textHandler, so all that klog
|
||||
// still does is manage the global default and retrieval from contexts.
|
||||
klog.SetSlogLogger(textLogger)
|
||||
|
||||
textLogger.Info("slog.Logger.Info")
|
||||
klog.InfoS("klog.InfoS")
|
||||
klog.Background().Info("klog.Background+logr.Logger.Info")
|
||||
klog.FromContext(context.Background()).Info("klog.FromContext+logr.Logger.Info")
|
||||
}
|
@ -20,6 +20,7 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@ -29,6 +30,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"k8s.io/component-base/featuregate"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
_ "k8s.io/component-base/logs/json/register"
|
||||
@ -93,6 +96,43 @@ func BenchmarkEncoding(b *testing.B) {
|
||||
b.Fatalf("Unexpected error configuring logging: %v", err)
|
||||
}
|
||||
logger := klog.Background()
|
||||
|
||||
// Edit and run with this if branch enabled to use slog instead of zapr for JSON.
|
||||
if format == "json" && false {
|
||||
var level slog.LevelVar
|
||||
level.Set(slog.Level(-3)) // hack
|
||||
logger = logr.FromSlogHandler(slog.NewJSONHandler(output, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: &level,
|
||||
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
|
||||
switch a.Key {
|
||||
case slog.TimeKey:
|
||||
// Could be user-supplied "time".
|
||||
if a.Value.Kind() == slog.KindTime {
|
||||
return slog.Float64("ts", float64(a.Value.Time().UnixMicro())/1000)
|
||||
}
|
||||
case slog.LevelKey:
|
||||
level := a.Value.Any().(slog.Level)
|
||||
if level >= slog.LevelError {
|
||||
// No verbosity on errors.
|
||||
return slog.Attr{}
|
||||
}
|
||||
if level >= 0 {
|
||||
return slog.Int("v", 0)
|
||||
}
|
||||
return slog.Int("v", int(-level))
|
||||
case slog.SourceKey:
|
||||
caller := zapcore.EntryCaller{
|
||||
Defined: true,
|
||||
File: a.Value.String(),
|
||||
}
|
||||
return slog.String("caller", caller.TrimmedPath())
|
||||
}
|
||||
return a
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
start := time.Now()
|
||||
total := int64(0)
|
||||
|
Loading…
Reference in New Issue
Block a user