日志组件
我们可以使用日志来观察程序的行为、诊断问题或者配置相应的告警等。定义良好的结构化日志,能够提高日志的检索效率,使处理问题变得更加方便。
设计理念
为了方便使用,glog 定义了两个层面的抽象,Logger统一了日志的接入方式,Helper接口统一的日志库的调用方式。
主要特性
- Logger 用于对接各种日志库或日志平台,可以用现成的或者自己实现
- Helper 是在您的项目代码中实际需要调用的,用于在业务代码里打日志
- Filter 用于对输出日志进行过滤或魔改(通常用于日志脱敏)
- Valuer 用于绑定一些全局的固定值或动态值(比如时间戳、traceID或者实例id之类的东西)到输出日志中
日志接口
用于快速适配各种日志库到框架中来,仅需要实现一个最简单的Log方法。
type Logger interface {
Log(level Level, keyvals ...any) error
}
- Level 日志级别
- LevelDebug 是日志调试级别。
- LevelInfo 是日志信息级别。
- LevelWarn 是日志警告级别。
- LevelError 是日志错误级别。
- LevelFatal 是日志致命级别。
- keyvals 是一个平铺的键值数组,它的长度需要是偶数,奇数位上的是key,偶数位上的是value。
调用实例:
glog.Log(glog.LevelInfo, "msg", "hello", "instance_id", 123)
初始化
首先你需要创建一个 Logger,这里可以选自带的std打印到标准输出或者用自己实现的Logger。
h := glog.NewHelper(yourlogger)
// 用默认 logger 可以直接用
h := glog.NewHelper(glog.DefaultLogger)
打印日志
注意:调用Fatal等级的方法会在打印日志后中断程序运行,请谨慎使用。
直接打印不同等级的日志,会默认打到 messageKey 里,默认是 msg
glog.Debug("Are you OK?")
glog.Info("42 is the answer to life, the universe, and everything")
glog.Warn("We are under attack!")
glog.Error("Houston, we have a problem.")
glog.Fatal("So Long, and Thanks for All the Fish.")
格式化打印不同等级的日志,方法都以 f 结尾
glog.Debugf("Hello %s", "boy")
glog.Infof("%d is the answer to life, the universe, and everything", 233)
glog.Warnf("We are under attack %s!", "boss")
glog.Errorf("%s, we have a problem.", "Master Shifu")
glog.Fatalf("So Long, and Thanks for All the %s.", "banana")
格式化打印不同等级的日志,方法都以w结尾,参数为 KV 对,可以输入多组
glog.Debugw("custom_key", "Are you OK?")
glog.Infow("custom_key", "42 is the answer to life, the universe, and everything")
glog.Warnw("custom_key", "We are under attack!")
glog.Errorw("custom_key", "Houston, we have a problem.")
glog.Fatalw("custom_key", "So Long, and Thanks for All the Fish.")
使用底层的 Log 接口直接打印 key 和 value
glog.Log(log.LevelInfo, "key1", "value1")
Valuer 设置全局字段
在业务日志中,通常我们会在每条日志中输出一些全局的字段,比如时间戳,实例id,追踪id,用户id,调用函数名等,显然在每条日志中手工写入这些值很麻烦。为了解决这个问题,可以使用Valuer。您可以认为它是logger的“中间件”,用它来打一些全局的信息到日志里。
glog.With
方法会返回一个新的 Logger,把参数的 Valuer 绑上去。
注意要按照key,value的顺序对应写入参数。
使用方法如下:
logger = glog.With(logger, "ts", glog.DefaultTimestamp, "caller", glog.DefaultCaller)
- glog.Caller 打印出调用日志方法的文件名和函数名
- glog.Timestamp 打印时间戳
Filter 日志过滤
有时日志中可能会有敏感信息,需要进行脱敏,或者只打印级别高的日志,这时候就可以使用 Filter 来对日志的输出进行一些过滤操作,通常用法是使用 Filter 来包装原始的 Logger,用来创建 Helper 使用。
它提供了如下参数:
FilterLevel
按照日志等级过滤,低于该等级的日志将不会被输出。例如这里传入FilterLevel(log.LevelError)
,则 debug/info/warn 日志都会被过滤掉不会输出,error 和fatal 正常输出。FilterKey(key ...string) FilterOption
按照 key 过滤,这些 key 的值会被***
遮蔽FilterValue(value ...string) FilterOption
按照 value 过滤,匹配的值会被***
遮蔽FilterFunc(f func(level Level, keyvals ...any) bool)
使用自定义的函数来对日志进行处理,keyvals 里为 key 和对应的 value,按照奇偶进行读取即可
h := glog.NewHelper(
glog.NewFilter(logger,
// 等级过滤
glog.FilterLevel(glog.LevelError),
// 按key遮蔽
glog.FilterKey("username"),
// 按value遮蔽
glog.FilterValue("hello"),
// 自定义过滤函数
glog.FilterFunc(
func (level glog.Level, keyvals ...any) bool {
if level == glog.LevelWarn {
return true
}
for i := 0; i < len(keyvals); i++ {
if keyvals[i] == "password" {
keyvals[i+1] = fuzzyStr
}
}
return false
}
),
),
)
h.Log(glog.LevelDebug, "msg", "test debug")
h.Info("hello")
h.Infow("password", "123456")
h.Infow("username", "kratos")
h.Warn("warn log")
绑定 context
设置context,使用如下方法将返回一个绑定指定context的helper实例
newHelper := h.WithContext(ctx)