成文耗时1小时,阅读3min,有用指数4颗星。
golang中常见的日志包是logrus, 根据logrus的胚子和我们的生产要求,给出一个生产可用的logrus实践姿势。
主谓宾定状补
logrus是一个结构化的、可插拔的、兼容golang标准log api的日志库。
快速过一下能力
• 支持对output=TTY增加关键字颜色
• 内置JSONFormatter和TextFormatter(默认)两种Formatter
• 支持输出logger所在的函数行位置
log.SetReportCaller(true)
• 可以兼容golang内置的标准log库, 建议无脑替换
• 鼓励输出可解析的日志字段,而不是大段的无法结构化的文本日志
log.WithFields(log.Fields{
"event": event,
"topic": topic,
"key": key,
}).Fatal("Failed to send event")
基于现状,凑了6个钱包上生产,下面给出一些自己的生产实践🙏。
添砖加瓦
1. logrus不支持滚动日志
好马配好鞍 https://github.com/lestrrat-go/file-rotatelogs 让你下雨天不再哭泣。
它会根据配置自动按照时间切分日志,并滚动清理日志(不用配磁盘报警,不用担心磁盘满故障)。
logf, err := rotatelogs.New(
cfg.Log.LogDir+logName+".%Y%m%d%H%M",
rotatelogs.WithLinkName(cfg.Log.LogDir+logName),
rotatelogs.WithMaxAge(24*time.Hour),
rotatelogs.WithRotationTime(time.Hour),
)
if err != nil {
stdLog.Printf("failed to create rotatelogs: %s", err)
return
}
2. 日志格式化
java生态默认日志输出格式:
11:44:44.827 WARN [93ef3E0120160803114444] [main] [ClassPathXmlApplicationContext] Exception encountered during context initialization - cancelling refresh attempt
在公司中javaer占据主流,故java的默认格式就成了公司集中式日志的"标准"格式。
很明显,logrus默认的两种Formatter都不匹配。
github.com/antonfisher/nested-logrus-formatter 让你柳暗花明。
log.SetFormatter(&nested.Formatter{ // 嵌套日志兼容skynet日志格式
HideKeys: true,
FieldsOrder: []string{"region", "node", "topic"},
TimestampFormat: "2006-01-02 15:04:05.000", // 显示ms
})
3. 自定义Hook用法:输出默认字段
写本文的时候,发现logrus官方本身支持输出默认日志字段。
requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
requestLogger.Warn("something not great happened")
Hook: 通常 钩子函数用于在触发某种事件时附带一些动作。
logrus的Hook定义:logEntry满足指定的logLevel日志时, 你想要做的动作,比如在产生panic错误,给用户组发短信报警。
(你甚至可以不设置output直接在hook输出日志, 这就是内置write hook[1]的实现)。
type Hook interface {
Levels() []Level
Fire(*Entry) error
}
示例代码为logLevel>=info的logEntry,固定了2个日志字段。
type FixedFieldHook struct {
LogLevels []logrus.Level
FixedField map[string]string
}
// Fire will be called when some logging function is called with current hook
// It will format log entry to string and write it to appropriate writer
func (hook *FixedFieldHook) Fire(entry *logrus.Entry) error {
for k, v := range hook.FixedField {
entry.Data[k] = v
}
return nil
}
log.AddHook(&FixedFieldHook{ // Set fixed field
FixedField: map[string]string{"region": cfg.LocalRegion, "node": ip},
LogLevels: []logrus.Level{
logrus.InfoLevel,
logrus.ErrorLevel,
logrus.WarnLevel,
logrus.FatalLevel,
},
})
以上是对于golang logrus日志库的战术性理解,文辞拙劣,如果错误或者不同见解,欢迎留言探讨。抛砖引玉,战术卧倒🐖。
引用链接
[1]
write hook: https://github.com/sirupsen/logrus/blob/master/hooks/writer/writer.go
点“赞”戳“在看”
体现态度很有必要!