Go zap 学习笔记

3,242 阅读2分钟

安装

go get -v go.uber.org/zap

zap 格式

zap 有两种格式

  • zap.Logger 使用结构体的格式来记录(性能比较高)
  • zap.SugaredLogger 使用传统的 printf 的格式来记录(性能相较上一种低)

zap 打印一条日志的大致流程

  1. 通过 zapcore.Entry 结构体生成一条信息
  2. 通过 zapcore.Core 的 check 函数检查日志级别,产生一个zapcore.CheckEntry 对象
  3. 根据日志级别判断是否需要 panic 和 fatal 并退出应用
  4. 通过zapcore.Option 的设置,zap.AddCaller(),zap.AddStacktrace(1) 或其他自定义的 fields
  5. 通过 Encoder 为 zapcore.CheckEntry() 提供编码器,通过 EncodeEntry 方法吧 zapcore.Entry 和 zapcore.Fields 转成最后输出格式,json 和 console 转成 buffer.Buffer, memory encode 转成 map

zap 自带的日志记录器

  • zap.NewProduction()
  • zap.NewDevelopment()
  • zap.NewExample()

zap 自定义日志记录器

  • 通过 build 创建日志记录器
  • 通过 zap.New 创建日志记录器

通过 build 创建

package main

import (
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"io"
	"time"
)

var Logger *zap.Logger

func main() {
	encoder := zap.Config{
		Level:             zap.NewAtomicLevel(),
		Development:       false,
		DisableCaller:     false,
		DisableStacktrace: false,
		Encoding:          "json",
		EncoderConfig: zapcore.EncoderConfig{
			MessageKey:    "msg",
			LevelKey:      "level",
			TimeKey:       "ts",
			CallerKey:     "file",
			StacktraceKey: "stack",
			EncodeLevel:   zapcore.CapitalLevelEncoder,
			EncodeTime: func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {
				encoder.AppendString(t.Format("2006-01-02 15:04:05"))
			},
			EncodeCaller: zapcore.ShortCallerEncoder,
		},
		OutputPaths:      []string{"stdout"},
		ErrorOutputPaths: []string{"stdout"},
		InitialFields:    map[string]interface{}{"url": "localhost"},
	}

	logger, _ := encoder.Build(zap.AddCaller())
	logger.Info("我是一个错误")
}

根据年月日切割日志的方法

这里选择 file-rotatelogs 库

go get -v github.com/lestrrat-go/file-rotatelogs

demo 代码

func getWriter(filename string) io.Writer {
	// 生成rotatelogs的Logger 实际生成的文件名 demo.log.YYmmdd
	// demo.log是指向最新日志的链接
	// 保存7天内的日志,每1小时(整点)分割一次日志
	hook, err := rotatelogs.New(
		filename+".%Y%m%d", // 没有使用go风格反人类的format格式
		rotatelogs.WithLinkName(filename),
		rotatelogs.WithMaxAge(time.Hour*24*7),
		rotatelogs.WithRotationTime(time.Hour*24),
	)

	if err != nil {
		panic(err)
	}
	return hook
}

完整代码

通过 zap.New 创建日志记录器

package main

import (
	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"io"
	"time"
)

var Logger *zap.Logger

func main() {
	encoder := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
		MessageKey:    "msg",
		LevelKey:      "level",
		TimeKey:       "ts",
		CallerKey:     "file",
		StacktraceKey: "stack",
		EncodeLevel:   zapcore.CapitalLevelEncoder,
		EncodeTime: func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {
			encoder.AppendString(t.Format("2006-01-02 15:04:05"))
		},
		EncodeCaller: zapcore.ShortCallerEncoder,
	})
	highLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= zapcore.ErrorLevel
	})

	lowLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl < zapcore.ErrorLevel
	})

	hook1 := getWriter("logs/errlogs/err.log")
	hook2 := getWriter("logs/infologs/info.log")

	core := zapcore.NewTee(
		zapcore.NewCore(encoder, zapcore.AddSync(hook1), highLevel),
		zapcore.NewCore(encoder, zapcore.AddSync(hook2), lowLevel),
	)
	logger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))
	logger.Info("我是一个错误", zap.String("url", "www.baidu.com"))
}

func getWriter(filename string) io.Writer {
	// 生成rotatelogs的Logger 实际生成的文件名 demo.log.YYmmddHH
	// demo.log是指向最新日志的链接
	// 保存7天内的日志,每24小时(整点)分割一次日志
	hook, err := rotatelogs.New(
		filename+".%Y%m%d",
		rotatelogs.WithLinkName(filename),
		rotatelogs.WithMaxAge(time.Hour*24*7),
		rotatelogs.WithRotationTime(time.Hour*24),
	)

	if err != nil {
		panic(err)
	}
	return hook
}

参考

  1. zap GitHub github.com/uber-go/zap…
  2. Go 每日一库 github.com/darjun/go-d…
  3. 夜读大佬的讲解视频 www.bilibili.com/video/BV15b…