github.com/xwi88/log4go@v0.0.6/log.go (about) 1 package log4go 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "path" 8 "runtime" 9 "strings" 10 "sync" 11 "time" 12 ) 13 14 // LevelFlag log level flags 15 const ( 16 LevelFlagEmergency = "EMERGENCY" 17 LevelFlagAlert = "ALERT" 18 LevelFlagCritical = "CRITICAL" 19 LevelFlagError = "ERROR" 20 LevelFlagWarning = "WARNING" // compatible WARN 21 LevelFlagWarn = "WARN" 22 LevelFlagNotice = "NOTICE" 23 LevelFlagInfo = "INFO" 24 LevelFlagDebug = "DEBUG" 25 ) 26 27 // RFC5424 log message levels. 28 // ref: https://tools.ietf.org/html/draft-ietf-syslog-protocol-23 29 const ( 30 EMERGENCY = iota // Emergency: system is unusable 31 ALERT // Alert: action must be taken immediately 32 CRITICAL // Critical: critical conditions 33 ERROR // Error: error conditions 34 WARNING // Warning: warning conditions 35 NOTICE // Notice: normal but significant condition 36 INFO // Informational: informational messages 37 DEBUG // Debug: debug-level messages 38 ) 39 40 const ( 41 // default size or min size for record channel 42 recordChannelSizeDefault = uint(4096) 43 // default time layout 44 defaultLayout = "2006/01/02 15:04:05" 45 // timestamp with zone info 46 timestampLayout = "2006-01-02T15:04:05.000+0800" 47 ) 48 49 // LevelFlags level Flags set 50 var ( 51 LevelFlags = []string{ 52 LevelFlagEmergency, 53 LevelFlagAlert, 54 LevelFlagCritical, 55 LevelFlagError, 56 LevelFlagWarning, 57 LevelFlagNotice, 58 LevelFlagInfo, 59 LevelFlagDebug, 60 } 61 DefaultLayout = defaultLayout 62 ) 63 64 // default logger 65 var ( 66 loggerDefault *Logger 67 recordPool *sync.Pool 68 recordChannelSize = recordChannelSizeDefault // log chan size 69 ) 70 71 // Record log record 72 type Record struct { 73 level int 74 time string 75 file string 76 msg string 77 } 78 79 func (r *Record) String() string { 80 return fmt.Sprintf("%s [%s] <%s> %s\n", r.time, LevelFlags[r.level], r.file, r.msg) 81 } 82 83 // Writer record writer 84 type Writer interface { 85 Init() error 86 Write(*Record) error 87 } 88 89 // Flusher record flusher 90 type Flusher interface { 91 Flush() error 92 } 93 94 // Rotater record rotater 95 type Rotater interface { 96 Rotate() error 97 SetPathPattern(string) error 98 } 99 100 // Logger logger define 101 type Logger struct { 102 writers []Writer 103 records chan *Record 104 recordsChanSize uint 105 lastTime int64 106 lastTimeStr string 107 108 flushTimer time.Duration // timer to flush logger record to chan 109 rotateTimer time.Duration // timer to rotate logger record for writer 110 111 c chan bool 112 113 layout string 114 level int 115 fullPath bool // show full path, default only show file:line_number 116 withFuncName bool // show caller func name 117 lock sync.RWMutex 118 } 119 120 // NewLogger create the logger 121 func NewLogger() *Logger { 122 if loggerDefault != nil { 123 return loggerDefault 124 } 125 records := make(chan *Record, recordChannelSize) 126 127 return newLoggerWithRecords(records) 128 } 129 130 // newLoggerWithRecords is useful for go test 131 func newLoggerWithRecords(records chan *Record) *Logger { 132 l := new(Logger) 133 l.writers = make([]Writer, 0, 1) // normal least has console writer 134 if l.recordsChanSize == 0 { 135 recordChannelSize = recordChannelSizeDefault 136 } 137 138 l.records = records 139 l.c = make(chan bool, 1) 140 l.level = DEBUG 141 l.layout = DefaultLayout 142 143 go bootstrapLogWriter(l) 144 145 return l 146 } 147 148 // Register register writer 149 // the writer should be register once for writers by kind 150 func (l *Logger) Register(w Writer) { 151 if err := w.Init(); err != nil { 152 panic(err) 153 } 154 155 l.writers = append(l.writers, w) 156 } 157 158 // Close close logger 159 func (l *Logger) Close() { 160 close(l.records) 161 <-l.c 162 163 for _, w := range l.writers { 164 if f, ok := w.(Flusher); ok { 165 if err := f.Flush(); err != nil { 166 log.Println(err) 167 } 168 } 169 } 170 } 171 172 // SetLayout set the logger time layout 173 func (l *Logger) SetLayout(layout string) { 174 l.layout = layout 175 } 176 177 // SetLevel set the logger level 178 func (l *Logger) SetLevel(lvl int) { 179 l.level = lvl 180 } 181 182 // WithFullPath set the logger with full path 183 func (l *Logger) WithFullPath(show bool) { 184 l.fullPath = show 185 } 186 187 // WithFuncName set the logger with func name 188 func (l *Logger) WithFuncName(show bool) { 189 l.withFuncName = show 190 } 191 192 // Debug level debug 193 func (l *Logger) Debug(fmt string, args ...interface{}) { 194 l.deliverRecordToWriter(DEBUG, fmt, args...) 195 } 196 197 // Info level info 198 func (l *Logger) Info(fmt string, args ...interface{}) { 199 l.deliverRecordToWriter(INFO, fmt, args...) 200 } 201 202 // Notice level notice 203 func (l *Logger) Notice(fmt string, args ...interface{}) { 204 l.deliverRecordToWriter(NOTICE, fmt, args...) 205 } 206 207 // Warn level warn 208 func (l *Logger) Warn(fmt string, args ...interface{}) { 209 l.deliverRecordToWriter(WARNING, fmt, args...) 210 } 211 212 // Error level error 213 func (l *Logger) Error(fmt string, args ...interface{}) { 214 l.deliverRecordToWriter(ERROR, fmt, args...) 215 } 216 217 // Critical level critical 218 func (l *Logger) Critical(fmt string, args ...interface{}) { 219 l.deliverRecordToWriter(CRITICAL, fmt, args...) 220 } 221 222 // Alert level alert 223 func (l *Logger) Alert(fmt string, args ...interface{}) { 224 l.deliverRecordToWriter(ALERT, fmt, args...) 225 } 226 227 // Emergency level emergency 228 func (l *Logger) Emergency(fmt string, args ...interface{}) { 229 l.deliverRecordToWriter(EMERGENCY, fmt, args...) 230 } 231 232 func (l *Logger) deliverRecordToWriter(level int, f string, args ...interface{}) { 233 var msg string 234 var fi bytes.Buffer 235 236 if level > l.level { 237 return 238 } 239 240 msg = f 241 sz := len(args) 242 if sz != 0 { 243 if strings.Contains(msg, "%") && !strings.Contains(msg, "%%") { 244 } else { 245 msg += strings.Repeat("%v", len(args)) 246 } 247 } 248 msg = fmt.Sprintf(msg, args...) 249 250 // source code, file and line num 251 pc, file, line, ok := runtime.Caller(2) 252 if ok { 253 fileName := path.Base(file) 254 if l.fullPath { 255 fileName = file 256 } 257 fi.WriteString(fmt.Sprintf("%s:%d", fileName, line)) 258 259 if l.withFuncName { 260 funcName := runtime.FuncForPC(pc).Name() 261 funcName = path.Base(funcName) 262 fi.WriteString(fmt.Sprintf(" %s", funcName)) 263 } 264 } 265 266 // format time 267 now := time.Now() 268 l.lock.Lock() // avoid data race 269 if now.Unix() != l.lastTime { 270 l.lastTime = now.Unix() 271 l.lastTimeStr = now.Format(l.layout) 272 } 273 lastTimeStr := l.lastTimeStr 274 l.lock.Unlock() 275 276 r := recordPool.Get().(*Record) 277 r.msg = msg 278 r.file = fi.String() 279 r.time = lastTimeStr 280 r.level = level 281 282 l.records <- r 283 } 284 285 func bootstrapLogWriter(logger *Logger) { 286 var ( 287 r *Record 288 ok bool 289 ) 290 291 if r, ok = <-logger.records; !ok { 292 logger.c <- true 293 return 294 } 295 296 for _, w := range logger.writers { 297 if err := w.Write(r); err != nil { 298 log.Printf("%v\n", err) 299 } 300 } 301 302 flushTimer := time.NewTimer(logger.flushTimer) 303 rotateTimer := time.NewTimer(logger.rotateTimer) 304 305 for { 306 select { 307 case r, ok = <-logger.records: 308 if !ok { 309 logger.c <- true 310 return 311 } 312 313 for _, w := range logger.writers { 314 if err := w.Write(r); err != nil { 315 log.Printf("%v\n", err) 316 } 317 } 318 319 recordPool.Put(r) 320 321 case <-flushTimer.C: 322 for _, w := range logger.writers { 323 if f, ok := w.(Flusher); ok { 324 if err := f.Flush(); err != nil { 325 log.Printf("%v\n", err) 326 } 327 } 328 } 329 flushTimer.Reset(logger.flushTimer) 330 331 case <-rotateTimer.C: 332 for _, w := range logger.writers { 333 if r, ok := w.(Rotater); ok { 334 if err := r.Rotate(); err != nil { 335 log.Printf("%v\n", err) 336 } 337 } 338 } 339 rotateTimer.Reset(logger.rotateTimer) 340 } 341 } 342 } 343 344 func init() { 345 loggerDefault = NewLogger() 346 loggerDefault.flushTimer = time.Millisecond * 500 347 loggerDefault.rotateTimer = time.Second * 10 348 recordPool = &sync.Pool{New: func() interface{} { 349 return &Record{} 350 }} 351 } 352 353 // Register register writer 354 func Register(w Writer) { 355 loggerDefault.Register(w) 356 } 357 358 // Close close logger 359 func Close() { 360 loggerDefault.Close() 361 } 362 363 // SetLayout set the logger time layout, should call before logger real use 364 func SetLayout(layout string) { 365 loggerDefault.layout = layout 366 } 367 368 // SetLevel set the logger level, should call before logger real use 369 func SetLevel(lvl int) { 370 loggerDefault.level = lvl 371 } 372 373 // WithFullPath set the logger with full path, should call before logger real use 374 func WithFullPath(show bool) { 375 loggerDefault.fullPath = show 376 } 377 378 // WithFuncName set the logger with func name, should call before logger real use 379 func WithFuncName(show bool) { 380 loggerDefault.withFuncName = show 381 } 382 383 // Debug level debug 384 func Debug(fmt string, args ...interface{}) { 385 loggerDefault.deliverRecordToWriter(DEBUG, fmt, args...) 386 } 387 388 // Info level info 389 func Info(fmt string, args ...interface{}) { 390 loggerDefault.deliverRecordToWriter(INFO, fmt, args...) 391 } 392 393 // Notice level notice 394 func Notice(fmt string, args ...interface{}) { 395 loggerDefault.deliverRecordToWriter(NOTICE, fmt, args...) 396 } 397 398 // Warn level warn 399 func Warn(fmt string, args ...interface{}) { 400 loggerDefault.deliverRecordToWriter(WARNING, fmt, args...) 401 } 402 403 // Error level error 404 func Error(fmt string, args ...interface{}) { 405 loggerDefault.deliverRecordToWriter(ERROR, fmt, args...) 406 } 407 408 // Critical level critical 409 func Critical(fmt string, args ...interface{}) { 410 loggerDefault.deliverRecordToWriter(CRITICAL, fmt, args...) 411 } 412 413 // Alert level alert 414 func Alert(fmt string, args ...interface{}) { 415 loggerDefault.deliverRecordToWriter(ALERT, fmt, args...) 416 } 417 418 // Emergency level emergency 419 func Emergency(fmt string, args ...interface{}) { 420 loggerDefault.deliverRecordToWriter(EMERGENCY, fmt, args...) 421 } 422 423 // The method is put here, so it's easy to test 424 func getLevelDefault(flag string, defaultFlag int, writer string) int { 425 // level WARN == WARNING 426 if strings.EqualFold(flag, LevelFlagWarn) { 427 flag = LevelFlagWarning 428 } 429 430 for i, f := range LevelFlags { 431 if strings.TrimSpace(strings.ToUpper(flag)) == f { 432 return i 433 } 434 } 435 log.Printf("[log4go] no matching level for writer(%v, flag:%v), use default level(%d, flag:%v)", writer, flag, defaultFlag, LevelFlags[defaultFlag]) 436 return defaultFlag 437 }