github.com/zaquestion/lab@v0.25.1/internal/logger/logger.go (about) 1 package logger 2 3 import ( 4 "errors" 5 "io" 6 "log" 7 "os" 8 "runtime" 9 "strconv" 10 "strings" 11 "sync" 12 ) 13 14 // Logger levels available 15 const ( 16 LogLevelNone = iota 17 LogLevelError 18 LogLevelInfo 19 LogLevelDebug 20 ) 21 22 // Logger represents lab's internal logger structure, which has a different 23 // log.Logger for each level, allowing different destination (file or fd) in 24 // different levels and also different prefixes. 25 type Logger struct { 26 level int 27 errorLogger *log.Logger 28 warnLogger *log.Logger 29 infoLogger *log.Logger 30 debugLogger *log.Logger 31 } 32 33 // Internal instance that is used by anyone getting it through GetInstance() 34 var internalLogger *Logger 35 36 // A way to avoid multiple initialization of the same logger 37 var once sync.Once 38 39 // GetInstance returns the default lab internal logger 40 func GetInstance() *Logger { 41 once.Do(func() { 42 internalLogger = &Logger{ 43 // Set INFO as default level. The user can change it 44 level: LogLevelInfo, 45 // Setting Lmsgprefix preffix make the prefix to be printed before 46 // the actual message, but after the LstdFlags (date and time) 47 errorLogger: log.New(os.Stderr, "ERROR: ", log.LstdFlags|log.Lmsgprefix), 48 warnLogger: log.New(os.Stderr, "WARNING: ", log.LstdFlags|log.Lmsgprefix), 49 infoLogger: log.New(os.Stderr, "", log.LstdFlags|log.Lmsgprefix), 50 debugLogger: log.New(os.Stderr, "DEBUG: ", log.LstdFlags|log.Lmsgprefix), 51 } 52 }) 53 return internalLogger 54 } 55 56 // SetLogLevel set the level of the internal logger. 57 // Allowed values are LogLevel{Error,Info,Debug,None}. 58 func (l *Logger) SetLogLevel(level int) error { 59 if !(level >= LogLevelNone && level <= LogLevelDebug) { 60 return errors.New("invalid log level") 61 } 62 l.level = level 63 return nil 64 } 65 66 // LogLevel return de current log level of the internal logger 67 func (l *Logger) LogLevel() int { 68 return l.level 69 } 70 71 // SetStdDest sets what's the desired output stream for the internal log. 72 // It can be any io.Writer value. 73 func (l *Logger) SetStdDest(dest io.Writer) { 74 l.errorLogger.SetOutput(dest) 75 l.warnLogger.SetOutput(dest) 76 l.infoLogger.SetOutput(dest) 77 l.debugLogger.SetOutput(dest) 78 } 79 80 // printKeysAndValues prints the keys and valus, as pairs, passed to those 81 // functions in the way expected by go-retryablehttp LeveledLogger interface 82 func printKeysAndValues(l *log.Logger, keysAndValues ...interface{}) { 83 for i := 0; i < len(keysAndValues)/2; i += 2 { 84 l.Printf("\t%s = %s\n", keysAndValues[i], keysAndValues[i+1]) 85 } 86 } 87 88 // addFileLinePrefix prepend the file name and line number to the message being 89 // printed. 90 func addFileLinePrefix(msg string) string { 91 var file string 92 93 // Using runtime.Caller() with calldepth == 2 is enough for getting the 94 // logger function callers 95 _, filePath, line, ok := runtime.Caller(2) 96 if ok { 97 fileParts := strings.Split(filePath, "/") 98 file = fileParts[len(fileParts)-1] 99 } else { 100 // Not sure if there's a better name or line number for an unknown caller 101 file = "???" 102 line = 0 103 } 104 105 prefix := []string{file, ":", strconv.Itoa(line), ":"} 106 // When called from Error, Warn, Info or Debug(), the Print() used 107 // doesn't know about this additional prefix we're adding, so we 108 // need to add the space between it and the msg ourselves. 109 if len(strings.TrimSpace(msg)) > 0 { 110 prefix = append(prefix, " ") 111 } 112 113 prefixedMsg := append(prefix, msg) 114 return strings.Join(prefixedMsg, "") 115 } 116 117 // Fatal prints the values and exit the program with os.Exit() 118 func (l *Logger) Fatal(values ...interface{}) { 119 values = append([]interface{}{addFileLinePrefix(" ")}, values...) 120 l.errorLogger.Fatal(values...) 121 } 122 123 // Fatalf prints formated strings and exit the program with os.Exit() 124 func (l *Logger) Fatalf(format string, values ...interface{}) { 125 values = append([]interface{}{addFileLinePrefix("")}, values...) 126 l.errorLogger.Fatalf("%s "+format, values...) 127 } 128 129 // Fatalln prints the values in a new line and exit the program with os.Exit() 130 func (l *Logger) Fatalln(values ...interface{}) { 131 values = append([]interface{}{addFileLinePrefix(" ")}, values...) 132 l.errorLogger.Fatalln(values...) 133 } 134 135 // Error prints error messages (prefixed with "ERROR:"). 136 // These parameters match the retryablehttp.LeveledLogger. 137 // Error message are always printed, regardless the log level. 138 func (l *Logger) Error(msg string, keysAndValues ...interface{}) { 139 if l.level >= LogLevelError { 140 l.errorLogger.Print(addFileLinePrefix(msg)) 141 printKeysAndValues(l.errorLogger, keysAndValues...) 142 } 143 } 144 145 // Errorf prints formated error message (prefixed with "ERROR:"). 146 // Error message are always printed, regardless the log level. 147 func (l *Logger) Errorf(format string, values ...interface{}) { 148 if l.level >= LogLevelError { 149 values = append([]interface{}{addFileLinePrefix("")}, values...) 150 l.errorLogger.Printf("%s "+format, values...) 151 } 152 } 153 154 // Errorln prints error values in a new line (prefixed with "ERROR:"). 155 // Error message are always printed, regardless the log level. 156 func (l *Logger) Errorln(values ...interface{}) { 157 if l.level >= LogLevelError { 158 values = append([]interface{}{addFileLinePrefix("")}, values...) 159 l.errorLogger.Println(values...) 160 } 161 } 162 163 // Warn prints warning messages (prefixed with "WARNING:"). 164 // These parameters match the retryablehttp.LeveledLogger. 165 // Warning messages require at least LogLevelInfo level. 166 func (l *Logger) Warn(msg string, keysAndValues ...interface{}) { 167 if l.level >= LogLevelInfo { 168 l.warnLogger.Print(addFileLinePrefix(msg)) 169 printKeysAndValues(l.warnLogger, keysAndValues...) 170 } 171 } 172 173 // Warnf prints formated warning message (prefixed with "WARNING:"). 174 // Warning messages require at least LogLevelInfo level. 175 func (l *Logger) Warnf(format string, values ...interface{}) { 176 if l.level >= LogLevelInfo { 177 values = append([]interface{}{addFileLinePrefix("")}, values...) 178 l.warnLogger.Printf("%s "+format, values...) 179 } 180 } 181 182 // Warnln prints warning values in a new line (prefixed with "WARNING:"). 183 // Warning messages require at least LogLevelInfo level. 184 func (l *Logger) Warnln(values ...interface{}) { 185 if l.level >= LogLevelInfo { 186 values = append([]interface{}{addFileLinePrefix("")}, values...) 187 l.warnLogger.Println(values...) 188 } 189 } 190 191 // Info prints informational messages (prefixed with "INFO:"). 192 // These parameters match the retryablehttp.LeveledLogger. 193 // Info messages require at least LogLevelInfo level. 194 func (l *Logger) Info(msg string, keysAndValues ...interface{}) { 195 if l.level >= LogLevelInfo { 196 l.infoLogger.Print(addFileLinePrefix(msg)) 197 printKeysAndValues(l.infoLogger, keysAndValues...) 198 } 199 } 200 201 // Infof prints formated informational message (prefixed with "INFO:"). 202 // Info messages require at least LogLevelInfo level. 203 func (l *Logger) Infof(format string, values ...interface{}) { 204 if l.level >= LogLevelInfo { 205 values = append([]interface{}{addFileLinePrefix("")}, values...) 206 l.infoLogger.Printf("%s "+format, values...) 207 } 208 } 209 210 // Infoln prints info values in a new line (prefixed with "INFO:"). 211 // Info messages require at least LogLevelInfo level. 212 func (l *Logger) Infoln(values ...interface{}) { 213 if l.level >= LogLevelInfo { 214 values = append([]interface{}{addFileLinePrefix("")}, values...) 215 l.infoLogger.Println(values...) 216 } 217 } 218 219 // Debug prints warning messages (prefixed with "DEBUG:"). 220 // These parameters match the retryablehttp.LeveledLogger. 221 // Debug messages require at least LogLevelDebug level. 222 func (l *Logger) Debug(msg string, keysAndValues ...interface{}) { 223 if l.level >= LogLevelDebug { 224 l.debugLogger.Print(addFileLinePrefix(msg)) 225 printKeysAndValues(l.debugLogger, keysAndValues...) 226 } 227 } 228 229 // Debugf prints formated debug message (prefixed with "DEBUG:"). 230 // Debug messages require at least LogLevelDebug level. 231 func (l *Logger) Debugf(format string, values ...interface{}) { 232 if l.level >= LogLevelDebug { 233 values = append([]interface{}{addFileLinePrefix("")}, values...) 234 l.debugLogger.Printf("%s "+format, values...) 235 } 236 } 237 238 // Debugln prints debug values in a new line (prefixed with "DEBUG:"). 239 // Debug messages require at least LogLevelDebug level. 240 func (l *Logger) Debugln(values ...interface{}) { 241 if l.level >= LogLevelDebug { 242 values = append([]interface{}{addFileLinePrefix("")}, values...) 243 l.debugLogger.Println(values...) 244 } 245 }