diff --git a/contextual.go b/contextual.go index a90ee7d3e..67492670d 100644 --- a/contextual.go +++ b/contextual.go @@ -56,7 +56,7 @@ var ( // verbosity checks before calling logger.V().Info. logger.Error is always // called, regardless of the klog verbosity settings. // -// If set, all log lines will be suppressed from the regular Output, and +// If set, all log lines will be suppressed from the regular output, and // redirected to the logr implementation. // Use as: // ... @@ -68,8 +68,57 @@ var ( // Modifying the logger is not thread-safe and should be done while no other // goroutines invoke log calls, usually during program initialization. func SetLogger(logger logr.Logger) { + SetLoggerWithOptions(logger) +} + +// SetLoggerWithOptions is a more flexible version of SetLogger. Without +// additional options, it behaves exactly like SetLogger. By passing +// ContextualLogger(true) as option, it can be used to set a logger that then +// will also get called directly by applications which retrieve it via +// FromContext, Background, or TODO. +// +// Supporting direct calls is recommended because it avoids the overhead of +// routing log entries through klogr into klog and then into the actual Logger +// backend. +// +// Experimental +// +// Notice: This function is EXPERIMENTAL and may be changed or removed in a +// later release. +func SetLoggerWithOptions(logger logr.Logger, opts ...LoggerOption) { globalLogger = &logger - contextualLogger = false + var o loggerOptions + for _, opt := range opts { + opt(&o) + } + contextualLogger = o.contextualLogger +} + +// ContextualLogger determines whether the logger passed to +// SetLoggerWithOptions may also get called directly. Such a logger cannot rely +// on verbosity checking in klog. +// +// Experimental +// +// Notice: This function is EXPERIMENTAL and may be changed or removed in a +// later release. +func ContextualLogger(enabled bool) LoggerOption { + return func(o *loggerOptions) { + o.contextualLogger = enabled + } +} + +// LoggerOption implements the functional parameter paradigm for +// SetLoggerWithOptions. +// +// Experimental +// +// Notice: This type is EXPERIMENTAL and may be changed or removed in a +// later release. +type LoggerOption func(o *loggerOptions) + +type loggerOptions struct { + contextualLogger bool } // SetContextualLogger does the same as SetLogger, but in addition the diff --git a/contextual_test.go b/contextual_test.go new file mode 100644 index 000000000..fc95f05f8 --- /dev/null +++ b/contextual_test.go @@ -0,0 +1,43 @@ +/* +Copyright 2022 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 ( + "fmt" + + "github.com/go-logr/logr" + "k8s.io/klog/v2" +) + +func ExampleSetLogger() { + // Logger is only used as backend, Background() returns klogr. + klog.SetLogger(logr.Discard()) + fmt.Printf("logger after SetLogger: %T\n", klog.Background().GetSink()) + + // Logger is only used as backend, Background() returns klogr. + klog.SetLoggerWithOptions(logr.Discard(), klog.ContextualLogger(false)) + fmt.Printf("logger after SetLoggerWithOptions with ContextualLogger(false): %T\n", klog.Background().GetSink()) + + // Logger is used as backend and directly. + klog.SetLoggerWithOptions(logr.Discard(), klog.ContextualLogger(true)) + fmt.Printf("logger after SetLoggerWithOptions with ContextualLogger(true): %T\n", klog.Background().GetSink()) + + // Output: + // logger after SetLogger: *klog.klogger + // logger after SetLoggerWithOptions with ContextualLogger(false): *klog.klogger + // logger after SetLoggerWithOptions with ContextualLogger(true): logr.discardLogSink +}