├── .gitignore ├── README.md ├── go.mod ├── logext.go └── logext_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## log 2 | [![GoDoc](https://godoc.org/github.com/gobuild/log?status.png)](https://godoc.org/github.com/gobuild/log) 3 | 4 | Extension module of golang logging, 5 | Most code came from [qiniu/log](https://github.com/qiniu/log) 6 | 7 | Improment: Add color support 8 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gobuild/log 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /logext.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | "runtime" 9 | "strings" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | // These flags define which text to prefix to each log entry generated by the Logger. 15 | const ( 16 | // Bits or'ed together to control what's printed. There is no control over the 17 | // order they appear (the order listed here) or the format they present (as 18 | // described in the comments). A colon appears after these items: 19 | // 2009/0123 01:23:23.123123 /a/b/c/d.go:23: message 20 | Ldate = 1 << iota // the date: 2009/0123 21 | Ltime // the time: 01:23:23 22 | Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime. 23 | Llongfile // full file name and line number: /a/b/c/d.go:23 24 | Lshortfile // final file name element and line number: d.go:23. overrides Llongfile 25 | Lmodule // module name 26 | Llevel // level: 0(Debug), 1(Info), 2(Warn), 3(Error), 4(Panic), 5(Fatal) 27 | Llongcolor // color will start [info] end of line 28 | Lshortcolor // color only include [info] 29 | LstdFlags = Ldate | Ltime // initial values for the standard logger 30 | Ldefault = Llevel | LstdFlags | Lshortfile | Llongcolor 31 | ) // [prefix][time][level][module][shortfile|longfile] 32 | 33 | const ( 34 | Ldebug = iota 35 | Linfo 36 | Lwarn 37 | Lerror 38 | Lpanic 39 | Lfatal 40 | ) 41 | 42 | const ( 43 | ForeBlack = iota + 30 //30 44 | ForeRed //31 45 | ForeGreen //32 46 | ForeYellow //33 47 | ForeBlue //34 48 | ForePurple //35 49 | ForeCyan //36 50 | ForeWhite //37 51 | ) 52 | 53 | const ( 54 | BackBlack = iota + 40 //40 55 | BackRed //41 56 | BackGreen //42 57 | BackYellow //43 58 | BackBlue //44 59 | BackPurple //45 60 | BackCyan //46 61 | BackWhite //47 62 | ) 63 | 64 | var levels = []string{ 65 | "[Debug]", 66 | "[Info]", 67 | "[Warn]", 68 | "[Error]", 69 | "[Panic]", 70 | "[Fatal]", 71 | } 72 | 73 | // MUST called before all logs 74 | func SetLevels(lvs []string) { 75 | levels = lvs 76 | } 77 | 78 | var colors = []int{ 79 | ForeCyan, 80 | ForeBlue, 81 | ForeGreen, 82 | ForeYellow, 83 | ForeRed, 84 | ForePurple, 85 | } 86 | 87 | // MUST called before all logs 88 | func SetColors(cls []int) { 89 | colors = cls 90 | } 91 | 92 | // A Logger represents an active logging object that generates lines of 93 | // output to an io.Writer. Each logging operation makes a single call to 94 | // the Writer's Write method. A Logger can be used simultaneously from 95 | // multiple goroutines; it guarantees to serialize access to the Writer. 96 | type Logger struct { 97 | mu sync.Mutex // ensures atomic writes; protects the following fields 98 | prefix string // prefix to write at beginning of each line 99 | flag int // properties 100 | Level int 101 | out io.Writer // destination for output 102 | buf bytes.Buffer // for accumulating text to write 103 | levelStats [6]int64 104 | } 105 | 106 | // New creates a new Logger. The out variable sets the 107 | // destination to which log data will be written. 108 | // The prefix appears at the beginning of each generated log line. 109 | // The flag argument defines the logging properties. 110 | func New(out io.Writer, prefix string, flag int) *Logger { 111 | return &Logger{out: out, prefix: prefix, Level: 1, flag: flag} 112 | } 113 | 114 | var Std = New(os.Stderr, "", Ldefault) 115 | 116 | // Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding. 117 | // Knows the buffer has capacity. 118 | func itoa(buf *bytes.Buffer, i int, wid int) { 119 | var u uint = uint(i) 120 | if u == 0 && wid <= 1 { 121 | buf.WriteByte('0') 122 | return 123 | } 124 | 125 | // Assemble decimal in reverse order. 126 | var b [32]byte 127 | bp := len(b) 128 | for ; u > 0 || wid > 0; u /= 10 { 129 | bp-- 130 | wid-- 131 | b[bp] = byte(u%10) + '0' 132 | } 133 | 134 | // avoid slicing b to avoid an allocation. 135 | for bp < len(b) { 136 | buf.WriteByte(b[bp]) 137 | bp++ 138 | } 139 | } 140 | 141 | func moduleOf(file string) string { 142 | pos := strings.LastIndex(file, "/") 143 | if pos != -1 { 144 | pos1 := strings.LastIndex(file[:pos], "/src/") 145 | if pos1 != -1 { 146 | return file[pos1+5 : pos] 147 | } 148 | } 149 | return "UNKNOWN" 150 | } 151 | 152 | func (l *Logger) formatHeader(buf *bytes.Buffer, t time.Time, file string, line int, lvl int, reqId string) { 153 | if l.prefix != "" { 154 | buf.WriteString(l.prefix) 155 | } 156 | if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 { 157 | if l.flag&Ldate != 0 { 158 | year, month, day := t.Date() 159 | itoa(buf, year, 4) 160 | buf.WriteByte('/') 161 | itoa(buf, int(month), 2) 162 | buf.WriteByte('/') 163 | itoa(buf, day, 2) 164 | buf.WriteByte(' ') 165 | } 166 | if l.flag&(Ltime|Lmicroseconds) != 0 { 167 | hour, min, sec := t.Clock() 168 | itoa(buf, hour, 2) 169 | buf.WriteByte(':') 170 | itoa(buf, min, 2) 171 | buf.WriteByte(':') 172 | itoa(buf, sec, 2) 173 | if l.flag&Lmicroseconds != 0 { 174 | buf.WriteByte('.') 175 | itoa(buf, t.Nanosecond()/1e3, 6) 176 | } 177 | buf.WriteByte(' ') 178 | } 179 | } 180 | if reqId != "" { 181 | buf.WriteByte('[') 182 | buf.WriteString(reqId) 183 | buf.WriteByte(']') 184 | buf.WriteByte(' ') 185 | } 186 | 187 | if l.flag&(Lshortcolor|Llongcolor) != 0 { 188 | buf.WriteString(fmt.Sprintf("\033[1;%dm", colors[lvl])) 189 | } 190 | if l.flag&Llevel != 0 { 191 | buf.WriteString(levels[lvl]) 192 | buf.WriteByte(' ') 193 | } 194 | if l.flag&Lshortcolor != 0 { 195 | buf.WriteString("\033[0m") 196 | } 197 | 198 | if l.flag&Lmodule != 0 { 199 | buf.WriteByte('[') 200 | buf.WriteString(moduleOf(file)) 201 | buf.WriteByte(']') 202 | buf.WriteByte(' ') 203 | } 204 | if l.flag&(Lshortfile|Llongfile) != 0 { 205 | if l.flag&Lshortfile != 0 { 206 | short := file 207 | for i := len(file) - 1; i > 0; i-- { 208 | if file[i] == '/' { 209 | short = file[i+1:] 210 | break 211 | } 212 | } 213 | file = short 214 | } 215 | buf.WriteString(file) 216 | buf.WriteByte(':') 217 | itoa(buf, line, -1) 218 | buf.WriteByte(' ') 219 | } 220 | } 221 | 222 | // Output writes the output for a logging event. The string s contains 223 | // the text to print after the prefix specified by the flags of the 224 | // Logger. A newline is appended if the last character of s is not 225 | // already a newline. Calldepth is used to recover the PC and is 226 | // provided for generality, although at the moment on all pre-defined 227 | // paths it will be 2. 228 | func (l *Logger) Output(reqId string, lvl int, calldepth int, s string) error { 229 | if lvl < l.Level { 230 | return nil 231 | } 232 | now := time.Now() // get this early. 233 | var file string 234 | var line int 235 | l.mu.Lock() 236 | defer l.mu.Unlock() 237 | if l.flag&(Lshortfile|Llongfile|Lmodule) != 0 { 238 | // release lock while getting caller info - it's expensive. 239 | l.mu.Unlock() 240 | var ok bool 241 | _, file, line, ok = runtime.Caller(calldepth) 242 | if !ok { 243 | file = "???" 244 | line = 0 245 | } 246 | l.mu.Lock() 247 | } 248 | l.levelStats[lvl]++ 249 | l.buf.Reset() 250 | l.formatHeader(&l.buf, now, file, line, lvl, reqId) 251 | l.buf.WriteString(s) 252 | if l.flag&Llongcolor != 0 { 253 | l.buf.WriteString("\033[0m") 254 | } 255 | if len(s) > 0 && s[len(s)-1] != '\n' { 256 | l.buf.WriteByte('\n') 257 | } 258 | _, err := l.out.Write(l.buf.Bytes()) 259 | return err 260 | } 261 | 262 | // ----------------------------------------- 263 | 264 | // Printf calls l.Output to print to the logger. 265 | // Arguments are handled in the manner of fmt.Printf. 266 | func (l *Logger) Printf(format string, v ...interface{}) { 267 | l.Output("", Linfo, 2, fmt.Sprintf(format, v...)) 268 | } 269 | 270 | // Print calls l.Output to print to the logger. 271 | // Arguments are handled in the manner of fmt.Print. 272 | func (l *Logger) Print(v ...interface{}) { l.Output("", Linfo, 2, fmt.Sprint(v...)) } 273 | 274 | // Println calls l.Output to print to the logger. 275 | // Arguments are handled in the manner of fmt.Println. 276 | func (l *Logger) Println(v ...interface{}) { l.Output("", Linfo, 2, fmt.Sprintln(v...)) } 277 | 278 | // ----------------------------------------- 279 | 280 | func (l *Logger) Debugf(format string, v ...interface{}) { 281 | if Ldebug < l.Level { 282 | return 283 | } 284 | l.Output("", Ldebug, 2, fmt.Sprintf(format, v...)) 285 | } 286 | 287 | func (l *Logger) Debug(v ...interface{}) { 288 | if Ldebug < l.Level { 289 | return 290 | } 291 | l.Output("", Ldebug, 2, fmt.Sprintln(v...)) 292 | } 293 | 294 | // ----------------------------------------- 295 | 296 | func (l *Logger) Infof(format string, v ...interface{}) { 297 | if Linfo < l.Level { 298 | return 299 | } 300 | l.Output("", Linfo, 2, fmt.Sprintf(format, v...)) 301 | } 302 | 303 | func (l *Logger) Info(v ...interface{}) { 304 | if Linfo < l.Level { 305 | return 306 | } 307 | l.Output("", Linfo, 2, fmt.Sprintln(v...)) 308 | } 309 | 310 | // ----------------------------------------- 311 | 312 | func (l *Logger) Warnf(format string, v ...interface{}) { 313 | l.Output("", Lwarn, 2, fmt.Sprintf(format, v...)) 314 | } 315 | 316 | func (l *Logger) Warn(v ...interface{}) { l.Output("", Lwarn, 2, fmt.Sprintln(v...)) } 317 | 318 | // ----------------------------------------- 319 | 320 | func (l *Logger) Errorf(format string, v ...interface{}) { 321 | l.Output("", Lerror, 2, fmt.Sprintf(format, v...)) 322 | } 323 | 324 | func (l *Logger) Error(v ...interface{}) { l.Output("", Lerror, 2, fmt.Sprintln(v...)) } 325 | 326 | // ----------------------------------------- 327 | 328 | func (l *Logger) Fatal(v ...interface{}) { 329 | l.Output("", Lfatal, 2, fmt.Sprint(v...)) 330 | os.Exit(1) 331 | } 332 | 333 | // Fatalf is equivalent to l.Printf() followed by a call to os.Exit(1). 334 | func (l *Logger) Fatalf(format string, v ...interface{}) { 335 | l.Output("", Lfatal, 2, fmt.Sprintf(format, v...)) 336 | os.Exit(1) 337 | } 338 | 339 | // Fatalln is equivalent to l.Println() followed by a call to os.Exit(1). 340 | func (l *Logger) Fatalln(v ...interface{}) { 341 | l.Output("", Lfatal, 2, fmt.Sprintln(v...)) 342 | os.Exit(1) 343 | } 344 | 345 | // ----------------------------------------- 346 | 347 | // Panic is equivalent to l.Print() followed by a call to panic(). 348 | func (l *Logger) Panic(v ...interface{}) { 349 | s := fmt.Sprint(v...) 350 | l.Output("", Lpanic, 2, s) 351 | panic(s) 352 | } 353 | 354 | // Panicf is equivalent to l.Printf() followed by a call to panic(). 355 | func (l *Logger) Panicf(format string, v ...interface{}) { 356 | s := fmt.Sprintf(format, v...) 357 | l.Output("", Lpanic, 2, s) 358 | panic(s) 359 | } 360 | 361 | // Panicln is equivalent to l.Println() followed by a call to panic(). 362 | func (l *Logger) Panicln(v ...interface{}) { 363 | s := fmt.Sprintln(v...) 364 | l.Output("", Lpanic, 2, s) 365 | panic(s) 366 | } 367 | 368 | // ----------------------------------------- 369 | 370 | func (l *Logger) Stack(v ...interface{}) { 371 | s := fmt.Sprint(v...) 372 | s += "\n" 373 | buf := make([]byte, 1024*1024) 374 | n := runtime.Stack(buf, true) 375 | s += string(buf[:n]) 376 | s += "\n" 377 | l.Output("", Lerror, 2, s) 378 | } 379 | 380 | // ----------------------------------------- 381 | 382 | func (l *Logger) Stat() (stats []int64) { 383 | l.mu.Lock() 384 | v := l.levelStats 385 | l.mu.Unlock() 386 | return v[:] 387 | } 388 | 389 | // Flags returns the output flags for the logger. 390 | func (l *Logger) Flags() int { 391 | l.mu.Lock() 392 | defer l.mu.Unlock() 393 | return l.flag 394 | } 395 | 396 | // SetFlags sets the output flags for the logger. 397 | func (l *Logger) SetFlags(flag int) { 398 | l.mu.Lock() 399 | defer l.mu.Unlock() 400 | l.flag = flag 401 | } 402 | 403 | // Prefix returns the output prefix for the logger. 404 | func (l *Logger) Prefix() string { 405 | l.mu.Lock() 406 | defer l.mu.Unlock() 407 | return l.prefix 408 | } 409 | 410 | // SetPrefix sets the output prefix for the logger. 411 | func (l *Logger) SetPrefix(prefix string) { 412 | l.mu.Lock() 413 | defer l.mu.Unlock() 414 | l.prefix = prefix 415 | } 416 | 417 | // SetOutputLevel sets the output level for the logger. 418 | func (l *Logger) SetOutputLevel(lvl int) { 419 | l.mu.Lock() 420 | defer l.mu.Unlock() 421 | l.Level = lvl 422 | } 423 | 424 | // SetOutput sets the output destination for the standard logger. 425 | func SetOutput(w io.Writer) { 426 | Std.mu.Lock() 427 | defer Std.mu.Unlock() 428 | Std.out = w 429 | } 430 | 431 | // Flags returns the output flags for the standard logger. 432 | func Flags() int { 433 | return Std.Flags() 434 | } 435 | 436 | // SetFlags sets the output flags for the standard logger. 437 | func SetFlags(flag int) { 438 | Std.SetFlags(flag) 439 | } 440 | 441 | // Prefix returns the output prefix for the standard logger. 442 | func Prefix() string { 443 | return Std.Prefix() 444 | } 445 | 446 | // SetPrefix sets the output prefix for the standard logger. 447 | func SetPrefix(prefix string) { 448 | Std.SetPrefix(prefix) 449 | } 450 | 451 | func SetOutputLevel(lvl int) { 452 | Std.SetOutputLevel(lvl) 453 | } 454 | 455 | func GetOutputLevel() int { 456 | return Std.Level 457 | } 458 | 459 | // ----------------------------------------- 460 | 461 | // Print calls Output to print to the standard logger. 462 | // Arguments are handled in the manner of fmt.Print. 463 | func Print(v ...interface{}) { 464 | Std.Output("", Linfo, 2, fmt.Sprint(v...)) 465 | } 466 | 467 | // Printf calls Output to print to the standard logger. 468 | // Arguments are handled in the manner of fmt.Printf. 469 | func Printf(format string, v ...interface{}) { 470 | Std.Output("", Linfo, 2, fmt.Sprintf(format, v...)) 471 | } 472 | 473 | // Println calls Output to print to the standard logger. 474 | // Arguments are handled in the manner of fmt.Println. 475 | func Println(v ...interface{}) { 476 | Std.Output("", Linfo, 2, fmt.Sprintln(v...)) 477 | } 478 | 479 | // ----------------------------------------- 480 | 481 | func Debugf(format string, v ...interface{}) { 482 | if Ldebug < Std.Level { 483 | return 484 | } 485 | Std.Output("", Ldebug, 2, fmt.Sprintf(format, v...)) 486 | } 487 | 488 | func Debug(v ...interface{}) { 489 | if Ldebug < Std.Level { 490 | return 491 | } 492 | Std.Output("", Ldebug, 2, fmt.Sprintln(v...)) 493 | } 494 | 495 | // ----------------------------------------- 496 | 497 | func Infof(format string, v ...interface{}) { 498 | if Linfo < Std.Level { 499 | return 500 | } 501 | Std.Output("", Linfo, 2, fmt.Sprintf(format, v...)) 502 | } 503 | 504 | func Info(v ...interface{}) { 505 | if Linfo < Std.Level { 506 | return 507 | } 508 | Std.Output("", Linfo, 2, fmt.Sprintln(v...)) 509 | } 510 | 511 | // ----------------------------------------- 512 | 513 | func Warnf(format string, v ...interface{}) { 514 | Std.Output("", Lwarn, 2, fmt.Sprintf(format, v...)) 515 | } 516 | 517 | func Warn(v ...interface{}) { Std.Output("", Lwarn, 2, fmt.Sprintln(v...)) } 518 | 519 | // ----------------------------------------- 520 | 521 | func Errorf(format string, v ...interface{}) { 522 | Std.Output("", Lerror, 2, fmt.Sprintf(format, v...)) 523 | } 524 | 525 | func Error(v ...interface{}) { Std.Output("", Lerror, 2, fmt.Sprintln(v...)) } 526 | 527 | // ----------------------------------------- 528 | 529 | // Fatal is equivalent to Print() followed by a call to os.Exit(1). 530 | func Fatal(v ...interface{}) { 531 | Std.Output("", Lfatal, 2, fmt.Sprint(v...)) 532 | os.Exit(1) 533 | } 534 | 535 | // Fatalf is equivalent to Printf() followed by a call to os.Exit(1). 536 | func Fatalf(format string, v ...interface{}) { 537 | Std.Output("", Lfatal, 2, fmt.Sprintf(format, v...)) 538 | os.Exit(1) 539 | } 540 | 541 | // Fatalln is equivalent to Println() followed by a call to os.Exit(1). 542 | func Fatalln(v ...interface{}) { 543 | Std.Output("", Lfatal, 2, fmt.Sprintln(v...)) 544 | os.Exit(1) 545 | } 546 | 547 | // ----------------------------------------- 548 | 549 | // Panic is equivalent to Print() followed by a call to panic(). 550 | func Panic(v ...interface{}) { 551 | s := fmt.Sprint(v...) 552 | Std.Output("", Lpanic, 2, s) 553 | panic(s) 554 | } 555 | 556 | // Panicf is equivalent to Printf() followed by a call to panic(). 557 | func Panicf(format string, v ...interface{}) { 558 | s := fmt.Sprintf(format, v...) 559 | Std.Output("", Lpanic, 2, s) 560 | panic(s) 561 | } 562 | 563 | // Panicln is equivalent to Println() followed by a call to panic(). 564 | func Panicln(v ...interface{}) { 565 | s := fmt.Sprintln(v...) 566 | Std.Output("", Lpanic, 2, s) 567 | panic(s) 568 | } 569 | 570 | // ----------------------------------------- 571 | 572 | func Stack(v ...interface{}) { 573 | s := fmt.Sprint(v...) 574 | s += "\n" 575 | buf := make([]byte, 1024*1024) 576 | n := runtime.Stack(buf, true) 577 | s += string(buf[:n]) 578 | s += "\n" 579 | Std.Output("", Lerror, 2, s) 580 | } 581 | 582 | // ----------------------------------------- 583 | -------------------------------------------------------------------------------- /logext_test.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestLog(t *testing.T) { 8 | 9 | SetOutputLevel(Ldebug) 10 | 11 | Debugf("Debug: foo\n") 12 | Debug("Debug: foo") 13 | 14 | Infof("Info: foo\n") 15 | Info("Info: foo") 16 | 17 | Warnf("Warn: foo\n") 18 | Warn("Warn: foo") 19 | 20 | Errorf("Error: foo\n") 21 | Error("Error: foo") 22 | 23 | SetOutputLevel(Linfo) 24 | 25 | Debugf("Debug: foo\n") 26 | Debug("Debug: foo") 27 | 28 | Infof("Info: foo\n") 29 | Info("Info: foo") 30 | 31 | Warnf("Warn: foo\n") 32 | Warn("Warn: foo") 33 | 34 | Errorf("Error: foo\n") 35 | Error("Error: foo") 36 | } 37 | --------------------------------------------------------------------------------