mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 10:03:42 +08:00 
			
		
		
		
	full diff: - https://github.com/google/certificate-transparency-go/compare/v1.0.21...v1.1.4 - https://github.com/kubernetes/klog/compare/v2.30.0...v2.80.1 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
		
			
				
	
	
		
			1690 lines
		
	
	
		
			56 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1690 lines
		
	
	
		
			56 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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
 | 
						|
}
 |