From e092d8942780f81a2e4a255467b1a8bfcc111eef Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Wed, 8 Feb 2023 14:48:16 +0100 Subject: [PATCH 1/2] ktesting: support verbosity changes at runtime Being able to change the verbosity at runtime is useful. It is already supported by the underlying code, ktesting and its Config struct just didn't expose it. --- ktesting/example_test.go | 23 +++++++++++++++++++++++ ktesting/options.go | 14 ++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/ktesting/example_test.go b/ktesting/example_test.go index 3fc688bf..9d887932 100644 --- a/ktesting/example_test.go +++ b/ktesting/example_test.go @@ -98,3 +98,26 @@ func ExampleNewLogger() { // E...] I failed err="failure" what="something" data={field:1} // I...] Logged at level 5. } + +func ExampleConfig_Verbosity() { + var buffer ktesting.BufferTL + config := ktesting.NewConfig(ktesting.Verbosity(1)) + logger := ktesting.NewLogger(&buffer, config) + + logger.Info("initial verbosity", "v", config.Verbosity().String()) + logger.V(2).Info("now you don't see me") + if err := config.Verbosity().Set("2"); err != nil { + logger.Error(err, "setting verbosity to 2") + } + logger.V(2).Info("now you see me") + if err := config.Verbosity().Set("1"); err != nil { + logger.Error(err, "setting verbosity to 1") + } + logger.V(2).Info("now I'm gone again") + + fmt.Print(headerRe.ReplaceAllString(buffer.String(), "${1}...] ")) + + // Output: + // I...] initial verbosity v="1" + // I...] now you see me +} diff --git a/ktesting/options.go b/ktesting/options.go index c9bb3da8..d039c40b 100644 --- a/ktesting/options.go +++ b/ktesting/options.go @@ -39,6 +39,20 @@ type Config struct { co configOptions } +// Verbosity returns a value instance that can be used to query (via String) or +// modify (via Set) the verbosity threshold. This is thread-safe and can be +// done at runtime. +func (c *Config) Verbosity() flag.Value { + return c.vstate.V() +} + +// VModule returns a value instance that can be used to query (via String) or +// modify (via Set) the vmodule settings. This is thread-safe and can be done +// at runtime. +func (c *Config) VModule() flag.Value { + return c.vstate.VModule() +} + // ConfigOption implements functional parameters for NewConfig. // // # Experimental From a0fea0c4ae59301dd74006e113fee053aeff55bc Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Wed, 8 Feb 2023 15:00:20 +0100 Subject: [PATCH 2/2] textlogger: verbosity changes through public API By embedding *verbosity.VState in Config, users of the package already had access to V and VModule, but that had two drawbacks: - not easy to discover - unclean separate between internal and external API Providing explicit functions is better. --- ktesting/testinglogger.go | 3 +++ textlogger/example_test.go | 50 ++++++++++++++++++++++++++++++++++++++ textlogger/options.go | 24 ++++++++++++++---- textlogger/textlogger.go | 5 +++- 4 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 textlogger/example_test.go diff --git a/ktesting/testinglogger.go b/ktesting/testinglogger.go index 434b1533..14a22bf4 100644 --- a/ktesting/testinglogger.go +++ b/ktesting/testinglogger.go @@ -107,6 +107,9 @@ var _ TL = &BufferTL{} // that output will be printed via the global klog logger with // ` leaked goroutine` as prefix. // +// Verbosity can be modified at any time through the Config.V and +// Config.VModule API. +// // # Experimental // // Notice: This type is EXPERIMENTAL and may be changed or removed in a diff --git a/textlogger/example_test.go b/textlogger/example_test.go new file mode 100644 index 00000000..9038d2bd --- /dev/null +++ b/textlogger/example_test.go @@ -0,0 +1,50 @@ +/* +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 textlogger_test + +import ( + "bytes" + "fmt" + "regexp" + + "k8s.io/klog/v2/textlogger" +) + +var headerRe = regexp.MustCompile(`([IE])[[:digit:]]{4} [[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\.[[:digit:]]{6}[[:space:]]+[[:digit:]]+ example_test.go:[[:digit:]]+\] `) + +func ExampleConfig_Verbosity() { + var buffer bytes.Buffer + config := textlogger.NewConfig(textlogger.Verbosity(1), textlogger.Output(&buffer)) + logger := textlogger.NewLogger(config) + + logger.Info("initial verbosity", "v", config.Verbosity().String()) + logger.V(2).Info("now you don't see me") + if err := config.Verbosity().Set("2"); err != nil { + logger.Error(err, "setting verbosity to 2") + } + logger.V(2).Info("now you see me") + if err := config.Verbosity().Set("1"); err != nil { + logger.Error(err, "setting verbosity to 1") + } + logger.V(2).Info("now I'm gone again") + + fmt.Print(headerRe.ReplaceAllString(buffer.String(), "${1}...] ")) + + // Output: + // I...] "initial verbosity" v="1" + // I...] "now you see me" +} diff --git a/textlogger/options.go b/textlogger/options.go index 5803ccec..db4cbebd 100644 --- a/textlogger/options.go +++ b/textlogger/options.go @@ -36,8 +36,22 @@ import ( // Notice: This type is EXPERIMENTAL and may be changed or removed in a // later release. type Config struct { - *verbosity.VState - co configOptions + vstate *verbosity.VState + co configOptions +} + +// Verbosity returns a value instance that can be used to query (via String) or +// modify (via Set) the verbosity threshold. This is thread-safe and can be +// done at runtime. +func (c *Config) Verbosity() flag.Value { + return c.vstate.V() +} + +// VModule returns a value instance that can be used to query (via String) or +// modify (via Set) the vmodule settings. This is thread-safe and can be done +// at runtime. +func (c *Config) VModule() flag.Value { + return c.vstate.VModule() } // ConfigOption implements functional parameters for NewConfig. @@ -111,7 +125,7 @@ func Output(output io.Writer) ConfigOption { // later release. func NewConfig(opts ...ConfigOption) *Config { c := &Config{ - VState: verbosity.New(), + vstate: verbosity.New(), co: configOptions{ verbosityFlagName: "v", vmoduleFlagName: "vmodule", @@ -123,7 +137,7 @@ func NewConfig(opts ...ConfigOption) *Config { opt(&c.co) } - c.V().Set(strconv.FormatInt(int64(c.co.verbosityDefault), 10)) + c.Verbosity().Set(strconv.FormatInt(int64(c.co.verbosityDefault), 10)) return c } @@ -134,6 +148,6 @@ func NewConfig(opts ...ConfigOption) *Config { // Notice: This function is EXPERIMENTAL and may be changed or removed in a // later release. func (c *Config) AddFlags(fs *flag.FlagSet) { - fs.Var(c.V(), c.co.verbosityFlagName, "number for the log level verbosity of the testing logger") + fs.Var(c.Verbosity(), c.co.verbosityFlagName, "number for the log level verbosity of the testing logger") fs.Var(c.VModule(), c.co.vmoduleFlagName, "comma-separated list of pattern=N log level settings for files matching the patterns") } diff --git a/textlogger/textlogger.go b/textlogger/textlogger.go index be946e0f..87029c2c 100644 --- a/textlogger/textlogger.go +++ b/textlogger/textlogger.go @@ -50,6 +50,9 @@ var ( // NewLogger constructs a new logger. // +// Verbosity can be modified at any time through the Config.V and +// Config.VModule API. +// // # Experimental // // Notice: This function is EXPERIMENTAL and may be changed or removed in a @@ -82,7 +85,7 @@ func (l *tlogger) WithCallDepth(depth int) logr.LogSink { func (l *tlogger) Enabled(level int) bool { // Skip this function and the Logger.Info call, then // also any additional stack frames from WithCallDepth. - return l.config.Enabled(verbosity.Level(level), 2+l.callDepth) + return l.config.vstate.Enabled(verbosity.Level(level), 2+l.callDepth) } func (l *tlogger) Info(level int, msg string, kvList ...interface{}) {