From 13d5920102be966507edd1e35e8c5cb7e123818d Mon Sep 17 00:00:00 2001 From: Alexander North Date: Mon, 3 Aug 2026 11:13:16 +0200 Subject: [PATCH 1/2] add flag to increase logging verbosity --- artifacts/deployment-apiserver.yaml | 1 + artifacts/deployment-controller.yaml | 2 + cmd/api-server/main.go | 26 ++++++----- cmd/controller/main.go | 28 +++++++----- go.mod | 7 ++- internal/verbosity/verbosity.go | 57 +++++++++++++++++++++++ internal/verbosity/verbosity_test.go | 68 ++++++++++++++++++++++++++++ 7 files changed, 161 insertions(+), 28 deletions(-) create mode 100644 internal/verbosity/verbosity.go create mode 100644 internal/verbosity/verbosity_test.go diff --git a/artifacts/deployment-apiserver.yaml b/artifacts/deployment-apiserver.yaml index 52e664e3..a19ae2f8 100644 --- a/artifacts/deployment-apiserver.yaml +++ b/artifacts/deployment-apiserver.yaml @@ -36,6 +36,7 @@ spec: - "--audit-log-maxage=0" - "--audit-log-maxbackup=0" - "--secure-port=6443" + #- "--v=4" # 4 enables the debug logs ports: - name: api-service containerPort: 6443 diff --git a/artifacts/deployment-controller.yaml b/artifacts/deployment-controller.yaml index cf20ad0d..c7ebb09f 100644 --- a/artifacts/deployment-controller.yaml +++ b/artifacts/deployment-controller.yaml @@ -28,6 +28,8 @@ spec: imagePullPolicy: Always command: - /app/controller + #args: + #- "--v=4" # 4 enables the debug logs env: - name: POD_IP valueFrom: diff --git a/cmd/api-server/main.go b/cmd/api-server/main.go index cafca7b1..fface671 100644 --- a/cmd/api-server/main.go +++ b/cmd/api-server/main.go @@ -25,19 +25,20 @@ import ( "os" "strconv" + "github.com/go-logr/logr" "github.com/henderiw/apiserver-builder/pkg/builder" "github.com/henderiw/apiserver-store/pkg/db/badgerdb" - "github.com/henderiw/logger/log" + logf "github.com/henderiw/logger/log" sdcconfig "github.com/sdcio/config-server/apis/config" "github.com/sdcio/config-server/apis/config/handlers" configv1alpha1 "github.com/sdcio/config-server/apis/config/v1alpha1" + "github.com/sdcio/config-server/internal/verbosity" "github.com/sdcio/config-server/pkg/openapi" _ "github.com/sdcio/config-server/pkg/reconcilers/all" configblameregistry "github.com/sdcio/config-server/pkg/registry/configblame" genericregistry "github.com/sdcio/config-server/pkg/registry/generic" "github.com/sdcio/config-server/pkg/registry/options" runningconfigregistry "github.com/sdcio/config-server/pkg/registry/runningconfig" - "go.uber.org/zap/zapcore" "k8s.io/apimachinery/pkg/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth" // register auth plugins @@ -45,7 +46,6 @@ import ( "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/healthz" - "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics/filters" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" ) @@ -61,18 +61,20 @@ func main() { logs.InitLogs() defer logs.FlushLogs() - l := log.NewLogger(&log.HandlerOptions{Name: "sdc-api-server-logger", AddSource: false}) - slog.SetDefault(l) - ctx := log.IntoContext(context.Background(), l) - log := log.FromContext(ctx) + verbosity.RegisterFlag() - opts := zap.Options{ - TimeEncoder: zapcore.RFC3339NanoTimeEncoder, - } + logVerbosity := verbosity.FromArgs(os.Args[1:]) + level := verbosity.LogLevel(logVerbosity) + + l := logf.NewLogger(&logf.HandlerOptions{Name: "sdc-api-server-logger", MinLevel: level, AddSource: false}) + slog.SetDefault(l) + ctx := logf.IntoContext(context.Background(), l) + log := logf.FromContext(ctx) - ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + // controller-runtime logs through logr, so give it the same handler. + ctrl.SetLogger(logr.FromSlogHandler(l.Handler())) - log.Info("api-server bootstrap", "version", version, "commit", commit) + log.Info("api-server bootstrap", "version", version, "commit", commit, "verbosity", logVerbosity) // setup controllers runScheme := runtime.NewScheme() diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 7bd1e774..e710eaea 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -20,6 +20,7 @@ package main import ( "context" "crypto/tls" + "flag" "fmt" "log/slog" "os" @@ -28,9 +29,11 @@ import ( //"time" - "github.com/henderiw/logger/log" + "github.com/go-logr/logr" + logf "github.com/henderiw/logger/log" configv1alpha1 "github.com/sdcio/config-server/apis/config/v1alpha1" invv1alpha1 "github.com/sdcio/config-server/apis/inv/v1alpha1" + "github.com/sdcio/config-server/internal/verbosity" "github.com/sdcio/config-server/pkg/output/prometheusserver" "github.com/sdcio/config-server/pkg/reconcilers" _ "github.com/sdcio/config-server/pkg/reconcilers/all" @@ -38,7 +41,6 @@ import ( dsclient "github.com/sdcio/config-server/pkg/sdc/dataserver/client" dsmanager "github.com/sdcio/config-server/pkg/sdc/dataserver/manager" targetmanager "github.com/sdcio/config-server/pkg/sdc/target/manager" - "go.uber.org/zap/zapcore" "k8s.io/apimachinery/pkg/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth" // register auth plugins @@ -47,7 +49,6 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/config" "sigs.k8s.io/controller-runtime/pkg/healthz" - "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics/filters" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" ) @@ -64,18 +65,21 @@ func main() { logs.InitLogs() defer logs.FlushLogs() - l := log.NewLogger(&log.HandlerOptions{Name: "sdc-controller-logger", AddSource: false}) - slog.SetDefault(l) - ctx := log.IntoContext(context.Background(), l) - log := log.FromContext(ctx) + verbosity.RegisterFlag() + flag.Parse() - opts := zap.Options{ - TimeEncoder: zapcore.RFC3339NanoTimeEncoder, - } + logVerbosity := verbosity.FromArgs(os.Args[1:]) + level := verbosity.LogLevel(logVerbosity) + + l := logf.NewLogger(&logf.HandlerOptions{Name: "sdc-controller-logger", MinLevel: level, AddSource: false}) + slog.SetDefault(l) + ctx := logf.IntoContext(context.Background(), l) + log := logf.FromContext(ctx) - ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + // controller-runtime logs through logr, so give it the same handler. + ctrl.SetLogger(logr.FromSlogHandler(l.Handler())) - log.Info("controller bootstrap", "version", version, "commit", commit) + log.Info("controller bootstrap", "version", version, "commit", commit, "verbosity", logVerbosity) // setup controllers runScheme := runtime.NewScheme() diff --git a/go.mod b/go.mod index 818a6148..94e9cca1 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.25.0 require ( github.com/dgraph-io/badger/v4 v4.9.1 github.com/go-git/go-git/v5 v5.16.5 + github.com/go-logr/logr v1.4.3 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 github.com/henderiw/apiserver-builder v0.0.8 @@ -22,10 +23,10 @@ require ( github.com/prometheus/prometheus v0.309.1 github.com/sdcio/sdc-protos v0.0.52-0.20260420093658-100270c40f0c github.com/spf13/cobra v1.10.2 + github.com/spf13/pflag v1.0.9 github.com/stretchr/testify v1.11.1 go.opentelemetry.io/otel v1.39.0 go.starlark.net v0.0.0-20250205221240-492d3672b3f4 - go.uber.org/zap v1.27.1 golang.org/x/mod v0.31.0 golang.org/x/sync v0.19.0 google.golang.org/grpc v1.80.0 @@ -75,9 +76,7 @@ require ( github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect - github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.22.1 // indirect github.com/go-openapi/jsonreference v0.21.3 // indirect github.com/go-openapi/swag v0.25.4 // indirect @@ -129,7 +128,6 @@ require ( github.com/sdcio/logger v0.0.3 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/skeema/knownhosts v1.3.1 // indirect - github.com/spf13/pflag v1.0.9 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect @@ -146,6 +144,7 @@ require ( go.opentelemetry.io/otel/trace v1.39.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect diff --git a/internal/verbosity/verbosity.go b/internal/verbosity/verbosity.go new file mode 100644 index 00000000..84e4fe18 --- /dev/null +++ b/internal/verbosity/verbosity.go @@ -0,0 +1,57 @@ +/* +Copyright 2026 Nokia. + +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 verbosity resolves the log level the binaries are started with. +package verbosity + +import ( + "flag" + "io" + "log/slog" + + "github.com/spf13/pflag" +) + +const ( + flagName = "v" + flagUsage = "number for the log level verbosity" +) + +// RegisterFlag declares -v unless one of the logging libraries already did. +func RegisterFlag() { + if flag.Lookup(flagName) == nil { + flag.Int(flagName, 0, flagUsage) + } +} + +// FromArgs returns the verbosity requested through -v, ignoring every other +// flag. The arguments are parsed here because the logger is built before the +// flag sets that own them are. +func FromArgs(args []string) int { + fs := pflag.NewFlagSet(flagName, pflag.ContinueOnError) + fs.ParseErrorsAllowlist = pflag.ParseErrorsAllowlist{UnknownFlags: true} + fs.SetOutput(io.Discard) + verbosity := fs.IntP(flagName, flagName, 0, flagUsage) + if err := fs.Parse(args); err != nil { + return 0 + } + return *verbosity +} + +// LogLevel follows the logr mapping of V(0) onto info and V(4) onto debug. +func LogLevel(verbosity int) slog.Level { + return slog.Level(-verbosity) +} diff --git a/internal/verbosity/verbosity_test.go b/internal/verbosity/verbosity_test.go new file mode 100644 index 00000000..23c65865 --- /dev/null +++ b/internal/verbosity/verbosity_test.go @@ -0,0 +1,68 @@ +/* +Copyright 2026 Nokia. + +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 verbosity + +import ( + "log/slog" + "testing" +) + +func TestFromArgs(t *testing.T) { + cases := map[string]struct { + args []string + expected int + }{ + "no args": {args: nil, expected: 0}, + "long": {args: []string{"--v=4"}, expected: 4}, + "short": {args: []string{"-v=4"}, expected: 4}, + "separate value": {args: []string{"-v", "4"}, expected: 4}, + "unparsable value": {args: []string{"--v=loud"}, expected: 0}, + "different flag": {args: []string{"--vmodule=reconciler=4"}, expected: 0}, + "apiserver flags only": {args: []string{ + "--tls-cert-file", "/apiserver.local.config/certificates/tls.crt", + "--audit-log-path=-", + "--secure-port=6443", + }, expected: 0}, + "among apiserver flags": {args: []string{ + "--tls-cert-file", "/apiserver.local.config/certificates/tls.crt", + "--audit-log-path=-", + "--v=4", + "--secure-port=6443", + }, expected: 4}, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + if got := FromArgs(tc.args); got != tc.expected { + t.Errorf("FromArgs(%q) = %v, want %v", tc.args, got, tc.expected) + } + }) + } +} + +func TestLogLevel(t *testing.T) { + cases := map[int]slog.Level{ + 0: slog.LevelInfo, + 4: slog.LevelDebug, + } + + for verbosity, expected := range cases { + if got := LogLevel(verbosity); got != expected { + t.Errorf("LogLevel(%d) = %v, want %v", verbosity, got, expected) + } + } +} From edec61c98ab00d4b8a5215ece69d16421ba47e3c Mon Sep 17 00:00:00 2001 From: Alexander North Date: Mon, 3 Aug 2026 11:48:29 +0200 Subject: [PATCH 2/2] migrate klog to new logging --- cmd/api-server/main.go | 5 ++++- cmd/controller/main.go | 5 ++++- go.mod | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cmd/api-server/main.go b/cmd/api-server/main.go index fface671..5d9a609e 100644 --- a/cmd/api-server/main.go +++ b/cmd/api-server/main.go @@ -43,6 +43,7 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth" // register auth plugins "k8s.io/component-base/logs" + "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/healthz" @@ -71,8 +72,10 @@ func main() { ctx := logf.IntoContext(context.Background(), l) log := logf.FromContext(ctx) - // controller-runtime logs through logr, so give it the same handler. + // controller-runtime logs through logr and the apiserver machinery through + // klog, so give them the same handler. ctrl.SetLogger(logr.FromSlogHandler(l.Handler())) + klog.SetSlogLogger(l) log.Info("api-server bootstrap", "version", version, "commit", commit, "verbosity", logVerbosity) diff --git a/cmd/controller/main.go b/cmd/controller/main.go index e710eaea..48cfffac 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -45,6 +45,7 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth" // register auth plugins "k8s.io/component-base/logs" + "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/config" @@ -76,8 +77,10 @@ func main() { ctx := logf.IntoContext(context.Background(), l) log := logf.FromContext(ctx) - // controller-runtime logs through logr, so give it the same handler. + // controller-runtime logs through logr and client-go through klog, so give + // them the same handler. ctrl.SetLogger(logr.FromSlogHandler(l.Handler())) + klog.SetSlogLogger(l) log.Info("controller bootstrap", "version", version, "commit", commit, "verbosity", logVerbosity) diff --git a/go.mod b/go.mod index 94e9cca1..3b2a4e32 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ require ( k8s.io/client-go v0.35.3 k8s.io/code-generator v0.35.3 k8s.io/component-base v0.35.3 + k8s.io/klog/v2 v2.130.1 k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 sigs.k8s.io/controller-runtime v0.23.1 @@ -167,7 +168,6 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.35.1 // indirect k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b // indirect - k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kms v0.35.3 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.0 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect