gopkg.in/essentialkaos/ek.v3@v3.5.1/log/log.go (about) 1 // Package log provides improved logger 2 package log 3 4 // ////////////////////////////////////////////////////////////////////////////////// // 5 // // 6 // Copyright (c) 2009-2016 Essential Kaos // 7 // Essential Kaos Open Source License <http://essentialkaos.com/ekol?en> // 8 // // 9 // ////////////////////////////////////////////////////////////////////////////////// // 10 11 import ( 12 "bufio" 13 "errors" 14 "fmt" 15 "io" 16 "os" 17 "strings" 18 "time" 19 ) 20 21 // ////////////////////////////////////////////////////////////////////////////////// // 22 23 // DEBUG debug messages 24 // INFO info messages 25 // WARN warning messages 26 // ERROR error messages 27 // CRIT critical error messages 28 // AUX unskipable messages (separators, headers, etc...) 29 const ( 30 DEBUG = 0 31 INFO = 1 32 WARN = 2 33 ERROR = 3 34 CRIT = 4 35 AUX = 99 36 ) 37 38 // ////////////////////////////////////////////////////////////////////////////////// // 39 40 // Logger is a basic logger struct 41 type Logger struct { 42 PrefixDebug bool // Prefix for debug messages 43 PrefixInfo bool // Prefix for info messages 44 PrefixWarn bool // Prefix for warning messages 45 PrefixError bool // Prefix for error messages 46 PrefixCrit bool // Prefix for critical error messages 47 48 file string 49 fd *os.File 50 w *bufio.Writer 51 level int 52 perms os.FileMode 53 useBufIO bool 54 } 55 56 // ////////////////////////////////////////////////////////////////////////////////// // 57 58 // Global is global logger struct 59 var Global = &Logger{ 60 PrefixWarn: true, 61 PrefixError: true, 62 PrefixCrit: true, 63 64 level: INFO, 65 } 66 67 // ////////////////////////////////////////////////////////////////////////////////// // 68 69 // PrefixMap is map with messages prefixes 70 var PrefixMap = map[int]string{ 71 DEBUG: "[DEBUG]", 72 INFO: "[INFO]", 73 WARN: "[WARNING]", 74 ERROR: "[ERROR]", 75 CRIT: "[CRITICAL]", 76 } 77 78 // TimeFormat contains format string for time in logs 79 var TimeFormat = "2006/01/02 15:04:05.000" 80 81 // ////////////////////////////////////////////////////////////////////////////////// // 82 83 var logLevelsNames = map[string]int{ 84 "debug": 0, 85 "info": 1, 86 "warn": 2, 87 "warning": 2, 88 "error": 3, 89 "crit": 4, 90 "critical": 4, 91 } 92 93 // ////////////////////////////////////////////////////////////////////////////////// // 94 95 // New creates new logger struct 96 func New(file string, perms os.FileMode) (*Logger, error) { 97 logger := &Logger{ 98 PrefixWarn: true, 99 PrefixCrit: true, 100 PrefixError: true, 101 102 level: INFO, 103 } 104 105 err := logger.Set(file, perms) 106 107 if err != nil { 108 return nil, err 109 } 110 111 return logger, nil 112 } 113 114 // Reopen close file descriptor for global logger and open it again 115 // Useful for log rotation 116 func Reopen() error { 117 return Global.Reopen() 118 } 119 120 // MinLevel defines minimal logging level 121 func MinLevel(level interface{}) error { 122 return Global.MinLevel(level) 123 } 124 125 // Set change global logger output target 126 func Set(file string, perms os.FileMode) error { 127 return Global.Set(file, perms) 128 } 129 130 // EnableBufIO enable buffered I/O 131 func EnableBufIO(interval time.Duration) { 132 Global.EnableBufIO(interval) 133 } 134 135 // Flush write buffered data to file 136 func Flush() error { 137 return Global.Flush() 138 } 139 140 // Print write message to global logger output 141 func Print(level int, f string, a ...interface{}) (int, error) { 142 return Global.Print(level, f, a...) 143 } 144 145 // Debug write debug message to global logger output 146 func Debug(f string, a ...interface{}) (int, error) { 147 return Global.Debug(f, a...) 148 } 149 150 // Info write info message to global logger output 151 func Info(f string, a ...interface{}) (int, error) { 152 return Global.Info(f, a...) 153 } 154 155 // Warn write warning message to global logger output 156 func Warn(f string, a ...interface{}) (int, error) { 157 return Global.Warn(f, a...) 158 } 159 160 // Error write error message to global logger output 161 func Error(f string, a ...interface{}) (int, error) { 162 return Global.Error(f, a...) 163 } 164 165 // Crit write critical message to global logger output 166 func Crit(f string, a ...interface{}) (int, error) { 167 return Global.Crit(f, a...) 168 } 169 170 // Aux write unskipable message (for separators/headers) 171 func Aux(f string, a ...interface{}) (int, error) { 172 return Global.Aux(f, a...) 173 } 174 175 // ////////////////////////////////////////////////////////////////////////////////// // 176 177 // Reopen close file descriptor and open again 178 // Useful for log rotation 179 func (l *Logger) Reopen() error { 180 if l == nil { 181 return errors.New("Logger is nil") 182 } 183 184 if l.fd == nil { 185 return errors.New("Output file is not set") 186 } 187 188 if l.w != nil { 189 l.w.Flush() 190 } 191 192 l.fd.Close() 193 194 return l.Set(l.file, l.perms) 195 } 196 197 // MinLevel defines minimal logging level 198 func (l *Logger) MinLevel(level interface{}) error { 199 if l == nil { 200 return errors.New("Logger is nil") 201 } 202 203 levelCode, err := convertMinLevelValue(level) 204 205 if err != nil { 206 return err 207 } 208 209 switch { 210 case levelCode < DEBUG: 211 levelCode = DEBUG 212 case levelCode > CRIT: 213 levelCode = CRIT 214 } 215 216 l.level = levelCode 217 218 return nil 219 } 220 221 // EnableBufIO enable buffered I/O support 222 func (l *Logger) EnableBufIO(interval time.Duration) { 223 l.useBufIO = true 224 225 if l.fd != nil { 226 l.w = bufio.NewWriter(l.fd) 227 } 228 229 go l.flushDaemon(interval) 230 } 231 232 // Set change logger output target 233 func (l *Logger) Set(file string, perms os.FileMode) error { 234 fd, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_APPEND, perms) 235 236 if err != nil { 237 return err 238 } 239 240 // Flush data if writter exist 241 if l.w != nil { 242 l.w.Flush() 243 l.w = nil 244 } 245 246 if l.fd != nil { 247 l.fd.Close() 248 l.fd = nil 249 } 250 251 l.fd, l.file, l.perms = fd, file, perms 252 253 if l.useBufIO { 254 l.w = bufio.NewWriter(l.fd) 255 } 256 257 return nil 258 } 259 260 // Print write message to logger output 261 func (l *Logger) Print(level int, f string, a ...interface{}) (int, error) { 262 if l == nil { 263 return -1, errors.New("Logger is nil") 264 } 265 266 if l.level > level { 267 return 0, nil 268 } 269 270 var w io.Writer 271 272 if l.fd == nil { 273 switch level { 274 case ERROR, CRIT: 275 w = os.Stderr 276 default: 277 w = os.Stdout 278 } 279 } else { 280 if l.w != nil { 281 w = l.w 282 } else { 283 w = l.fd 284 } 285 } 286 287 var showPrefixes bool 288 289 switch { 290 case level == DEBUG && l.PrefixDebug, 291 level == INFO && l.PrefixInfo, 292 level == WARN && l.PrefixWarn, 293 level == ERROR && l.PrefixError, 294 level == CRIT && l.PrefixCrit: 295 showPrefixes = true 296 } 297 298 if f == "" || f[len(f)-1:] != "\n" { 299 f += "\n" 300 } 301 302 if showPrefixes { 303 return fmt.Fprintf(w, "%s %s %s", getTime(), PrefixMap[level], fmt.Sprintf(f, a...)) 304 } 305 306 return fmt.Fprintf(w, "%s %s", getTime(), fmt.Sprintf(f, a...)) 307 } 308 309 // Flush write buffered data to file 310 func (l *Logger) Flush() error { 311 if l == nil { 312 return errors.New("Logger is nil") 313 } 314 315 if l.w == nil { 316 return nil 317 } 318 319 return l.w.Flush() 320 } 321 322 // Debug write debug message to logger output 323 func (l *Logger) Debug(f string, a ...interface{}) (int, error) { 324 if l == nil { 325 return -1, errors.New("Logger is nil") 326 } 327 328 return l.Print(DEBUG, f, a...) 329 } 330 331 // Info write info message to logger output 332 func (l *Logger) Info(f string, a ...interface{}) (int, error) { 333 if l == nil { 334 return -1, errors.New("Logger is nil") 335 } 336 337 return l.Print(INFO, f, a...) 338 } 339 340 // Warn write warning message to logger output 341 func (l *Logger) Warn(f string, a ...interface{}) (int, error) { 342 if l == nil { 343 return -1, errors.New("Logger is nil") 344 } 345 346 return l.Print(WARN, f, a...) 347 } 348 349 // Error write error message to logger output 350 func (l *Logger) Error(f string, a ...interface{}) (int, error) { 351 if l == nil { 352 return -1, errors.New("Logger is nil") 353 } 354 355 return l.Print(ERROR, f, a...) 356 } 357 358 // Crit write critical message to logger output 359 func (l *Logger) Crit(f string, a ...interface{}) (int, error) { 360 if l == nil { 361 return -1, errors.New("Logger is nil") 362 } 363 364 return l.Print(CRIT, f, a...) 365 } 366 367 // Aux write unskipable message (for separators/headers) 368 func (l *Logger) Aux(f string, a ...interface{}) (int, error) { 369 if l == nil { 370 return -1, errors.New("Logger is nil") 371 } 372 373 return l.Print(AUX, f, a...) 374 } 375 376 // ////////////////////////////////////////////////////////////////////////////////// // 377 378 func (l *Logger) flushDaemon(interval time.Duration) { 379 for range time.NewTicker(interval).C { 380 l.Flush() 381 } 382 } 383 384 // ////////////////////////////////////////////////////////////////////////////////// // 385 386 func getTime() string { 387 return "[ " + time.Now().Format(TimeFormat) + " ]" 388 } 389 390 func convertMinLevelValue(level interface{}) (int, error) { 391 switch level.(type) { 392 393 case int: 394 return level.(int), nil 395 396 case int8: 397 return int(level.(int8)), nil 398 399 case int16: 400 return int(level.(int16)), nil 401 402 case int32: 403 return int(level.(int32)), nil 404 405 case int64: 406 return int(level.(int64)), nil 407 408 case uint: 409 return int(level.(uint)), nil 410 411 case uint8: 412 return int(level.(uint8)), nil 413 414 case uint16: 415 return int(level.(uint16)), nil 416 417 case uint32: 418 return int(level.(uint32)), nil 419 420 case uint64: 421 return int(level.(uint64)), nil 422 423 case float32: 424 return int(level.(float32)), nil 425 426 case float64: 427 return int(level.(float64)), nil 428 429 case string: 430 code, ok := logLevelsNames[strings.ToLower(level.(string))] 431 432 if !ok { 433 return -1, errors.New("Unknown level " + level.(string)) 434 } 435 436 return code, nil 437 } 438 439 return -1, errors.New("Unexpected level type") 440 }