Golang中的结构化日志

368 阅读3分钟

Golang中的结构化日志

什么是结构化日志,有什么好处,以及如何在golang中做到这一点

Nick Galbreath-May 13, 2018

开箱即用,golang提供了stdlib/log包。它提供了 "printf "式的日志记录。一个模板字符串和一个参数列表一起使用,然后生成一条日志信息。

log.Printf("user %d wasn't found", 1234)
// user 1234 wasn't found

这种动态生成的消息是简单方便的,但有一些涉及存储、搜索和一致性的问题。

结构化日志通过用命名字段代替printf消息模板解决了这些问题。它们有两种风格。

// vargargs style
log.Info("user not found", "userid", 1234)
// or using another popular syntax
log.WithField("userid", 1234).Info("user not found")

输出是一个键值对的列表,后面是静态消息。

// userid=1234 msg="user not found"

如果不是所有的结构化记录器都以某种可序列化的格式输出,如JSON或logfmt。这使得导出或导入数据到Splunk或Kibana变得非常简单。

用grep解决问题

因为消息是一个静态的字符串,所以搜索、排序、聚合和计算各种不同的错误信息都可以用grep解决。无论是在日志文件中(发生了什么),还是在代码中(在哪里发生的)。如果你的问题可以简化为grep,你就赢了。比如说。

// sample message
msg="user not found" id=1234
// How many times did this happen?
grep -c "user not found" mylogfile.log
// what happened with user id "1234" ?
grep 'id=1234' mylogfile.log
// what line of code emitted this?
find . -name '*.go' | xargs grep "user not found"

人们仍然可以用 "user id 1234 not found" "的形式做同样的事情,但这要难得多,而且容易出错。

更好的英语,更好的国际化

结构化日志和使用静态字符串的另一个好处是,信息在语法上变得简单了许多。复数和特殊形式通常会消失(1个用户对2个用户)。语言变得更加一致,例如,"未找到用户 "与 "未找到用户1234 "与 "无法找到用户1234 "与 "找不到用户1234"。如上所述,这使搜索更容易,也使阅读更容易,特别是当英语不是他们的主要语言时。如果你最终将应用程序国际化,"找不到用户 "比一个动态字符串更容易翻译。

上下文日志

golang中的大多数结构化日志也提供了一个叫做 "上下文日志 "的助手。这允许你预先填充一些字段,所以关键数据不需要重复。它也使代码更易读。

func setupUser(uid int) {
   logger = logger.With("userid", uid)
   logger.Debug("looking up user")
   // ...
   logger.Debug("updating record")
}
// userid=1234 msg="looking up user"
// userid=1234 msg="updating record"

高朗

如前所述,Golang的标准日志并不支持结构化日志。然而,许多甚至大多数第三方软件包都支持它。要开始使用,两个不错的通用包是sirupus/log顶点/log。对于高性能的日志,请看uber/zap 或rs/zerolog。更多的细节可以在Golang 的日志包中找到。

登录...