...

Source file src/k8s.io/klog/v2/contextual.go

Documentation: k8s.io/klog/v2

     1  /*
     2  Copyright 2021 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 klog
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/go-logr/logr"
    23  )
    24  
    25  // This file provides the implementation of
    26  // https://github.com/kubernetes/enhancements/tree/master/keps/sig-instrumentation/1602-structured-logging
    27  //
    28  // SetLogger and ClearLogger were originally added to klog.go and got moved
    29  // here. Contextual logging adds a way to retrieve a Logger for direct logging
    30  // without the logging calls in klog.go.
    31  //
    32  // The global variables are expected to be modified only during sequential
    33  // parts of a program (init, serial tests) and therefore are not protected by
    34  // mutex locking.
    35  
    36  var (
    37  	// klogLogger is used as fallback for logging through the normal klog code
    38  	// when no Logger is set.
    39  	klogLogger logr.Logger = logr.New(&klogger{})
    40  )
    41  
    42  // SetLogger sets a Logger implementation that will be used as backing
    43  // implementation of the traditional klog log calls. klog will do its own
    44  // verbosity checks before calling logger.V().Info. logger.Error is always
    45  // called, regardless of the klog verbosity settings.
    46  //
    47  // If set, all log lines will be suppressed from the regular output, and
    48  // redirected to the logr implementation.
    49  // Use as:
    50  //
    51  //	...
    52  //	klog.SetLogger(zapr.NewLogger(zapLog))
    53  //
    54  // To remove a backing logr implemention, use ClearLogger. Setting an
    55  // empty logger with SetLogger(logr.Logger{}) does not work.
    56  //
    57  // Modifying the logger is not thread-safe and should be done while no other
    58  // goroutines invoke log calls, usually during program initialization.
    59  func SetLogger(logger logr.Logger) {
    60  	SetLoggerWithOptions(logger)
    61  }
    62  
    63  // SetLoggerWithOptions is a more flexible version of SetLogger. Without
    64  // additional options, it behaves exactly like SetLogger. By passing
    65  // ContextualLogger(true) as option, it can be used to set a logger that then
    66  // will also get called directly by applications which retrieve it via
    67  // FromContext, Background, or TODO.
    68  //
    69  // Supporting direct calls is recommended because it avoids the overhead of
    70  // routing log entries through klogr into klog and then into the actual Logger
    71  // backend.
    72  func SetLoggerWithOptions(logger logr.Logger, opts ...LoggerOption) {
    73  	logging.loggerOptions = loggerOptions{}
    74  	for _, opt := range opts {
    75  		opt(&logging.loggerOptions)
    76  	}
    77  	logging.logger = &logWriter{
    78  		Logger:          logger,
    79  		writeKlogBuffer: logging.loggerOptions.writeKlogBuffer,
    80  	}
    81  }
    82  
    83  // ContextualLogger determines whether the logger passed to
    84  // SetLoggerWithOptions may also get called directly. Such a logger cannot rely
    85  // on verbosity checking in klog.
    86  func ContextualLogger(enabled bool) LoggerOption {
    87  	return func(o *loggerOptions) {
    88  		o.contextualLogger = enabled
    89  	}
    90  }
    91  
    92  // FlushLogger provides a callback for flushing data buffered by the logger.
    93  func FlushLogger(flush func()) LoggerOption {
    94  	return func(o *loggerOptions) {
    95  		o.flush = flush
    96  	}
    97  }
    98  
    99  // WriteKlogBuffer sets a callback that will be invoked by klog to write output
   100  // produced by non-structured log calls like Infof.
   101  //
   102  // The buffer will contain exactly the same data that klog normally would write
   103  // into its own output stream(s). In particular this includes the header, if
   104  // klog is configured to write one. The callback then can divert that data into
   105  // its own output streams. The buffer may or may not end in a line break.
   106  //
   107  // Without such a callback, klog will call the logger's Info or Error method
   108  // with just the message string (i.e. no header).
   109  func WriteKlogBuffer(write func([]byte)) LoggerOption {
   110  	return func(o *loggerOptions) {
   111  		o.writeKlogBuffer = write
   112  	}
   113  }
   114  
   115  // LoggerOption implements the functional parameter paradigm for
   116  // SetLoggerWithOptions.
   117  type LoggerOption func(o *loggerOptions)
   118  
   119  type loggerOptions struct {
   120  	contextualLogger bool
   121  	flush            func()
   122  	writeKlogBuffer  func([]byte)
   123  }
   124  
   125  // logWriter combines a logger (always set) with a write callback (optional).
   126  type logWriter struct {
   127  	Logger
   128  	writeKlogBuffer func([]byte)
   129  }
   130  
   131  // ClearLogger removes a backing Logger implementation if one was set earlier
   132  // with SetLogger.
   133  //
   134  // Modifying the logger is not thread-safe and should be done while no other
   135  // goroutines invoke log calls, usually during program initialization.
   136  func ClearLogger() {
   137  	logging.logger = nil
   138  	logging.loggerOptions = loggerOptions{}
   139  }
   140  
   141  // EnableContextualLogging controls whether contextual logging is enabled.
   142  // By default it is enabled. When disabled, FromContext avoids looking up
   143  // the logger in the context and always returns the global logger.
   144  // LoggerWithValues, LoggerWithName, and NewContext become no-ops
   145  // and return their input logger respectively context. This may be useful
   146  // to avoid the additional overhead for contextual logging.
   147  //
   148  // This must be called during initialization before goroutines are started.
   149  func EnableContextualLogging(enabled bool) {
   150  	logging.contextualLoggingEnabled = enabled
   151  }
   152  
   153  // FromContext retrieves a logger set by the caller or, if not set,
   154  // falls back to the program's global logger (a Logger instance or klog
   155  // itself).
   156  func FromContext(ctx context.Context) Logger {
   157  	if logging.contextualLoggingEnabled {
   158  		if logger, err := logr.FromContext(ctx); err == nil {
   159  			return logger
   160  		}
   161  	}
   162  
   163  	return Background()
   164  }
   165  
   166  // TODO can be used as a last resort by code that has no means of
   167  // receiving a logger from its caller. FromContext or an explicit logger
   168  // parameter should be used instead.
   169  func TODO() Logger {
   170  	return Background()
   171  }
   172  
   173  // Background retrieves the fallback logger. It should not be called before
   174  // that logger was initialized by the program and not by code that should
   175  // better receive a logger via its parameters. TODO can be used as a temporary
   176  // solution for such code.
   177  func Background() Logger {
   178  	if logging.loggerOptions.contextualLogger {
   179  		// Is non-nil because logging.loggerOptions.contextualLogger is
   180  		// only true if a logger was set.
   181  		return logging.logger.Logger
   182  	}
   183  
   184  	return klogLogger
   185  }
   186  
   187  // LoggerWithValues returns logger.WithValues(...kv) when
   188  // contextual logging is enabled, otherwise the logger.
   189  func LoggerWithValues(logger Logger, kv ...interface{}) Logger {
   190  	if logging.contextualLoggingEnabled {
   191  		return logger.WithValues(kv...)
   192  	}
   193  	return logger
   194  }
   195  
   196  // LoggerWithName returns logger.WithName(name) when contextual logging is
   197  // enabled, otherwise the logger.
   198  func LoggerWithName(logger Logger, name string) Logger {
   199  	if logging.contextualLoggingEnabled {
   200  		return logger.WithName(name)
   201  	}
   202  	return logger
   203  }
   204  
   205  // NewContext returns logr.NewContext(ctx, logger) when
   206  // contextual logging is enabled, otherwise ctx.
   207  func NewContext(ctx context.Context, logger Logger) context.Context {
   208  	if logging.contextualLoggingEnabled {
   209  		return logr.NewContext(ctx, logger)
   210  	}
   211  	return ctx
   212  }
   213  

View as plain text