用来打日志的库,也没啥好看的。

log.go

其实就和 fmt 差不多啦,不过多加了前缀而已。需要加哪些前缀是可以调用者指定的,具体可以看那堆常量 flags 的注释

  1. // src/log/log.go ---- line 16
  2. // These flags define which text to prefix to each log entry generated by the Logger.
  3. // Bits are or'ed together to control what's printed.
  4. // With the exception of the Lmsgprefix flag, there is no
  5. // control over the order they appear (the order listed here)
  6. // or the format they present (as described in the comments).
  7. // The prefix is followed by a colon only when Llongfile or Lshortfile
  8. // is specified.
  9. // For example, flags Ldate | Ltime (or LstdFlags) produce,
  10. // 2009/01/23 01:23:23 message
  11. // while flags Ldate | Ltime | Lmicroseconds | Llongfile produce,
  12. // 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
  13. const (
  14. Ldate = 1 << iota // the date in the local time zone: 2009/01/23
  15. Ltime // the time in the local time zone: 01:23:23
  16. Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
  17. Llongfile // full file name and line number: /a/b/c/d.go:23
  18. Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
  19. LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
  20. Lmsgprefix // move the "prefix" from the beginning of the line to before the message
  21. LstdFlags = Ldate | Ltime // initial values for the standard logger
  22. )
  23. // A Logger represents an active logging object that generates lines of
  24. // output to an io.Writer. Each logging operation makes a single call to
  25. // the Writer's Write method. A Logger can be used simultaneously from
  26. // multiple goroutines; it guarantees to serialize access to the Writer.
  27. type Logger struct {
  28. mu sync.Mutex // ensures atomic writes; protects the following fields
  29. prefix string // prefix on each line to identify the logger (but see Lmsgprefix)
  30. flag int // properties
  31. out io.Writer // destination for output
  32. buf []byte // for accumulating text to write
  33. }

有意思的地方是,获取文件名和代码行数是通过 runtime 实现的。也就是下面代码块的第 18 行

  1. // src/log/log.go ---- line 152
  2. // Output writes the output for a logging event. The string s contains
  3. // the text to print after the prefix specified by the flags of the
  4. // Logger. A newline is appended if the last character of s is not
  5. // already a newline. Calldepth is used to recover the PC and is
  6. // provided for generality, although at the moment on all pre-defined
  7. // paths it will be 2.
  8. func (l *Logger) Output(calldepth int, s string) error {
  9. now := time.Now() // get this early.
  10. var file string
  11. var line int
  12. l.mu.Lock()
  13. defer l.mu.Unlock()
  14. if l.flag&(Lshortfile|Llongfile) != 0 {
  15. // Release lock while getting caller info - it's expensive.
  16. l.mu.Unlock()
  17. var ok bool
  18. _, file, line, ok = runtime.Caller(calldepth)
  19. if !ok {
  20. file = "???"
  21. line = 0
  22. }
  23. l.mu.Lock()
  24. }
  25. l.buf = l.buf[:0]
  26. l.formatHeader(&l.buf, now, file, line)
  27. l.buf = append(l.buf, s...)
  28. if len(s) == 0 || s[len(s)-1] != '\n' {
  29. l.buf = append(l.buf, '\n')
  30. }
  31. _, err := l.out.Write(l.buf)
  32. return err
  33. }

当然具体实现还是得到后面看到 runtime 的时候再来深究