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