首页 > 其他分享 >Golang一日一库之 日志库 zap

Golang一日一库之 日志库 zap

时间:2023-04-12 22:23:53浏览次数:51  
标签:zapcore log Golang 一库 日志 message zap encoderConfig

简介

在开发过程中 会使用到日志库去记录错误的日志,尤其是golang中 有无穷无尽的error 如果不记录,当你的代码出错,就无从排错了。
zap 是开源的 Go 高性能日志库 主要有以下特点:

  1. 支持不同的日志级别
  2. 能够打印基本信息等但不支持日志的分割 但是可以使用 lumberjack 也是 zap 官方推荐用于日志分割

官网:https://github.com/uber-go/zap
https://pkg.go.dev/go.uber.org/zap#section-readme

安装

go get -u go.uber.org/zap

zap只支持Go的两个最新小版本。

日志记录器 logger和 sugared logger

zap库的使用与其他的日志库非常相似。先创建一个logger,然后调用各个级别的方法记录日志
而 zap库给我们提供两种模式的日志记录

  1. Logger
  2. Sugared Logger
    至于你想问他们之间有什么区别,很简单,我们先来看代码
    这里我就直接用官网的例用代码了

Logger

logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("failed to fetch URL",
	// Structured context as strongly typed Field values.
	zap.String("url", "https://www.baidu.com"),
	zap.Int("attempt", 3),
	zap.Duration("backoff", time.Second),
)


说实话我是很不喜欢logger模式的日志的
调用起来是真的麻烦 还要指定 int类型 string类型 这个类型那个类型
但优点也很明显那就是 而且内存分配少性能至上

Sugared Logger

logger, _ := zap.NewProduction()
defer logger.Sync() // flushes buffer, if any
sugar := logger.Sugar()
sugar.Infow("failed to fetch URL",
	// Structured context as loosely typed key-value pairs.
	"url", "https://www.baidu.com",
	"attempt", 3,
	"backoff", time.Second,
)
sugar.Infof("Failed to fetch URL: %s", "https://www.baidu.com")

这种就是printf风格的调用起来方便即开即用

Example 和 Production 以及development

Example

log := zap.NewExample()
log.Debug("this is debug message")
log.Info("this is info message")
log.Info("this is info message with fileds",
	zap.Int("age", 24), zap.String("agender", "man"))
log.Warn("this is warn message")
log.Error("this is error message")
log.Panic("this is panic message")

结果

Production

log, _ := zap.NewProduction()
log.Debug("this is debug message")
log.Info("this is info message")
log.Info("this is info message with fileds",
	zap.Int("age", 24), zap.String("agender", "man"))
log.Warn("this is warn message")
log.Error("this is error message")
log.Panic("this is panic message")

结果

NewDevelopment

log, _ := zap.NewDevelopment()
log.Debug("this is debug message")
log.Info("this is info message")
log.Info("this is info message with fileds",
	zap.Int("age", 24), zap.String("agender", "man"))
log.Warn("this is warn message")
log.Error("this is error message")
log.Panic("this is panic message")

结果

三者对比

由上文可见
Example和Production使用的是json格式
而development使用行的形式
除此之外
Example和Production 所输出的多少也不一样。

具体如下:

Development

  • 从警告级别向上打印到堆栈中来跟踪
  • 始终打印包/文件/行(方法)
  • 在行尾添加任何额外字段作为json字符串
  • 以大写形式打印级别名称
  • 以毫秒为单位打印ISO8601格式的时间戳

Production

  • 调试级别消息不记录
  • Error,Dpanic级别的记录,会在堆栈中跟踪文件,warn不会
  • 始终将调用者添加到文件中
  • 以时间戳格式打印日期
  • 以小写形式打印级别名称

调整日志输出的 格式

如下文代码所示

func getEncoder() zapcore.Encoder {
    encoderConfig := zap.NewDevelopmentEncoderConfig()
    {
        // LevelKey值变为 level
        encoderConfig.LevelKey = "level"
        // MessageKey值变为 msg
        encoderConfig.MessageKey = "msg"
        // TimeKey值 变成time
        encoderConfig.TimeKey = "time"
        // 把输出的info 变成INFO 只需要丢对象 不许执行
        encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
        // 对时间进行格式化处理
        encoderConfig.EncodeTime = func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {
            encoder.AppendString(t.Local().Format("2006-01-02 15:04:05"))
        }
    }

    return zapcore.NewJSONEncoder(encoderConfig)
}

如上代码所示,可以调节任意位置,我注释也标的很清楚

使用lumberjack进行配合

官网: https://pkg.go.dev/gopkg.in/natefinch/lumberjack.v2
zap没有切割日志的功能,所以我们必须借助第三方库来实现

使用

要将 lumberjack 与标准库的日志包一起使用,只需在应用程序启动时将其传递到 SetOutput 函数中即可。

log.SetOutput(&lumberjack.Logger{
    Filename:   "/var/log/myapp/foo.log",
    MaxSize:    500, // megabytes
    MaxBackups: 3,
    MaxAge:     28, //days
    Compress:   true, // disabled by default
})

如果要和Zap所结合的话 需要放入到zapcore.AddSync中

zapcore.AddSync(&lumberjack.Logger{
    Filename:   "/var/log/myapp/foo.log",
    MaxSize:    500, // megabytes
    MaxBackups: 3,
    MaxAge:     28, //days
    Compress:   true, // disabled by default
}) 

声明日志并且初始化使用

我们上文以及配置好了日志的格式以及规定了日志输出的位置 也做好了 lumberjack日志的切割
那我们该如何初始化呢
只需要声明core 然后把这三个丢进去即可
如下代码所示

core := zapcore.NewCore(getEncoder(), zapcore.NewMultiWriteSyncer(zapcore.AddSync(&lumberjack.Logger{
    Filename:   "/var/log/myapp/foo.log",
    MaxSize:    500, // megabytes
    MaxBackups: 3,
    MaxAge:     28, //days
    Compress:   true, // disabled by default
}) , zapcore.AddSync(os.Stdout)), zapcore.DebugLevel)
zap.New(core).Sugar()

当然 会发现 我还加了一个值 zapcore.AddSync(os.Stdout))
这句代码是代表除了输出到文件中还会输出到终端中,完成多个终端的输出

完整代码 日志库初始化组件

package conf

import (
    "fmt"
    "github.com/natefinch/lumberjack"
    "github.com/spf13/viper"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "os"
    "path/filepath"
    "time"
)

func InitLogger() *zap.SugaredLogger {
    logMode := zapcore.InfoLevel

    if viper.GetBool("model.development") {
        logMode = zapcore.DebugLevel
    }
    // 第一个参数是输出的格式 第二个参数 输出的位置

    //zapcore.NewMultiWriteSyncer 输出到多个终端 比如 文件 console中
    core := zapcore.NewCore(getEncoder(), zapcore.NewMultiWriteSyncer(getWriterSyncer(), zapcore.AddSync(os.Stdout)), logMode)
    return zap.New(core).Sugar()
}

// def 输出日志的格式
func getEncoder() zapcore.Encoder {
    encoderConfig := zap.NewDevelopmentEncoderConfig()
    {
        // LevelKey值变为 level
        encoderConfig.LevelKey = "level"
        // MessageKey值变为 msg
        encoderConfig.MessageKey = "msg"
        // TimeKey值 变成time
        encoderConfig.TimeKey = "time"
        // 把输出的info 变成INFO 只需要丢对象 不许执行
        encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
        // 对时间进行格式化处理
        encoderConfig.EncodeTime = func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {
            encoder.AppendString(t.Local().Format("2006-01-02 15:04:05"))
        }
    }

    return zapcore.NewJSONEncoder(encoderConfig)
}

// def 日志要输出到什么地方
func getWriterSyncer() zapcore.WriteSyncer {
    stSeparator := string(filepath.Separator)
    stRootDir, _ := os.Getwd()
    stLogFilePath := stRootDir + stSeparator + "log" + stSeparator + time.Now().Format("2006-01-02") + ".log"
    fmt.Println(stLogFilePath)

    // 日志分割
    hook := lumberjack.Logger{
        Filename:   stLogFilePath,                  // 日志文件路径,默认 os.TempDir()
        MaxSize:    viper.GetInt("log.MaxSize"),    // 每个日志文件保存500M,默认 100M
        MaxBackups: viper.GetInt("log.MaxBackups"), // 保留3个备份,默认不限
        MaxAge:     viper.GetInt("log.MaxAge"),     // 保留28天,默认不限
        Compress:   viper.GetBool("log.Compress"),  // 是否压缩,默认不压缩
    }

    return zapcore.AddSync(&hook)
}

标签:zapcore,log,Golang,一库,日志,message,zap,encoderConfig
From: https://www.cnblogs.com/zichliang/p/17311480.html

相关文章

  • golang中的错误处理
    0.1、索引https://waterflow.link/articles/16667167272361、panic当我们执行panic的时候会结束下面的流程:packagemainimport"fmt"funcmain(){ fmt.Println("hello") panic("stop") fmt.Println("world")}gorun9.gohellopanic:s......
  • 如何用Golang处理每分钟100万个请求
    用Golang处理每分钟100万个请求转载请注明来源:https://janrs.com/9yaq面临的问题在我设计一个分析系统中,我们公司的目标是能够处理来自数百万个端点的大量POST请求。web网络处理程序将收到一个JSON文档,其中可能包含许多有效载荷的集合,需要写入AmazonS3,以便我们的地图还原......
  • [golang]使用logrus自定义日志模块
    简介logrus是一个第三方日志库,性能虽不如zap和zerolog,但方便易用灵活。logrus完全兼容标准的log库,还支持文本、JSON两种日志输出格式。特点相较于标准库,logrus有更细致的日志级别,从高到低分别是:trace>debug>info>warn>error>fatal>panic支持自定义日志格式,内置支......
  • ChatGPT垂直行业私有数据知识库功能-咨询接口采用流式响应输出-JS和Golang实现流式响
    近期开发私有数据知识库功能,想要实现和ChatGPT聊天效果类似的逐字流式输出展示效果。GPT3.5本身就有流式聊天补全接口,后端Golang对接后,也需要能流式输出。下面就介绍下前端JS后端Golang来实现这种输出效果 大部分介绍是使用EventStream来实现,我现在不使用EventStream也来实现......
  • Golang基础-- select的用法
    select是golang在语言层面提供的多路IO复用的机制,其可以检测多个channel是否ready三个题目示例来说明一下select的大概作用:题目一:声明两个channel,分别为chan1和chan2,依次启动两个协程,分别向两个channel中写入一个数据就进入睡眠。select语句两个case分别检测chan1和chan2是......
  • golang 中的 goto 用法和使用场景(转)
    转自:golang中的goto场景一:跳出多重循环packagemainimport"fmt"funcmain(){forx:=0;x<10;x++{fory:=0;y<10;y++{ify==2{//跳转到标签gotobreakHere}......
  • golang 编译碰到问题 Package python-2.7 was not found in the pkg-config search pa
    golang运行单测或者编译程序时提示需要配置PKG_CONFIG_PATH环境变量,原因是在程序里使用了go-python包,要求运行环境有python2.7,并设置PKG_CONFIG_PATH环境变量,解决方案如下:#pkg-config--cflags--python-2.7Packagepython-2.7wasnotfoundinthepkg-configsear......
  • Golang与Java全方位对比总结
    本文针对Golang与Java的基础语法、结构体函数、异常处理、并发编程及垃圾回收、资源消耗等各方面的差异进行对比总结,有不准确、不到位的地方还请大家不吝赐教。一、基础语法Golang:编码风格及可见域规则严格且简单;Java:来说层次接口清晰、规范,主要表现有以下这些。1、变量......
  • Golang基础--defer的用法
    defer语句用于延迟函数的调用,每次defer都会把一个函数压入栈中,函数返回前再把延迟的函数取出执行。三个示例:import"fmt"funcmain(){varinit=1deferfmt.Println(init)init=2}输出1。延迟函数fmt.Println(aInt)的参数在defer语句出现时就已经确......
  • Golang回调函数
    Golang回调函数实例二则定义回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于......