github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zlog/zlogger.go (about) 1 package zlog 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "os" 8 "reflect" 9 "runtime" 10 "sync" 11 "text/tabwriter" 12 "time" 13 14 "github.com/sohaha/zlsgo/zfile" 15 "github.com/sohaha/zlsgo/zreflect" 16 "github.com/sohaha/zlsgo/zstring" 17 "github.com/sohaha/zlsgo/ztime" 18 "github.com/sohaha/zlsgo/zutil" 19 ) 20 21 // log header information tag bit, using bitmap mode 22 const ( 23 BitDate int = 1 << iota // Date marker 2019/01/23 24 BitTime // Time Label Bit 01:23:12 25 BitMicroSeconds // Microsecond label bit 01:23:12.111222 26 BitLongFile // Full file name, example: /home/go/src/github.com/sohaha/zlsgo/doc.go 27 BitShortFile // Final File name doc.go 28 BitLevel // Current log level 29 BitStdFlag = BitDate | BitTime // Standard header log format 30 BitDefault = BitLevel | BitShortFile | BitTime // Default log header format 31 // LogMaxBuf LogMaxBuf 32 LogMaxBuf = 1024 * 1024 33 ) 34 35 // log level 36 const ( 37 LogFatal = iota 38 LogPanic 39 LogTrack 40 LogError 41 LogWarn 42 LogTips 43 LogSuccess 44 LogInfo 45 LogDebug 46 LogDump 47 LogNot = -1 48 ) 49 50 var Levels = []string{ 51 "[FATAL]", 52 "[PANIC]", 53 "[TRACK]", 54 "[ERROR]", 55 "[WARN] ", 56 "[TIPS] ", 57 "[SUCCE]", 58 "[INFO] ", 59 "[DEBUG]", 60 "[DUMP] ", 61 } 62 63 var LevelColous = []Color{ 64 ColorRed, 65 ColorLightRed, 66 ColorLightYellow, 67 ColorRed, 68 ColorYellow, 69 ColorWhite, 70 ColorGreen, 71 ColorBlue, 72 ColorLightCyan, 73 ColorCyan, 74 } 75 76 type ( 77 // Logger logger struct 78 Logger struct { 79 out io.Writer 80 file *zfile.MemoryFile 81 prefix string 82 fileDir string 83 fileName string 84 writeBefore []func(level int, log string) bool 85 // buf bytes.Buffer 86 calldDepth int 87 level int 88 flag int 89 mu sync.RWMutex 90 color bool 91 fileAndStdout bool 92 } 93 formatter struct { 94 v reflect.Value 95 force bool 96 quote bool 97 } 98 visit struct { 99 typ reflect.Type 100 v uintptr 101 } 102 zprinter struct { 103 io.Writer 104 tw *tabwriter.Writer 105 visited map[visit]int 106 depth int 107 } 108 ) 109 110 // New Initialize a log object 111 func New(moduleName ...string) *Logger { 112 name := "" 113 if len(moduleName) > 0 { 114 name = moduleName[0] 115 } 116 return NewZLog(os.Stderr, name, BitDefault, LogDump, true, 3) 117 } 118 119 // NewZLog Create log 120 func NewZLog(out io.Writer, prefix string, flag int, level int, color bool, calldDepth int) *Logger { 121 zlog := &Logger{out: out, prefix: prefix, flag: flag, file: nil, calldDepth: calldDepth, level: level, color: color} 122 runtime.SetFinalizer(zlog, CleanLog) 123 return zlog 124 } 125 126 // CleanLog CleanLog 127 func CleanLog(log *Logger) { 128 log.CloseFile() 129 } 130 131 // DisableConsoleColor DisableConsoleColor 132 func (log *Logger) DisableConsoleColor() { 133 log.color = false 134 } 135 136 // ForceConsoleColor ForceConsoleColor 137 func (log *Logger) ForceConsoleColor() { 138 log.color = true 139 } 140 141 // ColorTextWrap ColorTextWrap 142 func (log *Logger) ColorTextWrap(color Color, text string) string { 143 if log.color { 144 return ColorTextWrap(color, text) 145 } 146 return text 147 } 148 149 // ColorBackgroundWrap ColorBackgroundWrap 150 func (log *Logger) ColorBackgroundWrap(color Color, backgroundColor Color, text string) string { 151 if log.color { 152 return ColorBackgroundWrap(color, backgroundColor, text) 153 } 154 return text 155 } 156 157 // OpTextWrap OpTextWrap 158 func (log *Logger) OpTextWrap(color Op, text string) string { 159 if log.color { 160 return OpTextWrap(color, text) 161 } 162 return text 163 } 164 165 func (log *Logger) formatHeader(buf *bytes.Buffer, t time.Time, file string, line int, level int) { 166 if log.flag&(BitDate|BitTime|BitMicroSeconds|BitLevel) != 0 { 167 168 if log.flag&BitDate != 0 { 169 buf.WriteString(ztime.FormatTime(t, "Y/m/d ")) 170 } 171 172 if log.flag&(BitTime|BitMicroSeconds) != 0 { 173 buf.WriteString(ztime.FormatTime(t, "H:i:s")) 174 if log.flag&BitMicroSeconds != 0 { 175 buf.WriteByte('.') 176 itoa(buf, t.Nanosecond()/1e3, 6) // "12:12:59.123456 177 } 178 buf.WriteByte(' ') 179 } 180 181 if log.flag&BitLevel != 0 { 182 buf.WriteString(log.ColorTextWrap(LevelColous[level], Levels[level]+" ")) 183 } 184 185 if log.flag&(BitShortFile|BitLongFile) != 0 { 186 if log.flag&BitShortFile != 0 { 187 short := file 188 for i := len(file) - 1; i > 0; i-- { 189 if file[i] == '/' { 190 short = file[i+1:] 191 break 192 } 193 } 194 file = short 195 } 196 buf.WriteString(file) 197 buf.WriteByte(':') 198 itoa(buf, line, -1) 199 buf.WriteString(": ") 200 } 201 } 202 } 203 204 // outPut Output log 205 func (log *Logger) outPut(level int, s string, isWrap bool, calldDepth int, prefixText ...string) error { 206 if log.writeBefore != nil && len(s) > 0 { 207 p := s 208 if isWrap { 209 p = p[:len(p)-1] 210 } 211 for i := range log.writeBefore { 212 if log.writeBefore[i](level, p) { 213 return nil 214 } 215 } 216 } 217 218 if len(prefixText) > 0 { 219 s = prefixText[0] + s 220 } 221 222 buf := zutil.GetBuff(len(s) + 34) 223 defer zutil.PutBuff(buf) 224 225 now := ztime.Time() 226 if level != LogNot { 227 file, line := log.fileLocation(calldDepth) 228 log.formatHeader(buf, now, file, line, level) 229 } 230 231 if log.prefix != "" { 232 buf.WriteString(log.prefix) 233 } 234 235 buf.WriteString(s) 236 if isWrap && len(s) > 0 && s[len(s)-1] != '\n' { 237 buf.WriteByte('\n') 238 } 239 _, err := log.out.Write(buf.Bytes()) 240 return err 241 } 242 243 // Printf formats according to a format specifier and writes to standard output 244 func (log *Logger) Printf(format string, v ...interface{}) { 245 _ = log.outPut(LogNot, fmt.Sprintf(format, v...), false, log.calldDepth) 246 } 247 248 // Println Println 249 func (log *Logger) Println(v ...interface{}) { 250 _ = log.outPut(LogNot, fmt.Sprintln(v...), true, log.calldDepth) 251 } 252 253 // Debugf Debugf 254 func (log *Logger) Debugf(format string, v ...interface{}) { 255 if log.level < LogDebug { 256 return 257 } 258 _ = log.outPut(LogDebug, fmt.Sprintf(format, v...), false, log.calldDepth) 259 } 260 261 // Debug Debug 262 func (log *Logger) Debug(v ...interface{}) { 263 if log.level < LogDebug { 264 return 265 } 266 _ = log.outPut(LogDebug, fmt.Sprintln(v...), true, log.calldDepth) 267 } 268 269 // Dump pretty print format 270 func (log *Logger) Dump(v ...interface{}) { 271 if log.level < LogDump { 272 return 273 } 274 args := formatArgs(v...) 275 _, file, line, ok := callerName(1) 276 if ok { 277 names, err := argNames(file, line) 278 if err == nil { 279 args = prependArgName(names, args) 280 } 281 } 282 283 _ = log.outPut(LogDump, fmt.Sprintln(args...), true, log.calldDepth) 284 } 285 286 // Successf output Success 287 func (log *Logger) Successf(format string, v ...interface{}) { 288 if log.level < LogSuccess { 289 return 290 } 291 _ = log.outPut(LogSuccess, fmt.Sprintf(format, v...), false, log.calldDepth) 292 } 293 294 // Success output Success 295 func (log *Logger) Success(v ...interface{}) { 296 if log.level < LogSuccess { 297 return 298 } 299 _ = log.outPut(LogSuccess, fmt.Sprintln(v...), true, log.calldDepth) 300 } 301 302 // Infof output Info 303 func (log *Logger) Infof(format string, v ...interface{}) { 304 if log.level < LogInfo { 305 return 306 } 307 _ = log.outPut(LogInfo, fmt.Sprintf(format, v...), false, log.calldDepth) 308 } 309 310 // Info output Info 311 func (log *Logger) Info(v ...interface{}) { 312 if log.level < LogInfo { 313 return 314 } 315 _ = log.outPut(LogInfo, fmt.Sprintln(v...), true, log.calldDepth) 316 } 317 318 // Tipsf output Tips 319 func (log *Logger) Tipsf(format string, v ...interface{}) { 320 if log.level < LogTips { 321 return 322 } 323 _ = log.outPut(LogTips, fmt.Sprintf(format, v...), false, log.calldDepth) 324 } 325 326 // Tips output Tips 327 func (log *Logger) Tips(v ...interface{}) { 328 if log.level < LogTips { 329 return 330 } 331 _ = log.outPut(LogTips, fmt.Sprintln(v...), true, log.calldDepth) 332 } 333 334 // Warnf output Warn 335 func (log *Logger) Warnf(format string, v ...interface{}) { 336 if log.level < LogWarn { 337 return 338 } 339 _ = log.outPut(LogWarn, fmt.Sprintf(format, v...), false, log.calldDepth) 340 } 341 342 // Warn output Warn 343 func (log *Logger) Warn(v ...interface{}) { 344 if log.level < LogWarn { 345 return 346 } 347 _ = log.outPut(LogWarn, fmt.Sprintln(v...), true, log.calldDepth) 348 } 349 350 // Errorf output Error 351 func (log *Logger) Errorf(format string, v ...interface{}) { 352 if log.level < LogError { 353 return 354 } 355 _ = log.outPut(LogError, fmt.Sprintf(format, v...), false, log.calldDepth) 356 } 357 358 // Error output Error 359 func (log *Logger) Error(v ...interface{}) { 360 if log.level < LogError { 361 return 362 } 363 _ = log.outPut(LogError, fmt.Sprintln(v...), true, log.calldDepth) 364 } 365 366 // Fatalf output Fatal 367 func (log *Logger) Fatalf(format string, v ...interface{}) { 368 if log.level < LogFatal { 369 return 370 } 371 _ = log.outPut(LogFatal, fmt.Sprintf(format, v...), false, log.calldDepth) 372 osExit(1) 373 } 374 375 // Fatal output Fatal 376 func (log *Logger) Fatal(v ...interface{}) { 377 if log.level < LogFatal { 378 return 379 } 380 _ = log.outPut(LogFatal, fmt.Sprintln(v...), true, log.calldDepth) 381 osExit(1) 382 } 383 384 // Panicf output Panic 385 func (log *Logger) Panicf(format string, v ...interface{}) { 386 if log.level < LogPanic { 387 return 388 } 389 s := fmt.Sprintf(format, v...) 390 _ = log.outPut(LogPanic, fmt.Sprintf(format, s), false, log.calldDepth) 391 panic(s) 392 } 393 394 // Panic output panic 395 func (log *Logger) Panic(v ...interface{}) { 396 if log.level < LogPanic { 397 return 398 } 399 s := fmt.Sprintln(v...) 400 _ = log.outPut(LogPanic, s, true, log.calldDepth) 401 panic(s) 402 } 403 404 // Stack output Stack 405 func (log *Logger) Stack(v interface{}) { 406 if log.level < LogTrack { 407 return 408 } 409 var s string 410 switch e := v.(type) { 411 case error: 412 s = fmt.Sprintf("%+v", e) 413 case string: 414 s = e 415 default: 416 s = fmt.Sprintf("%v", e) 417 } 418 _ = log.outPut(LogTrack, s, true, log.calldDepth) 419 } 420 421 // Track output Track 422 func (log *Logger) Track(v string, i ...int) { 423 if log.level < LogTrack { 424 return 425 } 426 b, skip, max, index := zutil.GetBuff(), 4, 1, 1 427 il := len(i) 428 if il > 0 { 429 max = i[0] 430 if il == 2 { 431 skip = skip + i[1] 432 } 433 } 434 s := zutil.Callers(skip) 435 l := len(s) 436 if max >= l { 437 max = l 438 } 439 s = s[:max] 440 space := " " 441 b.WriteString(v + "\n") 442 s.Format(func(fn *runtime.Func, file string, line int) bool { 443 if index > 9 { 444 space = " " 445 } 446 b.WriteString(fmt.Sprintf( 447 " %d).%s%s\n \t%s:%d\n", 448 index, space, fn.Name(), file, line, 449 )) 450 index++ 451 return true 452 }) 453 text := b.String() 454 zutil.PutBuff(b) 455 _ = log.outPut(LogTrack, text, true, log.calldDepth) 456 } 457 458 func callerName(skip int) (name, file string, line int, ok bool) { 459 var pc uintptr 460 if pc, file, line, ok = runtime.Caller(skip + 1); !ok { 461 return 462 } 463 name = runtime.FuncForPC(pc).Name() 464 return 465 } 466 467 // GetFlags Get the current log bitmap tag 468 func (log *Logger) GetFlags() int { 469 log.mu.Lock() 470 defer log.mu.Unlock() 471 return log.flag 472 } 473 474 // ResetFlags Reset the GetFlags bitMap tag bit in the log 475 func (log *Logger) ResetFlags(flag int) { 476 log.mu.Lock() 477 defer log.mu.Unlock() 478 log.flag = flag 479 } 480 481 // AddFlag Set flag Tags 482 func (log *Logger) AddFlag(flag int) { 483 log.mu.Lock() 484 defer log.mu.Unlock() 485 log.flag |= flag 486 } 487 488 // SetPrefix Setting log prefix 489 func (log *Logger) SetPrefix(prefix string) { 490 log.mu.Lock() 491 defer log.mu.Unlock() 492 log.prefix = prefix 493 } 494 495 func (log *Logger) GetPrefix() string { 496 return log.prefix 497 } 498 499 // SetLogLevel Setting log display level 500 func (log *Logger) SetLogLevel(level int) { 501 log.level = level 502 } 503 504 // GetLogLevel Get log display level 505 func (log *Logger) GetLogLevel() int { 506 return log.level 507 } 508 509 func (log *Logger) Write(b []byte) (n int, err error) { 510 _ = log.outPut(LogWarn, zstring.Bytes2String(b), false, log.calldDepth) 511 return len(b), nil 512 } 513 514 func (log *Logger) SetIgnoreLog(logs ...string) { 515 log.WriteBefore(func(level int, log string) bool { 516 for _, v := range logs { 517 if zstring.Match(log, v) { 518 return true 519 } 520 } 521 return false 522 }) 523 } 524 525 func (log *Logger) WriteBefore(fn ...func(level int, log string) bool) { 526 log.writeBefore = append(log.writeBefore, fn...) 527 } 528 529 func itoa(buf *bytes.Buffer, i int, wid int) { 530 u := uint(i) 531 if u == 0 && wid <= 1 { 532 buf.WriteByte('0') 533 return 534 } 535 536 var b [32]byte 537 bp := len(b) 538 for ; u > 0 || wid > 0; u /= 10 { 539 bp-- 540 wid-- 541 b[bp] = byte(u%10) + '0' 542 } 543 544 for bp < len(b) { 545 buf.WriteByte(b[bp]) 546 bp++ 547 } 548 } 549 550 func (log *Logger) ResetWriter(w io.Writer) { 551 log.out = w 552 } 553 554 func formatArgs(args ...interface{}) []interface{} { 555 formatted := make([]interface{}, 0, len(args)) 556 for _, a := range args { 557 s := ColorTextWrap(ColorCyan, sprint(a)) 558 formatted = append(formatted, s) 559 } 560 561 return formatted 562 } 563 564 func sprint(a ...interface{}) string { 565 return fmt.Sprint(wrap(a, true)...) 566 } 567 568 func wrap(a []interface{}, force bool) []interface{} { 569 w := make([]interface{}, len(a)) 570 for i, x := range a { 571 w[i] = formatter{v: zreflect.ValueOf(x), force: force} 572 } 573 return w 574 } 575 576 func writeByte(w io.Writer, b byte) { 577 _, _ = w.Write([]byte{b}) 578 } 579 580 func prependArgName(names []string, values []interface{}) []interface{} { 581 vLen := len(values) 582 nLen := len(names) 583 prepended := make([]interface{}, vLen) 584 for i, value := range values { 585 name := "" 586 if i < nLen { 587 name = names[i] 588 } 589 if name == "" { 590 prepended[i] = OpTextWrap(OpBold, value.(string)) 591 continue 592 } 593 name = ColorTextWrap(ColorBlue, OpTextWrap(OpBold, name)) 594 prepended[i] = fmt.Sprintf("%s=%s", name, value) 595 } 596 return prepended 597 } 598 599 func (log *Logger) fileLocation(calldDepth int) (file string, line int) { 600 if log.flag&(BitShortFile|BitLongFile) != 0 { 601 var ok bool 602 _, file, line, ok = runtime.Caller(calldDepth) 603 if !ok { 604 file = "unknown-file" 605 line = 0 606 } 607 } 608 return 609 }