日志组件

我们可以使用日志来观察程序的行为、诊断问题或者配置相应的告警等。定义良好的结构化日志,能够提高日志的检索效率,使处理问题变得更加方便。

设计理念

为了方便使用,glog 定义了两个层面的抽象,Logger统一了日志的接入方式,Helper接口统一的日志库的调用方式。

主要特性

日志接口

用于快速适配各种日志库到框架中来,仅需要实现一个最简单的Log方法。

type Logger interface {
    Log(level Level, keyvals ...any) error
}

调用实例:

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)

Filter 日志过滤

有时日志中可能会有敏感信息,需要进行脱敏,或者只打印级别高的日志,这时候就可以使用 Filter 来对日志的输出进行一些过滤操作,通常用法是使用 Filter 来包装原始的 Logger,用来创建 Helper 使用。

它提供了如下参数:

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)