zap 自定义日志格式_zap日志框架-使用篇(1)

zap日志框架分了三篇来讲解:使用篇 ,源码篇,性能篇。

为什么选择 zap

流行的日志框架中,如:logrusgo-kitlog15,都提供了结构化、非结构化输出。使用起来大同小异,正基于此,性能+扩展 成了我们选择日志框架的两个比较重要的维度。

性能

参考 github上的单元测试,如下:

记录一个包含10个字段的日志的消耗情况

1f005dbbd590a43a7e2b95a7d0a25849.png
记录一个静态的字符串,或者 printf输出的模板的消耗情况

0bbe676fff3189f12c9e38643447f94e.png

从上面的测试接口来看,单就从性能上来说,zap几乎可以说非常优秀的(zerolog 性能最优,但是只能输出结构化日志)

扩展性

支持自定义输出:json key自定义、时间格式自定义、日志级别自定义、自定义Writer接口
  • json key 自定义
config := zapcore.EncoderConfig{
    TimeKey:        "time",
    LevelKey:       "level",
    NameKey:        "logger",
    CallerKey:      "caller",
    MessageKey:     "msg",
}
  • 时间格式自定义
config := zapcore.EncoderConfig{
    EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
        enc.AppendString(t.Format("2006-01-02 15:04:05"))
    }
}
  • 日志级别的自定义
infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
    return lvl < zapcore.WarnLevel && lvl >= logLevel
})
  • 自定义Writer接口
我们可以选择 lumberjack库作为日志归档库,也可以选择 rotatelogs,只要其实现了Writer接口即可。

例子

进入正题,我们先可以一个完整的例子

初始化

package main
​
import (
    "github.com/natefinch/lumberjack"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "io"
    "os"
    "time"
)
​
func main() {
    InitLog("./info.log", "./error.log", zap.InfoLevel)
    defer logger.Sync()
    
    sugarLogger.Infof("sugarLogger name:%s", "修华师1")
    sugarLogger.Infow("sugarLogger", zap.String("name", "修华师2"))
    sugarLogger.Errorf("sugarLogger name:%s", "修华师3")
    sugarLogger.Debugf("sugarLogger name:%s", "修华师4")
    sugarLogger.Warnf("sugarLogger name:%s", "修华师5")
​
    logger.Info("logger", zap.String("name", "修华师6"))
    logger.Error("logger", zap.String("name", "修华师7"))
    logger.Debug("logger", zap.String("name", "修华师8"))
}
​
//只能输出结构化日志,但是性能要高于 SugaredLogger 
var logger *zap.Logger
//可以输出 结构化日志、非结构化日志。性能茶语 zap.Logger,具体可见上面的的单元测试
var sugarLogger *zap.SugaredLogger
​
// 初始化日志 logger
func InitLog(logPath, errPath string, logLevel zapcore.Level) {
    config := zapcore.EncoderConfig{
        MessageKey:   "msg",  //结构化(json)输出:msg的key
        LevelKey:     "level",//结构化(json)输出:日志级别的key(INFO,WARN,ERROR等)
        TimeKey:      "ts",   //结构化(json)输出:时间的key(INFO,WARN,ERROR等)
        CallerKey:    "file", //结构化(json)输出:打印日志的文件对应的Key
        EncodeLevel:  zapcore.CapitalLevelEncoder, //将日志级别转换成大写(INFO,WARN,ERROR等)
        EncodeCaller: zapcore.ShortCallerEncoder, //采用短文件路径编码输出(test/main.go:14 )
        EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
            enc.AppendString(t.Format("2006-01-02 15:04:05"))
        },//输出的时间格式
        EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) {
            enc.AppendInt64(int64(d) / 1000000)
        },//
    }
    //自定义日志级别:自定义Info级别
    infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
        return lvl < zapcore.WarnLevel && lvl >= logLevel
    })
​
    //自定义日志级别:自定义Warn级别
    warnLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
        return lvl >= zapcore.WarnLevel && lvl >= logLevel
    })
​
    // 获取io.Writer的实现
    infoWriter := getWriter(logPath)
    warnWriter := getWriter(errPath)
​
    // 实现多个输出
    core := zapcore.NewTee(
        zapcore.NewCore(zapcore.NewConsoleEncoder(config), zapcore.AddSync(infoWriter), infoLevel), //将info及以下写入logPath,NewConsoleEncoder 是非结构化输出
        zapcore.NewCore(zapcore.NewConsoleEncoder(config), zapcore.AddSync(warnWriter), warnLevel),//warn及以上写入errPath
        zapcore.NewCore(zapcore.NewJSONEncoder(config), zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout)), logLevel),//同时将日志输出到控制台,NewJSONEncoder 是结构化输出
    )
    logger = zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.WarnLevel)) 
    sugarLogger = logger.Sugar()
}

获取Writer实现

实现一

func getWriter(filename string) io.Writer {
    return &lumberjack.Logger{
        Filename:   filename,
        MaxSize:    10,  //最大M数,超过则切割
        MaxBackups: 5,   //最大文件保留数,超过就删除最老的日志文件
        MaxAge:     30,  //保存30天
        Compress:   false,//是否压缩
    }
}

实现二

​
func getWriter(filename string) io.Writer {
    // 生成rotatelogs的Logger 实际生成的文件名 filename.YYmmddHH
    // filename是指向最新日志的链接
    hook, err := rotatelogs.New(
        filename+".%Y%m%d%H",
        rotatelogs.WithLinkName(filename),
        rotatelogs.WithMaxAge(time.Hour*24*30),    // 保存30天
        rotatelogs.WithRotationTime(time.Hour*24), //切割频率 24小时
    )
    if err != nil {
        log.Println("日志启动异常")
        panic(err)
    }
    return hook
}

输出

控制台输出:可以看到debug日志没有输出,级别限制
{"level":"INFO","ts":"2020-05-15 22:57:10","file":"test/main.go:17","msg":"sugarLogger name:修华师1"}
{"level":"INFO","ts":"2020-05-15 22:57:10","file":"test/main.go:18","msg":"sugarLogger","name":"修华师2"}
{"level":"ERROR","ts":"2020-05-15 22:57:10","file":"test/main.go:19","msg":"sugarLogger name:修华师3"}
{"level":"WARN","ts":"2020-05-15 22:57:10","file":"test/main.go:21","msg":"sugarLogger name:修华师5"}
{"level":"INFO","ts":"2020-05-15 22:57:10","file":"test/main.go:23","msg":"logger","name":"修华师6"}
{"level":"ERROR","ts":"2020-05-15 22:57:10","file":"test/main.go:24","msg":"logger","name":"修华师7"}
info.log输出:info级别的日志输出在info.log中,和我们的自定义级别日志相符
2020-05-15 22:57:10 INFO    test/main.go:17 sugarLogger name:修华师1
2020-05-15 22:57:10 INFO    test/main.go:18 sugarLogger {"name": "修华师2"}
2020-05-15 22:57:10 INFO    test/main.go:23 logger  {"name": "修华师6"}
error.log输出:warn级别以上的日志输出在error.log中,和我们的自定义级别日志相符
2020-05-15 22:57:10 ERROR   test/main.go:19 sugarLogger name:修华师3
2020-05-15 22:57:10 WARN    test/main.go:21 sugarLogger name:修华师5
2020-05-15 22:57:10 ERROR   test/main.go:24 logger  {"name": "修华师7"}

结语

使用篇到此结束,有问题请留言!

下一篇:zap日志框架-源码篇(2)

相关阅读

zap日志框架-源码篇(2)

zap日志框架-性能篇(3)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值