Go 源码学习 - log 库

log 包实现了一个简单的日志功能。

Logger 结构体作为日志对象,生成文本行到 io.Writer。每次记录日志的操作都生成一行日志,即便是log.Printf也不用包含\n。结构体中有sync.Metex锁,保证了在多 goroutine 情况时的顺序写入。

type Logger struct {
	mu     sync.Mutex // 确保原子写入
	prefix string     // 日志行首的前缀字符串
	flag   int        // 属性标志
	out    io.Writer  // 输出目标
	buf    []byte     // 字节数组缓冲区,用于加速写入
}

其中 flags 可选项如下:

const (
	Ldate         = 1 << iota     // 本地时区的日期: 2009/01/23
	Ltime                         // 本地时区的时间: 01:23:23
	Lmicroseconds                 // 毫秒粒度: 01:23:23.123123.  包含了 Ltime.
	Llongfile                     // 完整的路径和文件名和行号: /a/b/c/d.go:23
	Lshortfile                    // 不包含路径,仅文件名和行号: d.go:23. overrides Llongfile
	LUTC                          // 使用 UTC 而非本地时区
	LstdFlags     = Ldate | Ltime // 标准 logger 的初始 flag 值
)

New 函数用于创建 logger,三个参数是:输出目标, 前缀字符串,属性标志

func New(out io.Writer, prefix string, flag int) *Logger 

logger 的 SetOutput 方法用于设置输出目标

func (l *Logger) SetOutput(w io.Writer) 

包中提供了一个标准 logger - std,我们的程序中执行log.Print时执行的就是std.Print

var std = New(os.Stderr, "", LstdFlags)

logger 的 formatHeader 方法将前缀字符串、日期、时间、文件名和行号写入缓冲区,它会调用itoa函数格式化文本。

func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int)

logger 的 Output 方法打印日志,它处理流程如下:

func (l *Logger) Output(calldepth int, s string) error
  1. 获取当前时间
  2. logger 加锁
  3. 如果 flags 设置了 Lshortfile 或 Llongfile,先解锁,再调用 runtime.Caller()函数获取文件名,再加锁。解锁加锁是因为获取文件名的动作消耗较大
  4. 清空 logger 的缓冲buf
  5. 调用 formatHeader 方法将时间、文件名、行号写入缓冲buf
  6. 将日志内容追加写入缓冲buf
  7. 如果行尾没有换行符就添加一个换行符
  8. 将缓冲buf写入输出目标

logger 的 Printf、Print、Println 调用 Output 方法打印日志,Fatal、Fatalf、Fatalln 调用 Output 方法打印日志后以错误码1退出程序,Panic、Panicf、Panicln 调用 Output 方法打印日志后触发panic,panic的内容就是日志内容。

还有其他一些设置和获取自定义 logger 或者 std logger 的前缀字符串、属性标志和输出目标的函数和方法。

logger 结构体里面的 buf 是为了高效写入,除此以外,log 包里面还有一个 itoa 函数,它会高效地把整数按位转换为字节数组写入缓冲buf

func itoa(buf *[]byte, i int, wid int) {
	// Assemble decimal in reverse order.
	var b [20]byte
	bp := len(b) - 1
	for i >= 10 || wid > 1 {
		wid--
		q := i / 10
		b[bp] = byte('0' + i - q*10)
		bp--
		i = q
	}
	// i < 10
	b[bp] = byte('0' + i)
	*buf = append(*buf, b[bp:]...)
}

如果要像 python 的logging 那样可以设置日志旋转的日志功能可以使用这个包:lumberjack

发布了66 篇原创文章 · 获赞 21 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/qq_35753140/article/details/104258625