Go log package 选择

Table of Contents

可能是 Go 尚年轻,行业对 Go 的应用领域还比较宽泛,也可能是开源的缘故,Go package 普遍造轮子的现象比较明显。

对于日志包也是如此,没有像 google/glog 和 log4plus 之于 C++ 这么权威。尝试了一些的 Go logging package,结论如下:

1. 标准库

功能较少,不同级别的日志区分不开,小的测试项目可以取代 fmt 来 debug。

2. google/glog

google 类似 C++ 日志版本的 Go 版本,性能较好,使用也比较简单。

  • 优点:代码很短,性能好,易用性较好,稳定;K8s 内置的就是 glog,不过最近好像要基于 glog 做二次开发 klog
  • 缺点:已经四五年没更新了···,日志级别区分的比较乱,需要二次封装

3. sirupsen/logrus

结构化的,可插拔的日志系统。

  • 业内评价较高,易用性好,定制化也很强,人性化。
  • 支持 hook,logrus-hook 我是自己写的 hook

4. uber-go/zap

Uber 开源的日志系统。

  • 性能 非常
  • 支持 Hook
  • 可能是设计就是为了设计之初就是为了高性能,定制性很强,就是非常不人性化,以至于不知道怎么定制···,好在有这个 sandipb/zap-examples

性能相比: zap > glog > logrus > log ,值得一提的是,zap 是远大于 glog 和 logrus 的。

5. 结论

  • 高性能的系统,推荐使用 zap;
  • 业务系统使用 zap/logrus,glog 的日志级别( V() )很容易误导开发人员,体验并不是很好。

6. 初始化范例

6.1. logrus

func init() {
    // TEXT
    logrus.SetFormatter(&logrus.TextFormatter{
        ForceColors:     false,
        DisableColors:   true,
        FullTimestamp:   true,
        TimestampFormat: "2006-01-02 15:04:05.000",
        CallerPrettyfier: func(frame *runtime.Frame) (function string, file string) {
            ss := strings.Split(frame.Function, ".")
            function = ss[len(ss)-1]
            file = fmt.Sprintf("%s:%d", filepath.Base(frame.File), frame.Line)
            return function, file
        },
    })
    // JSON
    logrus.SetFormatter(&logrus.JSONFormatter{
        TimestampFormat: "2006-01-02 15:04:05.000",
        CallerPrettyfier: func(frame *runtime.Frame) (function string, file string) {
            ss := strings.Split(frame.Function, ".")
            function = ss[len(ss)-1]
            file = fmt.Sprintf("%s:%d", filepath.Base(frame.File), frame.Line)
            return function, file
        },
        // FieldMap: logrus.FieldMap{
        //  logrus.FieldKeyTime:  "@timestamp",
        //  logrus.FieldKeyLevel: "@level",
        //  logrus.FieldKeyMsg:   "@message",
        //  logrus.FieldKeyFunc:  "@caller",
        // },
        PrettyPrint: true,
    })

    // 设置输出流
    logrus.SetOutput(os.Stdout)
    // include calling method
    logrus.SetReportCaller(true)
    // 日志级别
    logrus.SetLevel(logrus.TraceLevel)
}

6.2. zap

logger, _ := zap.Config{
    Encoding:      "console",
    Level:         zap.NewAtomicLevelAt(logLevel),
    DisableCaller: true,
    OutputPaths:   []string{"stdout"},
    EncoderConfig: zapcore.EncoderConfig{
        TimeKey:     "ts",
        LevelKey:    "level",
        MessageKey:  "msg",
        EncodeLevel: zapcore.CapitalLevelEncoder,
        EncodeTime:  zapcore.RFC3339TimeEncoder,
    },
}.Build()
zap.ReplaceGlobals(logger)

First created: 2019-03-14 17:50:00
Last updated: 2021-11-15 Mon 10:18
Power by Emacs 27.2 (Org mode 9.4.6)