summaryrefslogtreecommitdiff
path: root/vendor/k8s.io/klog/v2/klog.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/k8s.io/klog/v2/klog.go')
-rw-r--r--vendor/k8s.io/klog/v2/klog.go1689
1 files changed, 1689 insertions, 0 deletions
diff --git a/vendor/k8s.io/klog/v2/klog.go b/vendor/k8s.io/klog/v2/klog.go
new file mode 100644
index 0000000000..1bd11b6754
--- /dev/null
+++ b/vendor/k8s.io/klog/v2/klog.go
@@ -0,0 +1,1689 @@
+// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
+//
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// 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 implements logging analogous to the Google-internal C++ INFO/ERROR/V setup.
+// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as
+// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags.
+//
+// Basic examples:
+//
+// klog.Info("Prepare to repel boarders")
+//
+// klog.Fatalf("Initialization failed: %s", err)
+//
+// See the documentation for the V function for an explanation of these examples:
+//
+// if klog.V(2) {
+// klog.Info("Starting transaction...")
+// }
+//
+// klog.V(2).Infoln("Processed", nItems, "elements")
+//
+// Log output is buffered and written periodically using Flush. Programs
+// should call Flush before exiting to guarantee all log output is written.
+//
+// By default, all log statements write to standard error.
+// This package provides several flags that modify this behavior.
+// As a result, flag.Parse must be called before any logging is done.
+//
+// -logtostderr=true
+// Logs are written to standard error instead of to files.
+// This shortcuts most of the usual output routing:
+// -alsologtostderr, -stderrthreshold and -log_dir have no
+// effect and output redirection at runtime with SetOutput is
+// ignored.
+// -alsologtostderr=false
+// Logs are written to standard error as well as to files.
+// -stderrthreshold=ERROR
+// Log events at or above this severity are logged to standard
+// error as well as to files.
+// -log_dir=""
+// Log files will be written to this directory instead of the
+// default temporary directory.
+//
+// Other flags provide aids to debugging.
+//
+// -log_backtrace_at=""
+// When set to a file and line number holding a logging statement,
+// such as
+// -log_backtrace_at=gopherflakes.go:234
+// a stack trace will be written to the Info log whenever execution
+// hits that statement. (Unlike with -vmodule, the ".go" must be
+// present.)
+// -v=0
+// Enable V-leveled logging at the specified level.
+// -vmodule=""
+// The syntax of the argument is a comma-separated list of pattern=N,
+// where pattern is a literal file name (minus the ".go" suffix) or
+// "glob" pattern and N is a V level. For instance,
+// -vmodule=gopher*=3
+// sets the V level to 3 in all Go files whose names begin "gopher".
+package klog
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ stdLog "log"
+ "math"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/go-logr/logr"
+
+ "k8s.io/klog/v2/internal/buffer"
+ "k8s.io/klog/v2/internal/clock"
+ "k8s.io/klog/v2/internal/dbg"
+ "k8s.io/klog/v2/internal/serialize"
+ "k8s.io/klog/v2/internal/severity"
+)
+
+// severityValue identifies the sort of log: info, warning etc. It also implements
+// the flag.Value interface. The -stderrthreshold flag is of type severity and
+// should be modified only through the flag.Value interface. The values match
+// the corresponding constants in C++.
+type severityValue struct {
+ severity.Severity
+}
+
+// get returns the value of the severity.
+func (s *severityValue) get() severity.Severity {
+ return severity.Severity(atomic.LoadInt32((*int32)(&s.Severity)))
+}
+
+// set sets the value of the severity.
+func (s *severityValue) set(val severity.Severity) {
+ atomic.StoreInt32((*int32)(&s.Severity), int32(val))
+}
+
+// String is part of the flag.Value interface.
+func (s *severityValue) String() string {
+ return strconv.FormatInt(int64(s.Severity), 10)
+}
+
+// Get is part of the flag.Getter interface.
+func (s *severityValue) Get() interface{} {
+ return s.Severity
+}
+
+// Set is part of the flag.Value interface.
+func (s *severityValue) Set(value string) error {
+ var threshold severity.Severity
+ // Is it a known name?
+ if v, ok := severity.ByName(value); ok {
+ threshold = v
+ } else {
+ v, err := strconv.ParseInt(value, 10, 32)
+ if err != nil {
+ return err
+ }
+ threshold = severity.Severity(v)
+ }
+ logging.stderrThreshold.set(threshold)
+ return nil
+}
+
+// OutputStats tracks the number of output lines and bytes written.
+type OutputStats struct {
+ lines int64
+ bytes int64
+}
+
+// Lines returns the number of lines written.
+func (s *OutputStats) Lines() int64 {
+ return atomic.LoadInt64(&s.lines)
+}
+
+// Bytes returns the number of bytes written.
+func (s *OutputStats) Bytes() int64 {
+ return atomic.LoadInt64(&s.bytes)
+}
+
+// Stats tracks the number of lines of output and number of bytes
+// per severity level. Values must be read with atomic.LoadInt64.
+var Stats struct {
+ Info, Warning, Error OutputStats
+}
+
+var severityStats = [severity.NumSeverity]*OutputStats{
+ severity.InfoLog: &Stats.Info,
+ severity.WarningLog: &Stats.Warning,
+ severity.ErrorLog: &Stats.Error,
+}
+
+// Level is exported because it appears in the arguments to V and is
+// the type of the v flag, which can be set programmatically.
+// It's a distinct type because we want to discriminate it from logType.
+// Variables of type level are only changed under logging.mu.
+// The -v flag is read only with atomic ops, so the state of the logging
+// module is consistent.
+
+// Level is treated as a sync/atomic int32.
+
+// Level specifies a level of verbosity for V logs. *Level implements
+// flag.Value; the -v flag is of type Level and should be modified
+// only through the flag.Value interface.
+type Level int32
+
+// get returns the value of the Level.
+func (l *Level) get() Level {
+ return Level(atomic.LoadInt32((*int32)(l)))
+}
+
+// set sets the value of the Level.
+func (l *Level) set(val Level) {
+ atomic.StoreInt32((*int32)(l), int32(val))
+}
+
+// String is part of the flag.Value interface.
+func (l *Level) String() string {
+ return strconv.FormatInt(int64(*l), 10)
+}
+
+// Get is part of the flag.Getter interface.
+func (l *Level) Get() interface{} {
+ return *l
+}
+
+// Set is part of the flag.Value interface.
+func (l *Level) Set(value string) error {
+ v, err := strconv.ParseInt(value, 10, 32)
+ if err != nil {
+ return err
+ }
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ logging.setVState(Level(v), logging.vmodule.filter, false)
+ return nil
+}
+
+// moduleSpec represents the setting of the -vmodule flag.
+type moduleSpec struct {
+ filter []modulePat
+}
+
+// modulePat contains a filter for the -vmodule flag.
+// It holds a verbosity level and a file pattern to match.
+type modulePat struct {
+ pattern string
+ literal bool // The pattern is a literal string
+ level Level
+}
+
+// match reports whether the file matches the pattern. It uses a string
+// comparison if the pattern contains no metacharacters.
+func (m *modulePat) match(file string) bool {
+ if m.literal {
+ return file == m.pattern
+ }
+ match, _ := filepath.Match(m.pattern, file)
+ return match
+}
+
+func (m *moduleSpec) String() string {
+ // Lock because the type is not atomic. TODO: clean this up.
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ return m.serialize()
+}
+
+func (m *moduleSpec) serialize() string {
+ var b bytes.Buffer
+ for i, f := range m.filter {
+ if i > 0 {
+ b.WriteRune(',')
+ }
+ fmt.Fprintf(&b, "%s=%d", f.pattern, f.level)
+ }
+ return b.String()
+}
+
+// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the
+// struct is not exported.
+func (m *moduleSpec) Get() interface{} {
+ return nil
+}
+
+var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N")
+
+// Set will sets module value
+// Syntax: -vmodule=recordio=2,file=1,gfs*=3
+func (m *moduleSpec) Set(value string) error {
+ filter, err := parseModuleSpec(value)
+ if err != nil {
+ return err
+ }
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ logging.setVState(logging.verbosity, filter, true)
+ return nil
+}
+
+func parseModuleSpec(value string) ([]modulePat, error) {
+ var filter []modulePat
+ for _, pat := range strings.Split(value, ",") {
+ if len(pat) == 0 {
+ // Empty strings such as from a trailing comma can be ignored.
+ continue
+ }
+ patLev := strings.Split(pat, "=")
+ if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 {
+ return nil, errVmoduleSyntax
+ }
+ pattern := patLev[0]
+ v, err := strconv.ParseInt(patLev[1], 10, 32)
+ if err != nil {
+ return nil, errors.New("syntax error: expect comma-separated list of filename=N")
+ }
+ if v < 0 {
+ return nil, errors.New("negative value for vmodule level")
+ }
+ if v == 0 {
+ continue // Ignore. It's harmless but no point in paying the overhead.
+ }
+ // TODO: check syntax of filter?
+ filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)})
+ }
+ return filter, nil
+}
+
+// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters
+// that require filepath.Match to be called to match the pattern.
+func isLiteral(pattern string) bool {
+ return !strings.ContainsAny(pattern, `\*?[]`)
+}
+
+// traceLocation represents the setting of the -log_backtrace_at flag.
+type traceLocation struct {
+ file string
+ line int
+}
+
+// isSet reports whether the trace location has been specified.
+// logging.mu is held.
+func (t *traceLocation) isSet() bool {
+ return t.line > 0
+}
+
+// match reports whether the specified file and line matches the trace location.
+// The argument file name is the full path, not the basename specified in the flag.
+// logging.mu is held.
+func (t *traceLocation) match(file string, line int) bool {
+ if t.line != line {
+ return false
+ }
+ if i := strings.LastIndex(file, "/"); i >= 0 {
+ file = file[i+1:]
+ }
+ return t.file == file
+}
+
+func (t *traceLocation) String() string {
+ // Lock because the type is not atomic. TODO: clean this up.
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ return fmt.Sprintf("%s:%d", t.file, t.line)
+}
+
+// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the
+// struct is not exported
+func (t *traceLocation) Get() interface{} {
+ return nil
+}
+
+var errTraceSyntax = errors.New("syntax error: expect file.go:234")
+
+// Set will sets backtrace value
+// Syntax: -log_backtrace_at=gopherflakes.go:234
+// Note that unlike vmodule the file extension is included here.
+func (t *traceLocation) Set(value string) error {
+ if value == "" {
+ // Unset.
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ t.line = 0
+ t.file = ""
+ return nil
+ }
+ fields := strings.Split(value, ":")
+ if len(fields) != 2 {
+ return errTraceSyntax
+ }
+ file, line := fields[0], fields[1]
+ if !strings.Contains(file, ".") {
+ return errTraceSyntax
+ }
+ v, err := strconv.Atoi(line)
+ if err != nil {
+ return errTraceSyntax
+ }
+ if v <= 0 {
+ return errors.New("negative or zero value for level")
+ }
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ t.line = v
+ t.file = file
+ return nil
+}
+
+// flushSyncWriter is the interface satisfied by logging destinations.
+type flushSyncWriter interface {
+ Flush() error
+ Sync() error
+ io.Writer
+}
+
+var logging loggingT
+var commandLine flag.FlagSet
+
+// init sets up the defaults and creates command line flags.
+func init() {
+ commandLine.StringVar(&logging.logDir, "log_dir", "", "If non-empty, write log files in this directory (no effect when -logtostderr=true)")
+ commandLine.StringVar(&logging.logFile, "log_file", "", "If non-empty, use this log file (no effect when -logtostderr=true)")
+ commandLine.Uint64Var(&logging.logFileMaxSizeMB, "log_file_max_size", 1800,
+ "Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. "+
+ "If the value is 0, the maximum file size is unlimited.")
+ commandLine.BoolVar(&logging.toStderr, "logtostderr", true, "log to standard error instead of files")
+ commandLine.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files (no effect when -logtostderr=true)")
+ logging.setVState(0, nil, false)
+ commandLine.Var(&logging.verbosity, "v", "number for the log level verbosity")
+ commandLine.BoolVar(&logging.addDirHeader, "add_dir_header", false, "If true, adds the file directory to the header of the log messages")
+ commandLine.BoolVar(&logging.skipHeaders, "skip_headers", false, "If true, avoid header prefixes in the log messages")
+ commandLine.BoolVar(&logging.oneOutput, "one_output", false, "If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true)")
+ commandLine.BoolVar(&logging.skipLogHeaders, "skip_log_headers", false, "If true, avoid headers when opening log files (no effect when -logtostderr=true)")
+ logging.stderrThreshold = severityValue{
+ Severity: severity.ErrorLog, // Default stderrThreshold is ERROR.
+ }
+ commandLine.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=false)")
+ commandLine.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging")
+ commandLine.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace")
+
+ logging.settings.contextualLoggingEnabled = true
+ logging.flushD = newFlushDaemon(logging.lockAndFlushAll, nil)
+}
+
+// InitFlags is for explicitly initializing the flags.
+// It may get called repeatedly for different flagsets, but not
+// twice for the same one. May get called concurrently
+// to other goroutines using klog. However, only some flags
+// may get set concurrently (see implementation).
+func InitFlags(flagset *flag.FlagSet) {
+ if flagset == nil {
+ flagset = flag.CommandLine
+ }
+
+ commandLine.VisitAll(func(f *flag.Flag) {
+ flagset.Var(f.Value, f.Name, f.Usage)
+ })
+}
+
+// Flush flushes all pending log I/O.
+func Flush() {
+ logging.lockAndFlushAll()
+}
+
+// settings collects global settings.
+type settings struct {
+ // contextualLoggingEnabled controls whether contextual logging is
+ // active. Disabling it may have some small performance benefit.
+ contextualLoggingEnabled bool
+
+ // logger is the global Logger chosen by users of klog, nil if
+ // none is available.
+ logger *Logger
+
+ // loggerOptions contains the options that were supplied for
+ // globalLogger.
+ loggerOptions loggerOptions
+
+ // Boolean flags. Not handled atomically because the flag.Value interface
+ // does not let us avoid the =true, and that shorthand is necessary for
+ // compatibility. TODO: does this matter enough to fix? Seems unlikely.
+ toStderr bool // The -logtostderr flag.
+ alsoToStderr bool // The -alsologtostderr flag.
+
+ // Level flag. Handled atomically.
+ stderrThreshold severityValue // The -stderrthreshold flag.
+
+ // Access to all of the following fields must be protected via a mutex.
+
+ // file holds writer for each of the log types.
+ file [severity.NumSeverity]flushSyncWriter
+ // flushInterval is the interval for periodic flushing. If zero,
+ // the global default will be used.
+ flushInterval time.Duration
+
+ // filterLength stores the length of the vmodule filter chain. If greater
+ // than zero, it means vmodule is enabled. It may be read safely
+ // using sync.LoadInt32, but is only modified under mu.
+ filterLength int32
+ // traceLocation is the state of the -log_backtrace_at flag.
+ traceLocation traceLocation
+ // These flags are modified only under lock, although verbosity may be fetched
+ // safely using atomic.LoadInt32.
+ vmodule moduleSpec // The state of the -vmodule flag.
+ verbosity Level // V logging level, the value of the -v flag/
+
+ // If non-empty, overrides the choice of directory in which to write logs.
+ // See createLogDirs for the full list of possible destinations.
+ logDir string
+
+ // If non-empty, specifies the path of the file to write logs. mutually exclusive
+ // with the log_dir option.
+ logFile string
+
+ // When logFile is specified, this limiter makes sure the logFile won't exceeds a certain size. When exceeds, the
+ // logFile will be cleaned up. If this value is 0, no size limitation will be applied to logFile.
+ logFileMaxSizeMB uint64
+
+ // If true, do not add the prefix headers, useful when used with SetOutput
+ skipHeaders bool
+
+ // If true, do not add the headers to log files
+ skipLogHeaders bool
+
+ // If true, add the file directory to the header
+ addDirHeader bool
+
+ // If true, messages will not be propagated to lower severity log levels
+ oneOutput bool
+
+ // If set, all output will be filtered through the filter.
+ filter LogFilter
+}
+
+// deepCopy creates a copy that doesn't share anything with the original
+// instance.
+func (s settings) deepCopy() settings {
+ // vmodule is a slice and would be shared, so we have copy it.
+ filter := make([]modulePat, len(s.vmodule.filter))
+ for i := range s.vmodule.filter {
+ filter[i] = s.vmodule.filter[i]
+ }
+ s.vmodule.filter = filter
+
+ return s
+}
+
+// loggingT collects all the global state of the logging setup.
+type loggingT struct {
+ settings
+
+ // bufferCache maintains the free list. It uses its own mutex
+ // so buffers can be grabbed and printed to without holding the main lock,
+ // for better parallelization.
+ bufferCache buffer.Buffers
+
+ // flushD holds a flushDaemon that frequently flushes log file buffers.
+ // Uses its own mutex.
+ flushD *flushDaemon
+
+ // mu protects the remaining elements of this structure and the fields
+ // in settingsT which need a mutex lock.
+ mu sync.Mutex
+
+ // pcs is used in V to avoid an allocation when computing the caller's PC.
+ pcs [1]uintptr
+ // vmap is a cache of the V Level for each V() call site, identified by PC.
+ // It is wiped whenever the vmodule flag changes state.
+ vmap map[uintptr]Level
+}
+
+// setVState sets a consistent state for V logging.
+// l.mu is held.
+func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) {
+ // Turn verbosity off so V will not fire while we are in transition.
+ l.verbosity.set(0)
+ // Ditto for filter length.
+ atomic.StoreInt32(&l.filterLength, 0)
+
+ // Set the new filters and wipe the pc->Level map if the filter has changed.
+ if setFilter {
+ l.vmodule.filter = filter
+ l.vmap = make(map[uintptr]Level)
+ }
+
+ // Things are consistent now, so enable filtering and verbosity.
+ // They are enabled in order opposite to that in V.
+ atomic.StoreInt32(&l.filterLength, int32(len(filter)))
+ l.verbosity.set(verbosity)
+}
+
+var timeNow = time.Now // Stubbed out for testing.
+
+// CaptureState gathers information about all current klog settings.
+// The result can be used to restore those settings.
+func CaptureState() State {
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ return &state{
+ settings: logging.settings.deepCopy(),
+ flushDRunning: logging.flushD.isRunning(),
+ maxSize: MaxSize,
+ }
+}
+
+// State stores a snapshot of klog settings. It gets created with CaptureState
+// and can be used to restore the entire state. Modifying individual settings
+// is supported via the command line flags.
+type State interface {
+ // Restore restore the entire state. It may get called more than once.
+ Restore()
+}
+
+type state struct {
+ settings
+
+ flushDRunning bool
+ maxSize uint64
+}
+
+func (s *state) Restore() {
+ // This needs to be done before mutex locking.
+ if s.flushDRunning && !logging.flushD.isRunning() {
+ // This is not quite accurate: StartFlushDaemon might
+ // have been called with some different interval.
+ interval := s.flushInterval
+ if interval == 0 {
+ interval = flushInterval
+ }
+ logging.flushD.run(interval)
+ } else if !s.flushDRunning && logging.flushD.isRunning() {
+ logging.flushD.stop()
+ }
+
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+
+ logging.settings = s.settings
+ logging.setVState(s.verbosity, s.vmodule.filter, true)
+ MaxSize = s.maxSize
+}
+
+/*
+header formats a log header as defined by the C++ implementation.
+It returns a buffer containing the formatted header and the user's file and line number.
+The depth specifies how many stack frames above lives the source line to be identified in the log message.
+
+Log lines have this form:
+
+ Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg...
+
+where the fields are defined as follows:
+
+ L A single character, representing the log level (eg 'I' for INFO)
+ mm The month (zero padded; ie May is '05')
+ dd The day (zero padded)
+ hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds
+ threadid The space-padded thread ID as returned by GetTID()
+ file The file name
+ line The line number
+ msg The user-supplied message
+*/
+func (l *loggingT) header(s severity.Severity, depth int) (*buffer.Buffer, string, int) {
+ _, file, line, ok := runtime.Caller(3 + depth)
+ if !ok {
+ file = "???"
+ line = 1
+ } else {
+ if slash := strings.LastIndex(file, "/"); slash >= 0 {
+ path := file
+ file = path[slash+1:]
+ if l.addDirHeader {
+ if dirsep := strings.LastIndex(path[:slash], "/"); dirsep >= 0 {
+ file = path[dirsep+1:]
+ }
+ }
+ }
+ }
+ return l.formatHeader(s, file, line), file, line
+}
+
+// formatHeader formats a log header using the provided file name and line number.
+func (l *loggingT) formatHeader(s severity.Severity, file string, line int) *buffer.Buffer {
+ buf := l.bufferCache.GetBuffer()
+ if l.skipHeaders {
+ return buf
+ }
+ now := timeNow()
+ buf.FormatHeader(s, file, line, now)
+ return buf
+}
+
+func (l *loggingT) println(s severity.Severity, logger *logr.Logger, filter LogFilter, args ...interface{}) {
+ l.printlnDepth(s, logger, filter, 1, args...)
+}
+
+func (l *loggingT) printlnDepth(s severity.Severity, logger *logr.Logger, filter LogFilter, depth int, args ...interface{}) {
+ buf, file, line := l.header(s, depth)
+ // if logger is set, we clear the generated header as we rely on the backing
+ // logger implementation to print headers
+ if logger != nil {
+ l.bufferCache.PutBuffer(buf)
+ buf = l.bufferCache.GetBuffer()
+ }
+ if filter != nil {
+ args = filter.Filter(args)
+ }
+ fmt.Fprintln(buf, args...)
+ l.output(s, logger, buf, depth, file, line, false)
+}
+
+func (l *loggingT) print(s severity.Severity, logger *logr.Logger, filter LogFilter, args ...interface{}) {
+ l.printDepth(s, logger, filter, 1, args...)
+}
+
+func (l *loggingT) printDepth(s severity.Severity, logger *logr.Logger, filter LogFilter, depth int, args ...interface{}) {
+ buf, file, line := l.header(s, depth)
+ // if logr is set, we clear the generated header as we rely on the backing
+ // logr implementation to print headers
+ if logger != nil {
+ l.bufferCache.PutBuffer(buf)
+ buf = l.bufferCache.GetBuffer()
+ }
+ if filter != nil {
+ args = filter.Filter(args)
+ }
+ fmt.Fprint(buf, args...)
+ if buf.Len() == 0 || buf.Bytes()[buf.Len()-1] != '\n' {
+ buf.WriteByte('\n')
+ }
+ l.output(s, logger, buf, depth, file, line, false)
+}
+
+func (l *loggingT) printf(s severity.Severity, logger *logr.Logger, filter LogFilter, format string, args ...interface{}) {
+ l.printfDepth(s, logger, filter, 1, format, args...)
+}
+
+func (l *loggingT) printfDepth(s severity.Severity, logger *logr.Logger, filter LogFilter, depth int, format string, args ...interface{}) {
+ buf, file, line := l.header(s, depth)
+ // if logr is set, we clear the generated header as we rely on the backing
+ // logr implementation to print headers
+ if logger != nil {
+ l.bufferCache.PutBuffer(buf)
+ buf = l.bufferCache.GetBuffer()
+ }
+ if filter != nil {
+ format, args = filter.FilterF(format, args)
+ }
+ fmt.Fprintf(buf, format, args...)
+ if buf.Bytes()[buf.Len()-1] != '\n' {
+ buf.WriteByte('\n')
+ }
+ l.output(s, logger, buf, depth, file, line, false)
+}
+
+// printWithFileLine behaves like print but uses the provided file and line number. If
+// alsoLogToStderr is true, the log message always appears on standard error; it
+// will also appear in the log file unless --logtostderr is set.
+func (l *loggingT) printWithFileLine(s severity.Severity, logger *logr.Logger, filter LogFilter, file string, line int, alsoToStderr bool, args ...interface{}) {
+ buf := l.formatHeader(s, file, line)
+ // if logr is set, we clear the generated header as we rely on the backing
+ // logr implementation to print headers
+ if logger != nil {
+ l.bufferCache.PutBuffer(buf)
+ buf = l.bufferCache.GetBuffer()
+ }
+ if filter != nil {
+ args = filter.Filter(args)
+ }
+ fmt.Fprint(buf, args...)
+ if buf.Bytes()[buf.Len()-1] != '\n' {
+ buf.WriteByte('\n')
+ }
+ l.output(s, logger, buf, 2 /* depth */, file, line, alsoToStderr)
+}
+
+// if loggr is specified, will call loggr.Error, otherwise output with logging module.
+func (l *loggingT) errorS(err error, logger *logr.Logger, filter LogFilter, depth int, msg string, keysAndValues ...interface{}) {
+ if filter != nil {
+ msg, keysAndValues = filter.FilterS(msg, keysAndValues)
+ }
+ if logger != nil {
+ logger.WithCallDepth(depth+2).Error(err, msg, keysAndValues...)
+ return
+ }
+ l.printS(err, severity.ErrorLog, depth+1, msg, keysAndValues...)
+}
+
+// if loggr is specified, will call loggr.Info, otherwise output with logging module.
+func (l *loggingT) infoS(logger *logr.Logger, filter LogFilter, depth int, msg string, keysAndValues ...interface{}) {
+ if filter != nil {
+ msg, keysAndValues = filter.FilterS(msg, keysAndValues)
+ }
+ if logger != nil {
+ logger.WithCallDepth(depth+2).Info(msg, keysAndValues...)
+ return
+ }
+ l.printS(nil, severity.InfoLog, depth+1, msg, keysAndValues...)
+}
+
+// printS is called from infoS and errorS if loggr is not specified.
+// set log severity by s
+func (l *loggingT) printS(err error, s severity.Severity, depth int, msg string, keysAndValues ...interface{}) {
+ // Only create a new buffer if we don't have one cached.
+ b := l.bufferCache.GetBuffer()
+ // The message is always quoted, even if it contains line breaks.
+ // If developers want multi-line output, they should use a small, fixed
+ // message and put the multi-line output into a value.
+ b.WriteString(strconv.Quote(msg))
+ if err != nil {
+ serialize.KVListFormat(&b.Buffer, "err", err)
+ }
+ serialize.KVListFormat(&b.Buffer, keysAndValues...)
+ l.printDepth(s, logging.logger, nil, depth+1, &b.Buffer)
+ // Make the buffer available for reuse.
+ l.bufferCache.PutBuffer(b)
+}
+
+// redirectBuffer is used to set an alternate destination for the logs
+type redirectBuffer struct {
+ w io.Writer
+}
+
+func (rb *redirectBuffer) Sync() error {
+ return nil
+}
+
+func (rb *redirectBuffer) Flush() error {
+ return nil
+}
+
+func (rb *redirectBuffer) Write(bytes []byte) (n int, err error) {
+ return rb.w.Write(bytes)
+}
+
+// SetOutput sets the output destination for all severities
+func SetOutput(w io.Writer) {
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ for s := severity.FatalLog; s >= severity.InfoLog; s-- {
+ rb := &redirectBuffer{
+ w: w,
+ }
+ logging.file[s] = rb
+ }
+}
+
+// SetOutputBySeverity sets the output destination for specific severity
+func SetOutputBySeverity(name string, w io.Writer) {
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ sev, ok := severity.ByName(name)
+ if !ok {
+ panic(fmt.Sprintf("SetOutputBySeverity(%q): unrecognized severity name", name))
+ }
+ rb := &redirectBuffer{
+ w: w,
+ }
+ logging.file[sev] = rb
+}
+
+// LogToStderr sets whether to log exclusively to stderr, bypassing outputs
+func LogToStderr(stderr bool) {
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+
+ logging.toStderr = stderr
+}
+
+// output writes the data to the log files and releases the buffer.
+func (l *loggingT) output(s severity.Severity, log *logr.Logger, buf *buffer.Buffer, depth int, file string, line int, alsoToStderr bool) {
+ var isLocked = true
+ l.mu.Lock()
+ defer func() {
+ if isLocked {
+ // Unlock before returning in case that it wasn't done already.
+ l.mu.Unlock()
+ }
+ }()
+
+ if l.traceLocation.isSet() {
+ if l.traceLocation.match(file, line) {
+ buf.Write(dbg.Stacks(false))
+ }
+ }
+ data := buf.Bytes()
+ if log != nil {
+ // TODO: set 'severity' and caller information as structured log info
+ // keysAndValues := []interface{}{"severity", severityName[s], "file", file, "line", line}
+ if s == severity.ErrorLog {
+ logging.logger.WithCallDepth(depth+3).Error(nil, string(data))
+ } else {
+ log.WithCallDepth(depth + 3).Info(string(data))
+ }
+ } else if l.toStderr {
+ os.Stderr.Write(data)
+ } else {
+ if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() {
+ os.Stderr.Write(data)
+ }
+
+ if logging.logFile != "" {
+ // Since we are using a single log file, all of the items in l.file array
+ // will point to the same file, so just use one of them to write data.
+ if l.file[severity.InfoLog] == nil {
+ if err := l.createFiles(severity.InfoLog); err != nil {
+ os.Stderr.Write(data) // Make sure the message appears somewhere.
+ l.exit(err)
+ }
+ }
+ l.file[severity.InfoLog].Write(data)
+ } else {
+ if l.file[s] == nil {
+ if err := l.createFiles(s); err != nil {
+ os.Stderr.Write(data) // Make sure the message appears somewhere.
+ l.exit(err)
+ }
+ }
+
+ if l.oneOutput {
+ l.file[s].Write(data)
+ } else {
+ switch s {
+ case severity.FatalLog:
+ l.file[severity.FatalLog].Write(data)
+ fallthrough
+ case severity.ErrorLog:
+ l.file[severity.ErrorLog].Write(data)
+ fallthrough
+ case severity.WarningLog:
+ l.file[severity.WarningLog].Write(data)
+ fallthrough
+ case severity.InfoLog:
+ l.file[severity.InfoLog].Write(data)
+ }
+ }
+ }
+ }
+ if s == severity.FatalLog {
+ // If we got here via Exit rather than Fatal, print no stacks.
+ if atomic.LoadUint32(&fatalNoStacks) > 0 {
+ l.mu.Unlock()
+ isLocked = false
+ timeoutFlush(ExitFlushTimeout)
+ OsExit(1)
+ }
+ // Dump all goroutine stacks before exiting.
+ // First, make sure we see the trace for the current goroutine on standard error.
+ // If -logtostderr has been specified, the loop below will do that anyway
+ // as the first stack in the full dump.
+ if !l.toStderr {
+ os.Stderr.Write(dbg.Stacks(false))
+ }
+
+ // Write the stack trace for all goroutines to the files.
+ trace := dbg.Stacks(true)
+ logExitFunc = func(error) {} // If we get a write error, we'll still exit below.
+ for log := severity.FatalLog; log >= severity.InfoLog; log-- {
+ if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set.
+ f.Write(trace)
+ }
+ }
+ l.mu.Unlock()
+ isLocked = false
+ timeoutFlush(ExitFlushTimeout)
+ OsExit(255) // C++ uses -1, which is silly because it's anded with 255 anyway.
+ }
+ l.bufferCache.PutBuffer(buf)
+
+ if stats := severityStats[s]; stats != nil {
+ atomic.AddInt64(&stats.lines, 1)
+ atomic.AddInt64(&stats.bytes, int64(len(data)))
+ }
+}
+
+// logExitFunc provides a simple mechanism to override the default behavior
+// of exiting on error. Used in testing and to guarantee we reach a required exit
+// for fatal logs. Instead, exit could be a function rather than a method but that
+// would make its use clumsier.
+var logExitFunc func(error)
+
+// exit is called if there is trouble creating or writing log files.
+// It flushes the logs and exits the program; there's no point in hanging around.
+// l.mu is held.
+func (l *loggingT) exit(err error) {
+ fmt.Fprintf(os.Stderr, "log: exiting because of error: %s\n", err)
+ // If logExitFunc is set, we do that instead of exiting.
+ if logExitFunc != nil {
+ logExitFunc(err)
+ return
+ }
+ l.flushAll()
+ OsExit(2)
+}
+
+// syncBuffer joins a bufio.Writer to its underlying file, providing access to the
+// file's Sync method and providing a wrapper for the Write method that provides log
+// file rotation. There are conflicting methods, so the file cannot be embedded.
+// l.mu is held for all its methods.
+type syncBuffer struct {
+ logger *loggingT
+ *bufio.Writer
+ file *os.File
+ sev severity.Severity
+ nbytes uint64 // The number of bytes written to this file
+ maxbytes uint64 // The max number of bytes this syncBuffer.file can hold before cleaning up.
+}
+
+func (sb *syncBuffer) Sync() error {
+ return sb.file.Sync()
+}
+
+// CalculateMaxSize returns the real max size in bytes after considering the default max size and the flag options.
+func CalculateMaxSize() uint64 {
+ if logging.logFile != "" {
+ if logging.logFileMaxSizeMB == 0 {
+ // If logFileMaxSizeMB is zero, we don't have limitations on the log size.
+ return math.MaxUint64
+ }
+ // Flag logFileMaxSizeMB is in MB for user convenience.
+ return logging.logFileMaxSizeMB * 1024 * 1024
+ }
+ // If "log_file" flag is not specified, the target file (sb.file) will be cleaned up when reaches a fixed size.
+ return MaxSize
+}
+
+func (sb *syncBuffer) Write(p []byte) (n int, err error) {
+ if sb.nbytes+uint64(len(p)) >= sb.maxbytes {
+ if err := sb.rotateFile(time.Now(), false); err != nil {
+ sb.logger.exit(err)
+ }
+ }
+ n, err = sb.Writer.Write(p)
+ sb.nbytes += uint64(n)
+ if err != nil {
+ sb.logger.exit(err)
+ }
+ return
+}
+
+// rotateFile closes the syncBuffer's file and starts a new one.
+// The startup argument indicates whether this is the initial startup of klog.
+// If startup is true, existing files are opened for appending instead of truncated.
+func (sb *syncBuffer) rotateFile(now time.Time, startup bool) error {
+ if sb.file != nil {
+ sb.Flush()
+ sb.file.Close()
+ }
+ var err error
+ sb.file, _, err = create(severity.Name[sb.sev], now, startup)
+ if err != nil {
+ return err
+ }
+ if startup {
+ fileInfo, err := sb.file.Stat()
+ if err != nil {
+ return fmt.Errorf("file stat could not get fileinfo: %v", err)
+ }
+ // init file size
+ sb.nbytes = uint64(fileInfo.Size())
+ } else {
+ sb.nbytes = 0
+ }
+ sb.Writer = bufio.NewWriterSize(sb.file, bufferSize)
+
+ if sb.logger.skipLogHeaders {
+ return nil
+ }
+
+ // Write header.
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05"))
+ fmt.Fprintf(&buf, "Running on machine: %s\n", host)
+ fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH)
+ fmt.Fprintf(&buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n")
+ n, err := sb.file.Write(buf.Bytes())
+ sb.nbytes += uint64(n)
+ return err
+}
+
+// bufferSize sizes the buffer associated with each log file. It's large
+// so that log records can accumulate without the logging thread blocking
+// on disk I/O. The flushDaemon will block instead.
+const bufferSize = 256 * 1024
+
+// createFiles creates all the log files for severity from sev down to infoLog.
+// l.mu is held.
+func (l *loggingT) createFiles(sev severity.Severity) error {
+ interval := l.flushInterval
+ if interval == 0 {
+ interval = flushInterval
+ }
+ l.flushD.run(interval)
+ now := time.Now()
+ // Files are created in decreasing severity order, so as soon as we find one
+ // has already been created, we can stop.
+ for s := sev; s >= severity.InfoLog && l.file[s] == nil; s-- {
+ sb := &syncBuffer{
+ logger: l,
+ sev: s,
+ maxbytes: CalculateMaxSize(),
+ }
+ if err := sb.rotateFile(now, true); err != nil {
+ return err
+ }
+ l.file[s] = sb
+ }
+ return nil
+}
+
+const flushInterval = 5 * time.Second
+
+// flushDaemon periodically flushes the log file buffers.
+type flushDaemon struct {
+ mu sync.Mutex
+ clock clock.WithTicker
+ flush func()
+ stopC chan struct{}
+ stopDone chan struct{}
+}
+
+// newFlushDaemon returns a new flushDaemon. If the passed clock is nil, a
+// clock.RealClock is used.
+func newFlushDaemon(flush func(), tickClock clock.WithTicker) *flushDaemon {
+ if tickClock == nil {
+ tickClock = clock.RealClock{}
+ }
+ return &flushDaemon{
+ flush: flush,
+ clock: tickClock,
+ }
+}
+
+// run starts a goroutine that periodically calls the daemons flush function.
+// Calling run on an already running daemon will have no effect.
+func (f *flushDaemon) run(interval time.Duration) {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+
+ if f.stopC != nil { // daemon already running
+ return
+ }
+
+ f.stopC = make(chan struct{}, 1)
+ f.stopDone = make(chan struct{}, 1)
+
+ ticker := f.clock.NewTicker(interval)
+ go func() {
+ defer ticker.Stop()
+ defer func() { f.stopDone <- struct{}{} }()
+ for {
+ select {
+ case <-ticker.C():
+ f.flush()
+ case <-f.stopC:
+ f.flush()
+ return
+ }
+ }
+ }()
+}
+
+// stop stops the running flushDaemon and waits until the daemon has shut down.
+// Calling stop on a daemon that isn't running will have no effect.
+func (f *flushDaemon) stop() {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+
+ if f.stopC == nil { // daemon not running
+ return
+ }
+
+ f.stopC <- struct{}{}
+ <-f.stopDone
+
+ f.stopC = nil
+ f.stopDone = nil
+}
+
+// isRunning returns true if the flush daemon is running.
+func (f *flushDaemon) isRunning() bool {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ return f.stopC != nil
+}
+
+// StopFlushDaemon stops the flush daemon, if running, and flushes once.
+// This prevents klog from leaking goroutines on shutdown. After stopping
+// the daemon, you can still manually flush buffers again by calling Flush().
+func StopFlushDaemon() {
+ logging.flushD.stop()
+}
+
+// StartFlushDaemon ensures that the flush daemon runs with the given delay
+// between flush calls. If it is already running, it gets restarted.
+func StartFlushDaemon(interval time.Duration) {
+ StopFlushDaemon()
+ logging.flushD.run(interval)
+}
+
+// lockAndFlushAll is like flushAll but locks l.mu first.
+func (l *loggingT) lockAndFlushAll() {
+ l.mu.Lock()
+ l.flushAll()
+ l.mu.Unlock()
+}
+
+// flushAll flushes all the logs and attempts to "sync" their data to disk.
+// l.mu is held.
+func (l *loggingT) flushAll() {
+ // Flush from fatal down, in case there's trouble flushing.
+ for s := severity.FatalLog; s >= severity.InfoLog; s-- {
+ file := l.file[s]
+ if file != nil {
+ file.Flush() // ignore error
+ file.Sync() // ignore error
+ }
+ }
+ if logging.loggerOptions.flush != nil {
+ logging.loggerOptions.flush()
+ }
+}
+
+// CopyStandardLogTo arranges for messages written to the Go "log" package's
+// default logs to also appear in the Google logs for the named and lower
+// severities. Subsequent changes to the standard log's default output location
+// or format may break this behavior.
+//
+// Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not
+// recognized, CopyStandardLogTo panics.
+func CopyStandardLogTo(name string) {
+ sev, ok := severity.ByName(name)
+ if !ok {
+ panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name))
+ }
+ // Set a log format that captures the user's file and line:
+ // d.go:23: message
+ stdLog.SetFlags(stdLog.Lshortfile)
+ stdLog.SetOutput(logBridge(sev))
+}
+
+// logBridge provides the Write method that enables CopyStandardLogTo to connect
+// Go's standard logs to the logs provided by this package.
+type logBridge severity.Severity
+
+// Write parses the standard logging line and passes its components to the
+// logger for severity(lb).
+func (lb logBridge) Write(b []byte) (n int, err error) {
+ var (
+ file = "???"
+ line = 1
+ text string
+ )
+ // Split "d.go:23: message" into "d.go", "23", and "message".
+ if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 {
+ text = fmt.Sprintf("bad log format: %s", b)
+ } else {
+ file = string(parts[0])
+ text = string(parts[2][1:]) // skip leading space
+ line, err = strconv.Atoi(string(parts[1]))
+ if err != nil {
+ text = fmt.Sprintf("bad line number: %s", b)
+ line = 1
+ }
+ }
+ // printWithFileLine with alsoToStderr=true, so standard log messages
+ // always appear on standard error.
+ logging.printWithFileLine(severity.Severity(lb), logging.logger, logging.filter, file, line, true, text)
+ return len(b), nil
+}
+
+// setV computes and remembers the V level for a given PC
+// when vmodule is enabled.
+// File pattern matching takes the basename of the file, stripped
+// of its .go suffix, and uses filepath.Match, which is a little more
+// general than the *? matching used in C++.
+// l.mu is held.
+func (l *loggingT) setV(pc uintptr) Level {
+ fn := runtime.FuncForPC(pc)
+ file, _ := fn.FileLine(pc)
+ // The file is something like /a/b/c/d.go. We want just the d.
+ if strings.HasSuffix(file, ".go") {
+ file = file[:len(file)-3]
+ }
+ if slash := strings.LastIndex(file, "/"); slash >= 0 {
+ file = file[slash+1:]
+ }
+ for _, filter := range l.vmodule.filter {
+ if filter.match(file) {
+ l.vmap[pc] = filter.level
+ return filter.level
+ }
+ }
+ l.vmap[pc] = 0
+ return 0
+}
+
+// Verbose is a boolean type that implements Infof (like Printf) etc.
+// See the documentation of V for more information.
+type Verbose struct {
+ enabled bool
+ logr *logr.Logger
+}
+
+func newVerbose(level Level, b bool) Verbose {
+ if logging.logger == nil {
+ return Verbose{b, nil}
+ }
+ v := logging.logger.V(int(level))
+ return Verbose{b, &v}
+}
+
+// V reports whether verbosity at the call site is at least the requested level.
+// The returned value is a struct of type Verbose, which implements Info, Infoln
+// and Infof. These methods will write to the Info log if called.
+// Thus, one may write either
+//
+// if klog.V(2).Enabled() { klog.Info("log this") }
+//
+// or
+//
+// klog.V(2).Info("log this")
+//
+// The second form is shorter but the first is cheaper if logging is off because it does
+// not evaluate its arguments.
+//
+// Whether an individual call to V generates a log record depends on the setting of
+// the -v and -vmodule flags; both are off by default. The V call will log if its level
+// is less than or equal to the value of the -v flag, or alternatively if its level is
+// less than or equal to the value of the -vmodule pattern matching the source file
+// containing the call.
+func V(level Level) Verbose {
+ // This function tries hard to be cheap unless there's work to do.
+ // The fast path is two atomic loads and compares.
+
+ // Here is a cheap but safe test to see if V logging is enabled globally.
+ if logging.verbosity.get() >= level {
+ return newVerbose(level, true)
+ }
+
+ // It's off globally but vmodule may still be set.
+ // Here is another cheap but safe test to see if vmodule is enabled.
+ if atomic.LoadInt32(&logging.filterLength) > 0 {
+ // Now we need a proper lock to use the logging structure. The pcs field
+ // is shared so we must lock before accessing it. This is fairly expensive,
+ // but if V logging is enabled we're slow anyway.
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ if runtime.Callers(2, logging.pcs[:]) == 0 {
+ return newVerbose(level, false)
+ }
+ // runtime.Callers returns "return PCs", but we want
+ // to look up the symbolic information for the call,
+ // so subtract 1 from the PC. runtime.CallersFrames
+ // would be cleaner, but allocates.
+ pc := logging.pcs[0] - 1
+ v, ok := logging.vmap[pc]
+ if !ok {
+ v = logging.setV(pc)
+ }
+ return newVerbose(level, v >= level)
+ }
+ return newVerbose(level, false)
+}
+
+// Enabled will return true if this log level is enabled, guarded by the value
+// of v.
+// See the documentation of V for usage.
+func (v Verbose) Enabled() bool {
+ return v.enabled
+}
+
+// Info is equivalent to the global Info function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) Info(args ...interface{}) {
+ if v.enabled {
+ logging.print(severity.InfoLog, v.logr, logging.filter, args...)
+ }
+}
+
+// InfoDepth is equivalent to the global InfoDepth function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) InfoDepth(depth int, args ...interface{}) {
+ if v.enabled {
+ logging.printDepth(severity.InfoLog, v.logr, logging.filter, depth, args...)
+ }
+}
+
+// Infoln is equivalent to the global Infoln function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) Infoln(args ...interface{}) {
+ if v.enabled {
+ logging.println(severity.InfoLog, v.logr, logging.filter, args...)
+ }
+}
+
+// InfolnDepth is equivalent to the global InfolnDepth function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) InfolnDepth(depth int, args ...interface{}) {
+ if v.enabled {
+ logging.printlnDepth(severity.InfoLog, v.logr, logging.filter, depth, args...)
+ }
+}
+
+// Infof is equivalent to the global Infof function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) Infof(format string, args ...interface{}) {
+ if v.enabled {
+ logging.printf(severity.InfoLog, v.logr, logging.filter, format, args...)
+ }
+}
+
+// InfofDepth is equivalent to the global InfofDepth function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) InfofDepth(depth int, format string, args ...interface{}) {
+ if v.enabled {
+ logging.printfDepth(severity.InfoLog, v.logr, logging.filter, depth, format, args...)
+ }
+}
+
+// InfoS is equivalent to the global InfoS function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) InfoS(msg string, keysAndValues ...interface{}) {
+ if v.enabled {
+ logging.infoS(v.logr, logging.filter, 0, msg, keysAndValues...)
+ }
+}
+
+// InfoSDepth acts as InfoS but uses depth to determine which call frame to log.
+// InfoSDepth(0, "msg") is the same as InfoS("msg").
+func InfoSDepth(depth int, msg string, keysAndValues ...interface{}) {
+ logging.infoS(logging.logger, logging.filter, depth, msg, keysAndValues...)
+}
+
+// InfoSDepth is equivalent to the global InfoSDepth function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) InfoSDepth(depth int, msg string, keysAndValues ...interface{}) {
+ if v.enabled {
+ logging.infoS(v.logr, logging.filter, depth, msg, keysAndValues...)
+ }
+}
+
+// Deprecated: Use ErrorS instead.
+func (v Verbose) Error(err error, msg string, args ...interface{}) {
+ if v.enabled {
+ logging.errorS(err, v.logr, logging.filter, 0, msg, args...)
+ }
+}
+
+// ErrorS is equivalent to the global Error function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) ErrorS(err error, msg string, keysAndValues ...interface{}) {
+ if v.enabled {
+ logging.errorS(err, v.logr, logging.filter, 0, msg, keysAndValues...)
+ }
+}
+
+// Info logs to the INFO log.
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Info(args ...interface{}) {
+ logging.print(severity.InfoLog, logging.logger, logging.filter, args...)
+}
+
+// InfoDepth acts as Info but uses depth to determine which call frame to log.
+// InfoDepth(0, "msg") is the same as Info("msg").
+func InfoDepth(depth int, args ...interface{}) {
+ logging.printDepth(severity.InfoLog, logging.logger, logging.filter, depth, args...)
+}
+
+// Infoln logs to the INFO log.
+// Arguments are handled in the manner of fmt.Println; a newline is always appended.
+func Infoln(args ...interface{}) {
+ logging.println(severity.InfoLog, logging.logger, logging.filter, args...)
+}
+
+// InfolnDepth acts as Infoln but uses depth to determine which call frame to log.
+// InfolnDepth(0, "msg") is the same as Infoln("msg").
+func InfolnDepth(depth int, args ...interface{}) {
+ logging.printlnDepth(severity.InfoLog, logging.logger, logging.filter, depth, args...)
+}
+
+// Infof logs to the INFO log.
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Infof(format string, args ...interface{}) {
+ logging.printf(severity.InfoLog, logging.logger, logging.filter, format, args...)
+}
+
+// InfofDepth acts as Infof but uses depth to determine which call frame to log.
+// InfofDepth(0, "msg", args...) is the same as Infof("msg", args...).
+func InfofDepth(depth int, format string, args ...interface{}) {
+ logging.printfDepth(severity.InfoLog, logging.logger, logging.filter, depth, format, args...)
+}
+
+// InfoS structured logs to the INFO log.
+// The msg argument used to add constant description to the log line.
+// The key/value pairs would be join by "=" ; a newline is always appended.
+//
+// Basic examples:
+// >> klog.InfoS("Pod status updated", "pod", "kubedns", "status", "ready")
+// output:
+// >> I1025 00:15:15.525108 1 controller_utils.go:116] "Pod status updated" pod="kubedns" status="ready"
+func InfoS(msg string, keysAndValues ...interface{}) {
+ logging.infoS(logging.logger, logging.filter, 0, msg, keysAndValues...)
+}
+
+// Warning logs to the WARNING and INFO logs.
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Warning(args ...interface{}) {
+ logging.print(severity.WarningLog, logging.logger, logging.filter, args...)
+}
+
+// WarningDepth acts as Warning but uses depth to determine which call frame to log.
+// WarningDepth(0, "msg") is the same as Warning("msg").
+func WarningDepth(depth int, args ...interface{}) {
+ logging.printDepth(severity.WarningLog, logging.logger, logging.filter, depth, args...)
+}
+
+// Warningln logs to the WARNING and INFO logs.
+// Arguments are handled in the manner of fmt.Println; a newline is always appended.
+func Warningln(args ...interface{}) {
+ logging.println(severity.WarningLog, logging.logger, logging.filter, args...)
+}
+
+// WarninglnDepth acts as Warningln but uses depth to determine which call frame to log.
+// WarninglnDepth(0, "msg") is the same as Warningln("msg").
+func WarninglnDepth(depth int, args ...interface{}) {
+ logging.printlnDepth(severity.WarningLog, logging.logger, logging.filter, depth, args...)
+}
+
+// Warningf logs to the WARNING and INFO logs.
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Warningf(format string, args ...interface{}) {
+ logging.printf(severity.WarningLog, logging.logger, logging.filter, format, args...)
+}
+
+// WarningfDepth acts as Warningf but uses depth to determine which call frame to log.
+// WarningfDepth(0, "msg", args...) is the same as Warningf("msg", args...).
+func WarningfDepth(depth int, format string, args ...interface{}) {
+ logging.printfDepth(severity.WarningLog, logging.logger, logging.filter, depth, format, args...)
+}
+
+// Error logs to the ERROR, WARNING, and INFO logs.
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Error(args ...interface{}) {
+ logging.print(severity.ErrorLog, logging.logger, logging.filter, args...)
+}
+
+// ErrorDepth acts as Error but uses depth to determine which call frame to log.
+// ErrorDepth(0, "msg") is the same as Error("msg").
+func ErrorDepth(depth int, args ...interface{}) {
+ logging.printDepth(severity.ErrorLog, logging.logger, logging.filter, depth, args...)
+}
+
+// Errorln logs to the ERROR, WARNING, and INFO logs.
+// Arguments are handled in the manner of fmt.Println; a newline is always appended.
+func Errorln(args ...interface{}) {
+ logging.println(severity.ErrorLog, logging.logger, logging.filter, args...)
+}
+
+// ErrorlnDepth acts as Errorln but uses depth to determine which call frame to log.
+// ErrorlnDepth(0, "msg") is the same as Errorln("msg").
+func ErrorlnDepth(depth int, args ...interface{}) {
+ logging.printlnDepth(severity.ErrorLog, logging.logger, logging.filter, depth, args...)
+}
+
+// Errorf logs to the ERROR, WARNING, and INFO logs.
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Errorf(format string, args ...interface{}) {
+ logging.printf(severity.ErrorLog, logging.logger, logging.filter, format, args...)
+}
+
+// ErrorfDepth acts as Errorf but uses depth to determine which call frame to log.
+// ErrorfDepth(0, "msg", args...) is the same as Errorf("msg", args...).
+func ErrorfDepth(depth int, format string, args ...interface{}) {
+ logging.printfDepth(severity.ErrorLog, logging.logger, logging.filter, depth, format, args...)
+}
+
+// ErrorS structured logs to the ERROR, WARNING, and INFO logs.
+// the err argument used as "err" field of log line.
+// The msg argument used to add constant description to the log line.
+// The key/value pairs would be join by "=" ; a newline is always appended.
+//
+// Basic examples:
+// >> klog.ErrorS(err, "Failed to update pod status")
+// output:
+// >> E1025 00:15:15.525108 1 controller_utils.go:114] "Failed to update pod status" err="timeout"
+func ErrorS(err error, msg string, keysAndValues ...interface{}) {
+ logging.errorS(err, logging.logger, logging.filter, 0, msg, keysAndValues...)
+}
+
+// ErrorSDepth acts as ErrorS but uses depth to determine which call frame to log.
+// ErrorSDepth(0, "msg") is the same as ErrorS("msg").
+func ErrorSDepth(depth int, err error, msg string, keysAndValues ...interface{}) {
+ logging.errorS(err, logging.logger, logging.filter, depth, msg, keysAndValues...)
+}
+
+// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs,
+// prints stack trace(s), then calls OsExit(255).
+//
+// Stderr only receives a dump of the current goroutine's stack trace. Log files,
+// if there are any, receive a dump of the stack traces in all goroutines.
+//
+// Callers who want more control over handling of fatal events may instead use a
+// combination of different functions:
+// - some info or error logging function, optionally with a stack trace
+// value generated by github.com/go-logr/lib/dbg.Backtrace
+// - Flush to flush pending log data
+// - panic, os.Exit or returning to the caller with an error
+//
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Fatal(args ...interface{}) {
+ logging.print(severity.FatalLog, logging.logger, logging.filter, args...)
+}
+
+// FatalDepth acts as Fatal but uses depth to determine which call frame to log.
+// FatalDepth(0, "msg") is the same as Fatal("msg").
+func FatalDepth(depth int, args ...interface{}) {
+ logging.printDepth(severity.FatalLog, logging.logger, logging.filter, depth, args...)
+}
+
+// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
+// including a stack trace of all running goroutines, then calls OsExit(255).
+// Arguments are handled in the manner of fmt.Println; a newline is always appended.
+func Fatalln(args ...interface{}) {
+ logging.println(severity.FatalLog, logging.logger, logging.filter, args...)
+}
+
+// FatallnDepth acts as Fatalln but uses depth to determine which call frame to log.
+// FatallnDepth(0, "msg") is the same as Fatalln("msg").
+func FatallnDepth(depth int, args ...interface{}) {
+ logging.printlnDepth(severity.FatalLog, logging.logger, logging.filter, depth, args...)
+}
+
+// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
+// including a stack trace of all running goroutines, then calls OsExit(255).
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Fatalf(format string, args ...interface{}) {
+ logging.printf(severity.FatalLog, logging.logger, logging.filter, format, args...)
+}
+
+// FatalfDepth acts as Fatalf but uses depth to determine which call frame to log.
+// FatalfDepth(0, "msg", args...) is the same as Fatalf("msg", args...).
+func FatalfDepth(depth int, format string, args ...interface{}) {
+ logging.printfDepth(severity.FatalLog, logging.logger, logging.filter, depth, format, args...)
+}
+
+// fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks.
+// It allows Exit and relatives to use the Fatal logs.
+var fatalNoStacks uint32
+
+// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls OsExit(1).
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Exit(args ...interface{}) {
+ atomic.StoreUint32(&fatalNoStacks, 1)
+ logging.print(severity.FatalLog, logging.logger, logging.filter, args...)
+}
+
+// ExitDepth acts as Exit but uses depth to determine which call frame to log.
+// ExitDepth(0, "msg") is the same as Exit("msg").
+func ExitDepth(depth int, args ...interface{}) {
+ atomic.StoreUint32(&fatalNoStacks, 1)
+ logging.printDepth(severity.FatalLog, logging.logger, logging.filter, depth, args...)
+}
+
+// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls OsExit(1).
+func Exitln(args ...interface{}) {
+ atomic.StoreUint32(&fatalNoStacks, 1)
+ logging.println(severity.FatalLog, logging.logger, logging.filter, args...)
+}
+
+// ExitlnDepth acts as Exitln but uses depth to determine which call frame to log.
+// ExitlnDepth(0, "msg") is the same as Exitln("msg").
+func ExitlnDepth(depth int, args ...interface{}) {
+ atomic.StoreUint32(&fatalNoStacks, 1)
+ logging.printlnDepth(severity.FatalLog, logging.logger, logging.filter, depth, args...)
+}
+
+// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls OsExit(1).
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Exitf(format string, args ...interface{}) {
+ atomic.StoreUint32(&fatalNoStacks, 1)
+ logging.printf(severity.FatalLog, logging.logger, logging.filter, format, args...)
+}
+
+// ExitfDepth acts as Exitf but uses depth to determine which call frame to log.
+// ExitfDepth(0, "msg", args...) is the same as Exitf("msg", args...).
+func ExitfDepth(depth int, format string, args ...interface{}) {
+ atomic.StoreUint32(&fatalNoStacks, 1)
+ logging.printfDepth(severity.FatalLog, logging.logger, logging.filter, depth, format, args...)
+}
+
+// LogFilter is a collection of functions that can filter all logging calls,
+// e.g. for sanitization of arguments and prevent accidental leaking of secrets.
+type LogFilter interface {
+ Filter(args []interface{}) []interface{}
+ FilterF(format string, args []interface{}) (string, []interface{})
+ FilterS(msg string, keysAndValues []interface{}) (string, []interface{})
+}
+
+// SetLogFilter installs a filter that is used for all log calls.
+//
+// Modifying the filter is not thread-safe and should be done while no other
+// goroutines invoke log calls, usually during program initialization.
+func SetLogFilter(filter LogFilter) {
+ logging.filter = filter
+}