...

Source file src/k8s.io/component-base/logs/logs.go

Documentation: k8s.io/component-base/logs

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package logs contains support for logging options, flags and setup.
    18  // Commands must explicitly enable command line flags. They no longer
    19  // get added automatically when importing this package.
    20  package logs
    21  
    22  import (
    23  	"flag"
    24  	"fmt"
    25  	"log"
    26  	"strconv"
    27  	"time"
    28  
    29  	"github.com/spf13/pflag"
    30  	logsapi "k8s.io/component-base/logs/api/v1"
    31  	"k8s.io/component-base/logs/internal/setverbositylevel"
    32  	"k8s.io/component-base/logs/klogflags"
    33  	"k8s.io/klog/v2"
    34  )
    35  
    36  const vmoduleUsage = " (only works for the default text log format)"
    37  
    38  var (
    39  	packageFlags = flag.NewFlagSet("logging", flag.ContinueOnError)
    40  
    41  	// Periodic flushing gets configured either via the global flag
    42  	// in this file or via LoggingConfiguration.
    43  	logFlushFreq time.Duration
    44  )
    45  
    46  func init() {
    47  	klogflags.Init(packageFlags)
    48  	packageFlags.DurationVar(&logFlushFreq, logsapi.LogFlushFreqFlagName, logsapi.LogFlushFreqDefault, "Maximum number of seconds between log flushes")
    49  }
    50  
    51  type addFlagsOptions struct {
    52  	skipLoggingConfigurationFlags bool
    53  }
    54  
    55  type Option func(*addFlagsOptions)
    56  
    57  // SkipLoggingConfigurationFlags must be used as option for AddFlags when
    58  // the program also uses a LoggingConfiguration struct for configuring
    59  // logging. Then only flags not covered by that get added.
    60  func SkipLoggingConfigurationFlags() Option {
    61  	return func(o *addFlagsOptions) {
    62  		o.skipLoggingConfigurationFlags = true
    63  	}
    64  }
    65  
    66  // Options is an alias for LoggingConfiguration to comply with component-base
    67  // conventions.
    68  type Options = logsapi.LoggingConfiguration
    69  
    70  // NewOptions is an alias for NewLoggingConfiguration.
    71  var NewOptions = logsapi.NewLoggingConfiguration
    72  
    73  // AddFlags registers this package's flags on arbitrary FlagSets. This includes
    74  // the klog flags, with the original underscore as separator between. If
    75  // commands want hyphens as separators, they can set
    76  // k8s.io/component-base/cli/flag/WordSepNormalizeFunc as normalization
    77  // function on the flag set before calling AddFlags.
    78  //
    79  // May be called more than once.
    80  func AddFlags(fs *pflag.FlagSet, opts ...Option) {
    81  	o := addFlagsOptions{}
    82  	for _, opt := range opts {
    83  		opt(&o)
    84  	}
    85  
    86  	// Add all supported flags.
    87  	packageFlags.VisitAll(func(f *flag.Flag) {
    88  		pf := pflag.PFlagFromGoFlag(f)
    89  		switch f.Name {
    90  		case "v", logsapi.LogFlushFreqFlagName:
    91  			// unchanged, potentially skip it
    92  			if o.skipLoggingConfigurationFlags {
    93  				return
    94  			}
    95  		case "vmodule":
    96  			if o.skipLoggingConfigurationFlags {
    97  				return
    98  			}
    99  			pf.Usage += vmoduleUsage
   100  		}
   101  		if fs.Lookup(pf.Name) == nil {
   102  			fs.AddFlag(pf)
   103  		}
   104  	})
   105  }
   106  
   107  // AddGoFlags is a variant of AddFlags for traditional Go flag.FlagSet.
   108  // Commands should use pflag whenever possible for the sake of consistency.
   109  // Cases where this function is needed include tests (they have to set up flags
   110  // in flag.CommandLine) and commands that for historic reasons use Go
   111  // flag.Parse and cannot change to pflag because it would break their command
   112  // line interface.
   113  func AddGoFlags(fs *flag.FlagSet, opts ...Option) {
   114  	o := addFlagsOptions{}
   115  	for _, opt := range opts {
   116  		opt(&o)
   117  	}
   118  
   119  	// Add flags with deprecation remark added to the usage text of
   120  	// some klog flags.
   121  	packageFlags.VisitAll(func(f *flag.Flag) {
   122  		usage := f.Usage
   123  		switch f.Name {
   124  		case "v", logsapi.LogFlushFreqFlagName:
   125  			// unchanged
   126  			if o.skipLoggingConfigurationFlags {
   127  				return
   128  			}
   129  		case "vmodule":
   130  			if o.skipLoggingConfigurationFlags {
   131  				return
   132  			}
   133  			usage += vmoduleUsage
   134  		}
   135  		fs.Var(f.Value, f.Name, usage)
   136  	})
   137  }
   138  
   139  // KlogWriter serves as a bridge between the standard log package and the glog package.
   140  type KlogWriter struct{}
   141  
   142  // Write implements the io.Writer interface.
   143  func (writer KlogWriter) Write(data []byte) (n int, err error) {
   144  	klog.InfoDepth(1, string(data))
   145  	return len(data), nil
   146  }
   147  
   148  // InitLogs initializes logs the way we want for Kubernetes.
   149  // It should be called after parsing flags. If called before that,
   150  // it will use the default log settings.
   151  //
   152  // InitLogs disables support for contextual logging in klog while
   153  // that Kubernetes feature is not considered stable yet. Commands
   154  // which want to support contextual logging can:
   155  //   - call klog.EnableContextualLogging after calling InitLogs,
   156  //     with a fixed `true` or depending on some command line flag or
   157  //     a feature gate check
   158  //   - set up a FeatureGate instance, the advanced logging configuration
   159  //     with Options and call Options.ValidateAndApply with the FeatureGate;
   160  //     k8s.io/component-base/logs/example/cmd demonstrates how to do that
   161  func InitLogs() {
   162  	log.SetOutput(KlogWriter{})
   163  	log.SetFlags(0)
   164  
   165  	// Start flushing now. If LoggingConfiguration.ApplyAndValidate is
   166  	// used, it will restart the daemon with the log flush interval defined
   167  	// there.
   168  	klog.StartFlushDaemon(logFlushFreq)
   169  
   170  	// This is the default in Kubernetes. Options.ValidateAndApply
   171  	// will override this with the result of a feature gate check.
   172  	klog.EnableContextualLogging(false)
   173  }
   174  
   175  // FlushLogs flushes logs immediately. This should be called at the end of
   176  // the main function via defer to ensure that all pending log messages
   177  // are printed before exiting the program.
   178  func FlushLogs() {
   179  	klog.Flush()
   180  }
   181  
   182  // NewLogger creates a new log.Logger which sends logs to klog.Info.
   183  func NewLogger(prefix string) *log.Logger {
   184  	return log.New(KlogWriter{}, prefix, 0)
   185  }
   186  
   187  // GlogSetter modifies the verbosity threshold for the entire program.
   188  // Some components have HTTP-based APIs for invoking this at runtime.
   189  func GlogSetter(val string) (string, error) {
   190  	v, err := strconv.ParseUint(val, 10, 32)
   191  	if err != nil {
   192  		return "", err
   193  	}
   194  
   195  	var level klog.Level
   196  	if err := level.Set(val); err != nil {
   197  		return "", fmt.Errorf("failed set klog.logging.verbosity %s: %v", val, err)
   198  	}
   199  
   200  	setverbositylevel.Mutex.Lock()
   201  	defer setverbositylevel.Mutex.Unlock()
   202  	for _, cb := range setverbositylevel.Callbacks {
   203  		if err := cb(uint32(v)); err != nil {
   204  			return "", err
   205  		}
   206  	}
   207  
   208  	return fmt.Sprintf("successfully set klog.logging.verbosity to %s", val), nil
   209  }
   210  

View as plain text