github.com/datadog/cilium@v1.6.12/pkg/logging/logging.go (about) 1 // Copyright 2016-2017 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package logging 16 17 import ( 18 "bufio" 19 "bytes" 20 "fmt" 21 "log/syslog" 22 "os" 23 "regexp" 24 "sync/atomic" 25 "time" 26 27 "github.com/sirupsen/logrus" 28 logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" 29 ) 30 31 const ( 32 SLevel = "syslog.level" 33 34 Syslog = "syslog" 35 ) 36 37 var ( 38 // DefaultLogger is the base logrus logger. It is different from the logrus 39 // default to avoid external dependencies from writing out unexpectedly 40 DefaultLogger = InitializeDefaultLogger() 41 42 // DefaultLogLevel is the alternative we provide to Debug 43 DefaultLogLevel = logrus.InfoLevel 44 45 // DefaultLogLevelStr is the string representation of DefaultLogLevel. It 46 // is used to allow for injection of the logging level via go's ldflags in 47 // unit tests, as only injection with strings via ldflags is allowed. 48 DefaultLogLevelStr = "info" 49 50 // syslogOpts is the set of supported options for syslog configuration. 51 syslogOpts = map[string]bool{ 52 "syslog.level": true, 53 } 54 55 // syslogLevelMap maps logrus.Level values to syslog.Priority levels. 56 syslogLevelMap = map[logrus.Level]syslog.Priority{ 57 logrus.PanicLevel: syslog.LOG_ALERT, 58 logrus.FatalLevel: syslog.LOG_CRIT, 59 logrus.ErrorLevel: syslog.LOG_ERR, 60 logrus.WarnLevel: syslog.LOG_WARNING, 61 logrus.InfoLevel: syslog.LOG_INFO, 62 logrus.DebugLevel: syslog.LOG_DEBUG, 63 } 64 65 // LevelStringToLogrusLevel maps string representations of logrus.Level into 66 // their corresponding logrus.Level. 67 LevelStringToLogrusLevel = map[string]logrus.Level{ 68 "panic": logrus.PanicLevel, 69 "error": logrus.ErrorLevel, 70 "warning": logrus.WarnLevel, 71 "info": logrus.InfoLevel, 72 "debug": logrus.DebugLevel, 73 } 74 ) 75 76 // InitializeDefaultLogger returns a logrus Logger with a custom text formatter. 77 func InitializeDefaultLogger() *logrus.Logger { 78 logger := logrus.New() 79 logger.Formatter = setupFormatter() 80 logger.SetLevel(LevelStringToLogrusLevel[DefaultLogLevelStr]) 81 return logger 82 } 83 84 // SetupLogging sets up each logging service provided in loggers and configures 85 // each logger with the provided logOpts. 86 func SetupLogging(loggers []string, logOpts map[string]string, tag string, debug bool) error { 87 // Set default logger to output to stdout if no loggers are provided. 88 if len(loggers) == 0 { 89 // TODO: switch to a per-logger version when we upgrade to logrus >1.0.3 90 logrus.SetOutput(os.Stdout) 91 } 92 93 ToggleDebugLogs(debug) 94 95 // always suppress the default logger so libraries don't print things 96 logrus.SetLevel(logrus.PanicLevel) 97 98 // Iterate through all provided loggers and configure them according 99 // to user-provided settings. 100 for _, logger := range loggers { 101 switch logger { 102 case Syslog: 103 valuesToValidate := getLogDriverConfig(Syslog, logOpts) 104 err := validateOpts(Syslog, valuesToValidate, syslogOpts) 105 if err != nil { 106 return err 107 } 108 setupSyslog(valuesToValidate, tag, debug) 109 default: 110 return fmt.Errorf("provided log driver %q is not a supported log driver", logger) 111 } 112 } 113 114 return nil 115 } 116 117 // SetLogLevel sets the log level on DefaultLogger. This logger is, by 118 // convention, the base logger for package specific ones thus setting the level 119 // here impacts the default logging behaviour. 120 // This function is thread-safe when logging, reading DefaultLogger.Level is 121 // not protected this way, however. 122 func SetLogLevel(level logrus.Level) { 123 DefaultLogger.SetLevel(level) 124 } 125 126 // ToggleDebugLogs switches on or off debugging logs. It will select 127 // DefaultLogLevel when turning debug off. 128 // It is thread-safe. 129 func ToggleDebugLogs(debug bool) { 130 if debug { 131 SetLogLevel(logrus.DebugLevel) 132 } else { 133 SetLogLevel(DefaultLogLevel) 134 } 135 } 136 137 // setupSyslog sets up and configures syslog with the provided options in 138 // logOpts. If some options are not provided, sensible defaults are used. 139 func setupSyslog(logOpts map[string]string, tag string, debug bool) { 140 logLevel, ok := logOpts[SLevel] 141 if !ok { 142 if debug { 143 logLevel = "debug" 144 } else { 145 logLevel = "info" 146 } 147 } 148 149 //Validate provided log level. 150 level, err := logrus.ParseLevel(logLevel) 151 if err != nil { 152 DefaultLogger.Fatal(err) 153 } 154 155 DefaultLogger.SetLevel(level) 156 // Create syslog hook. 157 h, err := logrus_syslog.NewSyslogHook("", "", syslogLevelMap[level], tag) 158 if err != nil { 159 DefaultLogger.Fatal(err) 160 } 161 // TODO: switch to a per-logger version when we upgrade to logrus >1.0.3 162 logrus.AddHook(h) 163 } 164 165 // setupFormatter sets up the text formatting for logs output by logrus. 166 func setupFormatter() logrus.Formatter { 167 fileFormat := new(logrus.TextFormatter) 168 fileFormat.DisableTimestamp = true 169 fileFormat.DisableColors = true 170 switch os.Getenv("INITSYSTEM") { 171 case "SYSTEMD": 172 fileFormat.FullTimestamp = true 173 default: 174 fileFormat.TimestampFormat = time.RFC3339 175 } 176 177 // TODO: switch to a per-logger version when we upgrade to logrus >1.0.3 178 return fileFormat 179 } 180 181 // validateOpts iterates through all of the keys in logOpts, and errors out if 182 // the key in logOpts is not a key in supportedOpts. 183 func validateOpts(logDriver string, logOpts map[string]string, supportedOpts map[string]bool) error { 184 for k := range logOpts { 185 if !supportedOpts[k] { 186 return fmt.Errorf("provided configuration value %q is not supported as a logging option for log driver %s", k, logDriver) 187 } 188 } 189 return nil 190 } 191 192 // getLogDriverConfig returns a map containing the key-value pairs that start 193 // with string logDriver from map logOpts. 194 func getLogDriverConfig(logDriver string, logOpts map[string]string) map[string]string { 195 keysToValidate := make(map[string]string) 196 for k, v := range logOpts { 197 ok, err := regexp.MatchString(logDriver+".*", k) 198 if err != nil { 199 DefaultLogger.Fatal(err) 200 } 201 if ok { 202 keysToValidate[k] = v 203 } 204 } 205 return keysToValidate 206 } 207 208 // MultiLine breaks a multi line text into individual log entries and calls the 209 // logging function to log each entry 210 func MultiLine(logFn func(args ...interface{}), output string) { 211 scanner := bufio.NewScanner(bytes.NewReader([]byte(output))) 212 for scanner.Scan() { 213 logFn(scanner.Text()) 214 } 215 } 216 217 // CanLogAt returns whether a log message at the given level would be 218 // logged by the given logger. 219 func CanLogAt(logger *logrus.Logger, level logrus.Level) bool { 220 return GetLevel(logger) >= level 221 } 222 223 // GetLevel returns the log level of the given logger. 224 func GetLevel(logger *logrus.Logger) logrus.Level { 225 return logrus.Level(atomic.LoadUint32((*uint32)(&logger.Level))) 226 }