go 包:zap日志

2023-03-29  本文已影响0人  呦丶耍脾气

1.介绍

Zapuber开源的日志库,支持日志级别分级 、结构化记录,对性能和内存分配做了极致的优化。目前 Star 12.8 源码地址: https://github.com/uber-go/zap

官方性能测试图

2.安装

go get -u gopkg.in/natefinch/lumberjack.v2

3.日志记录器

Zap提供了两种类型的日志记录器: SugaredLoggerLogger,两者对比如下:

SugaredLogger : 在性能很好但不是很关键的上下文中使用,它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。与 log15go-kit 一样,SugaredLogger 的结构化日志 api 类型灵活,并接受可变的键值对的数量。

Logger : 在每一微秒和每一次内存分配都很重要的上下文中,使用Logger。它甚至比SugaredLogger更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。

4.创建Logger

4.1 创建Logger几种方式

Zap中通过调用zap.NewProduction()zap.NewDevelopment()或者zap.Example()可创建一个Logger

他们创建的Logger,唯一的区别在于它将记录的信息不同。

使用场景如下:

4.2 使用示例

a.代码
func TestCreateLogger(t *testing.T) {
    // 初始化logger
    logger := zap.NewExample()
    // 使用defer logger.Sync()将缓存同步到文件中。
    defer logger.Sync()
    // 记录日志
    logger.Info("NewExample",
        zap.String("name","张三"),
        zap.Int("age",18),
    )
    productionLogger, _ := zap.NewProduction()
    defer productionLogger.Sync()
    productionLogger.Info("NewProduction",
        zap.String("name","张三"),
        zap.Int("age",18),
    )
    devLogger, _ := zap.NewDevelopment()
    defer devLogger.Sync()
    devLogger.Info("NewDevelopment",
        zap.String("name","张三"),
        zap.Int("age",18),
    )
}
b. 输出
=== RUN   TestCreateLogger
{"level":"info","msg":"NewExample","name":"张三","age":18}
{"level":"info","ts":1624005421.7035909,"caller":"test/zap_test.go:25","msg":"NewProduction","name":"张三","age":18}
2021-06-18T16:37:01.703+0800    INFO    test/zap_test.go:31    NewDevelopment    {"name": "张三", "age": 18}
--- PASS: TestCreateLogger (0.00s)
PASS

zap底层 API 可以设置缓存,所以一般使用defer logger.Sync()将缓存同步到文件中

4.3 总结

  1. 使用NewProduction()记录日志,默认会记录调用函数信息、日期和时间。
  2. NewExampleNewProduction() 默认都是使用json格式记录日志,而NewDevelopment不是。
  3. 默认情况下日志都会打印到应用程序的控制台界面。
  4. 记录日志时,尽量调用zap.T(key,val)对应的类型方法,这也是zap高性能原因的一部分。

5.记录日志

5.1 使用默认记录器(Logger)

a.代码示例
// 使用默认记录日志
func TestRecordLogWithDefault(t *testing.T) {
    // 初始化记录器(使用默认记录器)
    logger := zap.NewExample()
    defer logger.Sync()
    // 记录日志
    logger.Debug("这是debug日志")
    logger.Debug("这是debug日志",zap.String("name","张三"))
    logger.Info("这是info日志",zap.Int("age",18))
    logger.Error("这是error日志",zap.Int("line",130),zap.Error(fmt.Errorf("错误示例")))
    logger.Warn("这是Warn日志")
    // 下面两个都会中断程序
    //logger.Fatal("这是Fatal日志")
    //logger.Panic("这是Panic日志")
}
b.输出
=== RUN   TestRecordLogWithDefault
{"level":"debug","msg":"这是debug日志"}
{"level":"debug","msg":"这是debug日志","name":"张三"}
{"level":"info","msg":"这是info日志","age":18}
{"level":"error","msg":"这是error日志","line":130,"error":"错误示例"}
{"level":"warn","msg":"这是Warn日志"}
--- PASS: TestRecordLogWithDefault (0.00s)

5.2 使用默认记录器(Sugar)

a.代码示例
// 使用Sugar记录器
func TestRecordLogWithSuage(t *testing.T) {
    // 初始化记录器
    logger := zap.NewExample()
    // 把日志记录器转成Sugar
    sugarLogger := logger.Sugar()
    defer sugarLogger.Sync()
    // 记录日志
    sugarLogger.Debug("这是debug日志 ",zap.String("name","张三"))
    sugarLogger.Debugf("这是Debugf日志 name:%s ","张三")
    sugarLogger.Info("这是info日志",zap.Int("age",18))
    sugarLogger.Infof("这是Infof日志  内容:%v",map[string]string{"爱好":"动漫"})
    sugarLogger.Error("这是error日志",zap.Int("line",130),zap.Error(fmt.Errorf("错误示例")))
    sugarLogger.Errorf("这是Errorf日志,错误信息:%s","错误报告!")
    sugarLogger.Warn("这是Warn日志")
    sugarLogger.Warnf("这是Warnf日志 %v",[]int{1,2,4,5})
    // 下面两个都会中断程序
    //sugarLogger.Fatal("这是Fatal日志")
    //sugarLogger.Panic("这是Panic日志")
}
b.输出
=== RUN   TestRecordLogWithSuage
{"level":"debug","msg":"这是debug日志 {name 15 0 张三 <nil>}"}
{"level":"debug","msg":"这是Debugf日志 name:张三 "}
{"level":"info","msg":"这是info日志{age 11 18  <nil>}"}
{"level":"info","msg":"这是Infof日志  内容:map[爱好:动漫]"}
{"level":"error","msg":"这是error日志{line 11 130  <nil>} {error 26 0  错误示例}"}
{"level":"error","msg":"这是Errorf日志,错误信息:错误报告!"}
{"level":"warn","msg":"这是Warn日志"}
{"level":"warn","msg":"这是Warnf日志 [1 2 4 5]"}
--- PASS: TestRecordLogWithSuage (0.00s)
PASS

6.定制Logger

除了zap.NewProduction()zap.NewDevelopment()zap.Example()还可以通过zap.New(...)创建一个Logger

6.1 定制一: 输出到文件

a.代码示例
func Test2File(t *testing.T) {
    // 指定写入文件
    fileHandle, _ := os.Create("./test.log")
    writeFile := zapcore.AddSync(fileHandle)
    // 设置日志输出格式为JSON (参数复用NewDevelopmentEncoderConfig)
    encoder := zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig())
    // 返回zapcore.Core,并指定记录zap.DebugLevel级别及以上日志
    zcore := zapcore.NewCore(encoder, zapcore.Lock(writeFile), zap.DebugLevel)
    // 创建日志记录器
    logger := zap.New(zcore)
    defer logger.Sync()
    // 记录日志
    logger.Info("输出日志到文件", zap.String("name", "张三"))
}

6.2 定制二: 同时输入文件和控制台

// 同时输入到文件和控制台
func TestPrintFileAndStd(t *testing.T) {
    // 指定写入文件
    fileHandle, _ := os.Create("./test.log")
  // 同时写入文件和控制台 (只修改这一行)
    writeFile := zapcore.NewMultiWriteSyncer(fileHandle,os.Stdout)
    // 设置日志输出格式为JSON (参数复用NewDevelopmentEncoderConfig)
    encoder := zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig())
    // 返回zapcore.Core
    zcore := zapcore.NewCore(encoder, zapcore.Lock(writeFile), zap.DebugLevel)
    // 创建日志记录器
    logger := zap.New(zcore)
    defer logger.Sync()
    // 记录日志
    logger.Info("输出日志到文件", zap.String("name", "张三"))
}

7.切割日志

Zap本身不支持文件切割和日志归档,好在开源强大,贡献出Lumberjack,它是一个Go包,用于将日志写入滚动文件。

7.1 安装Lumberjack

go get -u github.com/natefinch/lumberjack

7.2 集成到Zap

a. 代码示例
// 获取文件切割和归档配置信息
func getLumberjackConfig() zapcore.WriteSyncer {
    lumberjackLogger := &lumberjack.Logger{
        Filename: "./zap.log",//日志文件
        MaxSize: 1,//单文件最大容量(单位MB)
        MaxBackups: 3,//保留旧文件的最大数量
        MaxAge: 1,// 旧文件最多保存几天
        Compress: false, // 是否压缩/归档旧文件
    }
    return zapcore.AddSync(lumberjackLogger)
}

// 测试日志切割和归档
func TestCutAndArchive(t *testing.T) {
    // 设置日志输出格式为JSON (参数复用NewDevelopmentEncoderConfig)
    encoder := zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig())
    core := zapcore.NewCore(encoder, getLumberjackConfig(), zap.DebugLevel)
    sugarLogger := zap.New(core).Sugar()
    defer sugarLogger.Sync()
    // 记录日志
    sugarLogger.Infof("日志内容:%s",strings.Repeat("日志",90000))
}
b. 效果

在代码中设置单文件最大容量为1MB,如上图所示当文件超过1MB(527+527 > 1024)时,则分割。

上一篇下一篇

猜你喜欢

热点阅读