携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第28天,点击查看活动详情
前言
本系列文章将以源码和文档为依据,梳理标准库的内容,拓展对标准库的认识,并进一步探索标准库的使用方法。
上一篇文章中,我们分析了 go 的内置日志库 log,它的代码较少,提供的功能也比较基础。所以,本篇文章,我们来看一看 go 的第三方日志库 logrus。
备注:本系列文章使用的是 go 1.19 源码:
log
Output
上一篇对 log 的分析其实还剩一点小尾巴,就是 Output 这个函数。
Output 函数可以传入 calldepth 直接控制计算文件名和行号时要跳过的栈帧(传给 runtime.Caller),其他的函数也都是通过此函数来输出日志的。
需要注意,直接调用函数 Output,它的 calldepth 是对于 Output 函数来计算的,在传向 runtime.Caller 的 skip 参数时会加一。
log.SetFlags(log.Lshortfile)
log.Output(1, "这是一条日志") // test.go:8: 这是一条日志
calldepth filename:line-number
-2 extern.go:219
-1 log.go:185(runtime.Caller的位置)
0 log.go:413(Output的位置)
1 test.go:8(log.Output的位置)
2 proc.go:250
3 asm_amd64.s:1594
不足
log 包的框架比较简单,只提供了 Print Fatal Panic 三类函数,缺少开发过程中常用的几种日志级别以及结构化输出日志的功能。
logrus
logrus 是 go 的结构化日志记录器,与标准库的 logger 完全兼容。目前,logrus 已处于维护模式,不再引入新功能。
logrus 提供 7 种日志级别:
const (
PanicLevel Level = iota
FatalLevel
ErrorLevel // 通常用于钩子,向错误处理服务发送错误
WarnLevel
InfoLevel
DebugLevel // 一般只在调试时启用
TraceLevel // 更细粒度的信息
)
logrus 默认创建一个标准记录器 std,最外层的方法会操作这个默认记录器。除了支持 log 中的三类函数外,logrus 还可以使用以 Fn 结尾的方法来传入函数。对于大量的消息,先判断日志级别是否启用,再生成日志信息,是更优的顺序。
由于 logrus 完全兼容 log,我们可以直接在导入时将它命名为 log,以兼容标准库。此外,我们可以在 init 函数中,对默认记录器进行设置:
import log "github.com/sirupsen/logrus"
func init() {
log.SetFormatter(&log.JSONFormatter{})
log.SetOutput(os.Stdout)
log.SetLevel(log.WarnLevel)
}
格式化
logrus 内置两种格式化器 TextFormatter 和 JsonFormatter,其中前者在标准输出是Linux终端时(TTY)会给日志加上颜色。
log.SetFormatter(&log.TextFormatter{ForceColors: true}) // 也可以强制输出颜色
log.Warn("这是一条日志")
默认情况下会输出如 time="2022-08-28T23:56:46+08:00" level=info msg="这是一条日志" 这样格式的日志。
如果想要添加文件名和方法,可以使用 log.SetReportCaller(true),会增加 func 和 file 字段。该功能会明显增加开销,其取决于 go 的版本。
添加字段
可以直接调用 WithFields 方法来添加多条字段信息
log.WithFields(log.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
WithFields 方法会返回一个存储了记录器和字段信息的 Entry,我们也可以将它保存下来并复用
logger := log.WithFields(log.Fields{
"common": "common msg",
})
logger.Info("1")
logger.Info("2")
Logger
除了使用默认的记录器以外,也可以自己来创建任意数量的记录器。
package main
import (
"os"
"github.com/sirupsen/logrus"
)
var log = logrus.New()
func main() {
log.Out = os.Stdout
log.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
}
需要注意的是,自己创建的记录器的配置方法与默认记录器的配置方法是不同的。
Hook
logrus 还可以为记录器设置钩子,只需要实现下面两个方法:
-
Levels() []logrus.Level:日志级别 -
Fire(entry *logrus.Entry) error:日志输出前会调用此函数,可以更改Entry记录的信息
import (
"github.com/sirupsen/logrus"
)
type hook struct {
msg string
}
func (h *hook) Levels() []logrus.Level {
return logrus.AllLevels
}
func (h *hook) Fire(entry *logrus.Entry) error {
entry.Data["hookMsg"] = h.msg
return nil
}
func main() {
h := &hook{"a hook msg"}
logrus.AddHook(h)
logrus.Info("info")
}
总结
本篇文章,我们完成了对 log 库的分析,并介绍了完全兼容 log 的第三方库 logrus 的特性以及基本使用方法。
最后,如果本篇文章对您有所帮助,希望您可以 点赞、收藏、评论,感谢支持 ✧(≖ ◡ ≖✿