美文网首页自留地
golang高性能日志库zap的使用

golang高性能日志库zap的使用

作者: 不能吃的坚果j | 来源:发表于2020-12-17 14:58 被阅读0次

    本文作者:陈进坚
    个人博客:https://jian1098.github.io
    CSDN博客:https://blog.csdn.net/c_jian
    简书:https://www.jianshu.com/u/8ba9ac5706b6
    联系方式:jian1098@qq.com

    摘要

    zapUber开发的非常快的、结构化的,分日志级别的Go日志库。根据Uber-go Zap的文档,它的性能比类似的结构化日志包更好,也比标准库更快。具体的性能测试可以去github上看到。

    github地址:https://github.com/uber-go/zap

    创建实例

    通过调用zap.NewProduction()/zap.NewDevelopment()或者zap.Example()创建一个Logger。这三个方法的区别在于它将记录的信息不同,参数只能是string类型

    //代码
    var log *zap.Logger
    log = zap.NewExample()
    log, _ := zap.NewDevelopment()
    log, _ := zap.NewProduction()
    log.Debug("This is a DEBUG message")
    log.Info("This is an INFO message")
    
    //Example 输出
    {"level":"debug","msg":"This is a DEBUG message"}
    {"level":"info","msg":"This is an INFO message"}
    
    //Development 输出
    2018-10-30T17:14:22.459+0800    DEBUG    development/main.go:7    This is a DEBUG message
    2018-10-30T17:14:22.459+0800    INFO    development/main.go:8    This is an INFO message
    
    //Production 输出
    {"level":"info","ts":1540891173.3190675,"caller":"production/main.go:8","msg":"This is an INFO message"}
    {"level":"info","ts":1540891173.3191047,"caller":"production/main.go:9","msg":"This is an INFO message with fields","region":["us-west"],"id":2}
    

    三种创建方式对比:

    • ExampleProduction使用的是json格式输出,Development使用行的形式输出
    • Development
      • 从警告级别向上打印到堆栈中来跟踪
      • 始终打印包/文件/行(方法)
      • 在行尾添加任何额外字段作为json字符串
      • 以大写形式打印级别名称
      • 以毫秒为单位打印ISO8601格式的时间戳
    • Production
      • 调试级别消息不记录
      • Error,Dpanic级别的记录,会在堆栈中跟踪文件,Warn不会
      • 始终将调用者添加到文件中
      • 以时间戳格式打印日期
      • 以小写形式打印级别名称

    格式化输出

    zap有两种类型,分别是*zap.Logger*zap.SugaredLogger,它们惟一的区别是,我们通过调用主logger的. Sugar()方法来获取一个SugaredLogger,然后使用SugaredLoggerprintf格式记录语句,例如

    var sugarLogger *zap.SugaredLogger
    
    func InitLogger() {
      logger, _ := zap.NewProduction()
        sugarLogger = logger.Sugar()
    }
    
    func main() {
        InitLogger()
        defer sugarLogger.Sync()
        sugarLogger.Errorf("Error fetching URL %s : Error = %s", url, err)
    }
    

    写入文件

    默认情况下日志都会打印到应用程序的console界面,但是为了方便查询,可以将日志写入文件,但是我们不能再使用前面创建实例的3个方法,而是使用zap.New()

    package main
    
    import (
        "go.uber.org/zap"
        "go.uber.org/zap/zapcore"
        "os"
    )
    
    var log *zap.Logger
    func main() {
        writeSyncer, _ := os.Create("./info.log")                           //日志文件存放目录
        encoderConfig := zap.NewProductionEncoderConfig()                    //指定时间格式
        encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
        encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
        encoder := zapcore.NewConsoleEncoder(encoderConfig)                 //获取编码器,NewJSONEncoder()输出json格式,NewConsoleEncoder()输出普通文本格式
        core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)    //第三个及之后的参数为写入文件的日志级别,ErrorLevel模式只记录error级别的日志
        log = zap.New(core,zap.AddCaller())                                //AddCaller()为显示文件名和行号
        log.Info("hello world")
        log.Error("hello world")
    }
    

    日志文件输出结果:

    2020-12-16T17:53:30.466+0800    INFO    geth/main.go:18 hello world
    2020-12-16T17:53:30.486+0800    ERROR   geth/main.go:19 hello world
    

    同时输出控制台和文件

    如果需要同时输出控制台和文件,只需要改造一下zapcore.NewCore即可,示例:

    package main
    
    import (
        "github.com/natefinch/lumberjack"
        "go.uber.org/zap"
        "go.uber.org/zap/zapcore"
        "os"
    )
    
    var log *zap.Logger
    
    func main() {
        //获取编码器,NewJSONEncoder()输出json格式,NewConsoleEncoder()输出普通文本格式
        encoderConfig := zap.NewProductionEncoderConfig()
        encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder //指定时间格式
        encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
        encoder := zapcore.NewConsoleEncoder(encoderConfig)
    
        //文件writeSyncer
        fileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
            Filename:   "./info.log", //日志文件存放目录
            MaxSize:    1,            //文件大小限制,单位MB
            MaxBackups: 5,            //最大保留日志文件数量
            MaxAge:     30,           //日志文件保留天数
            Compress:   false,        //是否压缩处理
        })
        fileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(fileWriteSyncer,zapcore.AddSync(os.Stdout)), zapcore.DebugLevel) //第三个及之后的参数为写入文件的日志级别,ErrorLevel模式只记录error级别的日志
    
        log = zap.New(fileCore, zap.AddCaller()) //AddCaller()为显示文件名和行号
    
    
        log.Info("hello world")
        log.Error("hello world")
    }
    

    文件切割

    日志文件会随时间越来越大,为了避免日志文件把硬盘空间占满,需要按条件对日志文件进行切割,zap包本身不提供文件切割的功能,但是可以用zap官方推荐的lumberjack包处理

        //文件writeSyncer
        fileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
            Filename:   "./info.log", //日志文件存放目录,如果文件夹不存在会自动创建
            MaxSize:    1,            //文件大小限制,单位MB
            MaxBackups: 5,            //最大保留日志文件数量
            MaxAge:     30,           //日志文件保留天数
            Compress:   false,        //是否压缩处理
        })
    

    按级别写入文件

    为了管理人员的查询方便,一般我们需要将低于error级别的放到info.log,error及以上严重级别日志存放到error.log文件中,我们只需要改造一下zapcore.NewCore方法的第3个参数,然后将文件WriteSyncer拆成infoerror两个即可,示例:

    package main
    
    import (
        "github.com/natefinch/lumberjack"
        "go.uber.org/zap"
        "go.uber.org/zap/zapcore"
        "os"
    )
    
    var log *zap.Logger
    
    func main() {
        var coreArr []zapcore.Core
    
        //获取编码器
        encoderConfig := zap.NewProductionEncoderConfig()               //NewJSONEncoder()输出json格式,NewConsoleEncoder()输出普通文本格式
        encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder           //指定时间格式
        encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder    //按级别显示不同颜色,不需要的话取值zapcore.CapitalLevelEncoder就可以了
        //encoderConfig.EncodeCaller = zapcore.FullCallerEncoder        //显示完整文件路径
        encoder := zapcore.NewConsoleEncoder(encoderConfig)
    
        //日志级别
        highPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool{  //error级别
            return lev >= zap.ErrorLevel
        })
        lowPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {  //info和debug级别,debug级别是最低的
            return lev < zap.ErrorLevel && lev >= zap.DebugLevel
        })
    
        //info文件writeSyncer
        infoFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
            Filename:   "./log/info.log",   //日志文件存放目录,如果文件夹不存在会自动创建
            MaxSize:    1,                  //文件大小限制,单位MB
            MaxBackups: 5,                  //最大保留日志文件数量
            MaxAge:     30,                 //日志文件保留天数
            Compress:   false,              //是否压缩处理
        })
        infoFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoFileWriteSyncer,zapcore.AddSync(os.Stdout)), lowPriority) //第三个及之后的参数为写入文件的日志级别,ErrorLevel模式只记录error级别的日志
        //error文件writeSyncer
        errorFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
            Filename:   "./log/error.log",      //日志文件存放目录
            MaxSize:    1,                      //文件大小限制,单位MB
            MaxBackups: 5,                      //最大保留日志文件数量
            MaxAge:     30,                     //日志文件保留天数
            Compress:   false,                  //是否压缩处理
        })
        errorFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(errorFileWriteSyncer,zapcore.AddSync(os.Stdout)), highPriority) //第三个及之后的参数为写入文件的日志级别,ErrorLevel模式只记录error级别的日志
    
        coreArr = append(coreArr, infoFileCore)
        coreArr = append(coreArr, errorFileCore)
        log = zap.New(zapcore.NewTee(coreArr...), zap.AddCaller()) //zap.AddCaller()为显示文件名和行号,可省略
    
        log.Info("hello info")
        log.Debug("hello debug")
        log.Error("hello error")
    }
    

    这样修改之后,infodebug级别的日志就存放到info.logerror级别的日志单独放到error.log文件中了

    控制台按级别显示颜色

    指定编码器的EncodeLevel即可,

        //获取编码器
        encoderConfig := zap.NewProductionEncoderConfig()               //NewJSONEncoder()输出json格式,NewConsoleEncoder()输出普通文本格式
        encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder           //指定时间格式
        encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder    //按级别显示不同颜色,不需要的话取值zapcore.CapitalLevelEncoder就可以了
        encoder := zapcore.NewConsoleEncoder(encoderConfig)
    

    显示文件路径和行号

    前面说到要显示文件路径和行号,只需要zap.New方法添加参数zap.AddCaller()即可,如果要显示完整的路径,需要在编码器配置中指定

        //获取编码器
        encoderConfig := zap.NewProductionEncoderConfig()               //NewJSONEncoder()输出json格式,NewConsoleEncoder()输出普通文本格式
        encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder           //指定时间格式
        encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder    //按级别显示不同颜色,不需要的话取值zapcore.CapitalLevelEncoder就可以了
        encoderConfig.EncodeCaller = zapcore.FullCallerEncoder          //显示完整文件路径
        encoder := zapcore.NewConsoleEncoder(encoderConfig)
    

    完整代码

    package main
    
    import (
        "github.com/natefinch/lumberjack"
        "go.uber.org/zap"
        "go.uber.org/zap/zapcore"
        "os"
    )
    
    var log *zap.Logger
    
    func main() {
        var coreArr []zapcore.Core
    
        //获取编码器
        encoderConfig := zap.NewProductionEncoderConfig()               //NewJSONEncoder()输出json格式,NewConsoleEncoder()输出普通文本格式
        encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder           //指定时间格式
        encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder    //按级别显示不同颜色,不需要的话取值zapcore.CapitalLevelEncoder就可以了
        //encoderConfig.EncodeCaller = zapcore.FullCallerEncoder        //显示完整文件路径
        encoder := zapcore.NewConsoleEncoder(encoderConfig)
    
        //日志级别
        highPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool{  //error级别
            return lev >= zap.ErrorLevel
        })
        lowPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {  //info和debug级别,debug级别是最低的
            return lev < zap.ErrorLevel && lev >= zap.DebugLevel
        })
    
        //info文件writeSyncer
        infoFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
            Filename:   "./log/info.log",   //日志文件存放目录,如果文件夹不存在会自动创建
            MaxSize:    2,                  //文件大小限制,单位MB
            MaxBackups: 100,                //最大保留日志文件数量
            MaxAge:     30,                 //日志文件保留天数
            Compress:   false,              //是否压缩处理
        })
        infoFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoFileWriteSyncer,zapcore.AddSync(os.Stdout)), lowPriority) //第三个及之后的参数为写入文件的日志级别,ErrorLevel模式只记录error级别的日志
        //error文件writeSyncer
        errorFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
            Filename:   "./log/error.log",      //日志文件存放目录
            MaxSize:    1,                      //文件大小限制,单位MB
            MaxBackups: 5,                      //最大保留日志文件数量
            MaxAge:     30,                     //日志文件保留天数
            Compress:   false,                  //是否压缩处理
        })
        errorFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(errorFileWriteSyncer,zapcore.AddSync(os.Stdout)), highPriority) //第三个及之后的参数为写入文件的日志级别,ErrorLevel模式只记录error级别的日志
    
        coreArr = append(coreArr, infoFileCore)
        coreArr = append(coreArr, errorFileCore)
        log = zap.New(zapcore.NewTee(coreArr...), zap.AddCaller()) //zap.AddCaller()为显示文件名和行号,可省略
    
    
        log.Info("hello info")
        log.Debug("hello debug")
        log.Error("hello error")
    }
    

    参考文章

    在Go语言项目中使用Zap日志库 - 知乎 (zhihu.com)

    golang高性能日志库zap配置示例 - ExplorerMan - 博客园 (cnblogs.com)

    相关文章

      网友评论

        本文标题:golang高性能日志库zap的使用

        本文链接:https://www.haomeiwen.com/subject/nwjzgktx.html