Go日志库log竟然这么小巧!

网友投稿 240 2022-09-13

Go日志库log竟然这么小巧!

前言

最近在尝试阅读字节开源RPC框架Kitex的源码,看到日志库​​klog​​​部分,果不其然在Go原生的​​log库​​的基础上增加了自己的设计,大体包括增加了一些格式化的输出、增加一些常用的日志级别等。

一番了解后,发现有不少开源的日志库也做了类似的事情,以补充原生​​log库​​​的不足。因为Go原生的​​log库​​​本身也比较简单,这篇文章先分析一下它的实现,为后续阅读Kitex的日志库​​klog​​做一下铺垫。

本次分析基于:​​GO SDK 1.18.1 /src/log/log.go​​的源码。

log库的使用

结果如下:

第三个日志因为第二个日志打印之后,调用​​panic()​​​函数,且没有调用​​recover()​​,导致程序终止。如果注释掉第二行日志即可打印出第三个日志的结果如下:

log.xxx能直接打印日志的原因

通过观察源码,​​log包​​​的​​log.go​​​文件中,提供了9个函数可以直接使用,3个一套,分别针对​​print型​​​日志输出、​​panic型​​​日志输出(可以​​recover​​​)、​​fatal型​​日志输出(直接终止程序)。

并且这9个函数中频繁使用到了一个​​std实例​​​,只要我们引入了​​log包​​​,​​std​​​就会完成初始化,并且作为默认使用的​​log​​实例。

Logger结构

既然​​std​​​是默认的​​Logger实例​​​,这里先看一下​​Logger​​的结构:

​​mu​​:互斥锁,用于原子写入操作。​​prefix​​:日志前缀/后缀。​​flag​​:控制需要展示的日志内容。​​out​​:描述输出。​​buf​​:缓冲区。

关于​​flag​​的使用,Go定义了如下的常量:

​​Ldata​​​:输出当地日期,如​​2009/01/23。​​​​Ltime​​​:输出当地时间,如​​01:23:23。​​​​Lmicroseconds​​​:时间精确到微妙,如​​01:23:23.123123​​​,兼并​​Ltime。​​​​Llongfile​​​:输出文件名全路径 + 调用行号,如​​/a/b/c/d.go:23。​​​​Lshortfile​​​:输出最终文件的名称 + 调用行号,如​​d.go:23​​​,覆盖​​Llongfile​​。​​LUTC​​​:如果设置了​​Ldata​​​和​​Ltime​​​,则将输出​​UTC​​时间,而不是本地时区。​​Lmsgprefix​​​:将​​prefix信息​​​从当前日志行首部移动到​​message​​之前。​​LstdFlags​​​:​​std实例​​​的默认值,表示​​Ldata | Ltime = 3​​。

官方的注释中给出了一些介绍flag用法的例子,这里介绍一个:

如果:​​std.flag == Ldate | Ltime | Lmicroseconds | Llongfile == 15​​,

则日志行输出结果为:​​2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message​​​,​​message​​为具体的日志内容。

std.Output()

回到上面9个函数打印日志,都通过调用​​std.Output()​​​实现日志的输出,是​​log库​​的核心函数,看一下代码:

通过​​l.mu.Lock()​​,确保日志内容的写入是原子的。检查​​l.flag​​​是否包括​​Lshortfile​​​或者​​Llongfile​​​标志位,如果有则需要获取​​文件名​​​和​​行数​​​,且这一步先释放了锁,因为​​Caller方法​​的调用比较耗时(expensive),确保锁住的临界区尽可能小。​​calldepth​​​:0表示获取调用​​runtime.Caller(calldepth)​​​的文件名和行数,1表示调用​​std.Output()​​​的文件名和函数,2表示调用​​log.Println()​​的文件名和行数,3则已经用不到了,Go原生log库获取行信息用的都是2。

清空缓冲区​​l.buf​​,并格式化日志头部信息(日期、文件名、行数),将其​​append​​入`buf。最后将具体的​​日志信息s​​​添加入​​buf​​​,会补全末尾换行符,并调用​​l.out.Write()​​,将日志写入事先注册的输出文件。

定制自己的Logger

log库默认使用的std实例是事先初始化好的,那么借助New方法,我们也可以定制自己的logger:

这里指定了日志输出到文件log.txt中,并且定义了一些flag,结果如下:

小结

通过分析,我们发现log是一个很简洁的日志库,它有三种日志输出方式​​print​​​、​​panic​​​、​​fatal​​​,且可以自己定制日志的输出格式。但是熟悉其他语言开发的同学可能会对日志级别有更多的需求,且​​log​​的格式化用起来比较复杂。

因此会衍生出很多基于​​log​​​的二次封装的日志库,下一篇文章将讲解字节跳动RPC框架Kitex的日志库​​klog​​的实现。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Go语言入门 - 工程实践|青训营笔记
下一篇:深入浅出 HBase 实战|青训营笔记
相关文章

 发表评论

暂时没有评论,来抢沙发吧~