返回顶部
首页 > 资讯 > 后端开发 > GO >Golang定制化zap日志库如何使用
  • 457
分享到

Golang定制化zap日志库如何使用

2023-07-05 19:07:15 457人浏览 独家记忆
摘要

本篇内容主要讲解“golang定制化zap日志库如何使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Golang定制化zap日志库如何使用”吧!为什么需要日志一个产品的诞生一定是因为有需求!新

本篇内容主要讲解“golang定制化zap日志库如何使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习Golang定制化zap日志库如何使用”吧!

为什么需要日志

一个产品的诞生一定是因为有需求!新技术大部分都是为了更加便利和实用而诞生的,日志也不例外。日志顾名思义就是对整个项目的事件进行记录。日志可以帮助我们查看某一天中某一时刻项目的运转情况等等。

日志的好处

在日常开发过程中难免会遇到BUG出现的情况,日志可以记录这些BUG出现的地点从而方便进行快速定位和排查。可以根据需求对日志进行自定义的输出,比如输出到控制台、文件等。日志也可以帮助我们在开发过程中检测到程序潜在的问题和程序运行的流程,能够有效的提高我们的开发效率。

日志都有什么

要让程序记录有效的,便利的日志。** Logger (日志记录器) 应该具备以下特点**:

  • 可以将日志信息输出到控制台、文件等地方,输出到文件便于项目长久运行,输出到控制台有助于开发过程中检错的效率。

  • 一个日志应该具有多个基本的级别,比如infodebugwarnerrorfatal等,他们可以对日志进行分类。

  • 可以对日志进行切割,按照日志大小、日期、时间间隔等因素分割。

  • 可以手动或自动记录一些开发信息。如前端传入的数据,异常错误信息,程序运行结果,错误行数,日志打印位置等等信息进行打印。

Go中默认的日志

Go语言中默认集成了一个log日志库

func New(out io.Writer, prefix string, flag int) *Logger {   l := &Logger{out: out, prefix: prefix, flag: flag}   if out == io.Discard {      l.isDiscard = 1   }   return l}

使用New可以获取到该日志对象。第一个参数为实现了Writer接口的对象。可以使用os.OpenFile()选择一个文件,然后将该文件对象作为输出,也可以使用os.Stdoutos.Stderr输出到控制台。第二个参数需要传入一个日志信息每一行的前缀(如果输出到控制台该处可以填空字符串)。第三个参数是设置打印默认信息的能力,比如打印时间等。

测试日志

var l *log.Loggerfunc main() {l.Printf("main method exec fail, err: %v", errors.New("nil Pointer error"))l.Println("test go log status")l.Fatal("wait five seconds")time.Sleep(time.Second * 5)l.Println("five seconds after!")}func init() {l = log.New(os.Stdout, "[我是一个前缀]", log.LstdFlags)}

打印信息

[我是一个前缀]2023/02/10 21:15:22 main method exec fail, err: nil Pointer error

[我是一个前缀]2023/02/10 21:15:22 test go log status

[我是一个前缀]2023/02/10 21:15:22 wait five seconds

// Fatal is equivalent to l.Print() followed by a call to os.Exit(1).func (l *Logger) Fatal(v ...any) {   l.Output(2, fmt.Sprint(v...))   os.Exit(1)}

Fatal之后的程序均不会执行,因为Fatal执行后会在内部调用os.Exit(1),从而在打印结束后退出进程。

goLogger的不足

  • 日志级别只支持Fatal,只有一个Print函数,没有其他级别

  • 日志自定义参数过少,无法打印栈信息,无法确定请求位置等

  • FatalPainc都是执行后退出,无法容忍错误情况的出现就会退出程序

  • 无法指定输出格式,只能以文本形式进行输出,没有根据日志大小、时间间隔、日期进行分割的能力

虽然gologger支持并发,但也只限于简单用着还行,实际开发用起来并不舒服的情况。

Zap日志库

引入日志库依赖

go get -u go.uber.org/zap

zap日志库是Uber开源的。性能很好,因为不用反射实现,但需要自己去手动指明打印信息的类型(下面会有示例)。个人觉得自己指定打印还是挺舒服的。zap的使用率非常高,不仅支持日志库的基本功能,而且很灵活的支持你去进一步的封装或者定制化。zap支持异步打印。

如何使用zap

格式化配置

func NewDevelopmentEncoderConfig() zapcore.EncoderConfig
func NewProductionEncoderConfig() zapcore.EncoderConfig
func NewProductionConfig() Config
func NewDevelopmentConfig() Config

这里可以根据实际生产和测试环境需求进行选择,也可以直接使用其他初始化方式。

// NewProductionEncoderConfig returns an opiNIOnated EncoderConfig for// production environments.func NewProductionEncoderConfig() zapcore.EncoderConfig {   return zapcore.EncoderConfig{       // 设置log内容里的一些属性的key      TimeKey:        "ts",//时间对应的key名      Levelkey:       "level",//日志级别对应的key名      NameKey:        "logger",//logger名对应的key名      CallerKey:      "caller",//调用者对应的key名      FunctionKey:    zapcore.OmiTKEy,      MessageKey:     "msg",//日志内容对应的key名,此参数必须不为空,否则日志主体不处理      StacktraceKey:  "stacktrace",//栈追踪的key名       // const DefaultLineEnding = "\n" 行末输出格式      LineEnding:     zapcore.DefaultLineEnding,       // 日志编码级别      EncodeLevel:    zapcore.LowercaseLevelEncoder,       // 日志时间解析      EncodeTime:     zapcore.EpochTimeEncoder,       // 日志日期解析      EncodeDuration: zapcore.SecondsDurationEncoder,       // 日志调用路径      EncodeCaller:   zapcore.ShortCallerEncoder,   }}

使用NewProductionEncoderConfig()创建的 Logger 在记录日志时会自动记录调用函数的信息、打日志的时间,日志级别等信息。

EncodeLevel
// A LevelEncoder serializes a Level to a primitive type.type LevelEncoder func(Level, PrimitiveArrayEncoder)// 将日志级别进行大写并带上颜色func CapitalColorLevelEncoder(l Level, enc PrimitiveArrayEncoder)// 将日志级别大写不带颜色func CapitalLevelEncoder(l Level, enc PrimitiveArrayEncoder)// 将日志级别小写带上颜色func LowercaseColorLevelEncoder(l Level, enc PrimitiveArrayEncoder)// 将日志级别小写不带颜色func LowercaseLevelEncoder(l Level, enc PrimitiveArrayEncoder)

需要实现LevelEncoder接口。可以调整日志编码级别,并且选择带上或者不带输出颜色。

EncodeTime
// A TimeEncoder serializes a time.Time to a primitive type.type TimeEncoder func(time.Time, PrimitiveArrayEncoder)// 根据不同时间进行格式化func EpochTimeEncoder(t time.Time, enc PrimitiveArrayEncoder)func EpochMillisTimeEncoder(t time.Time, enc PrimitiveArrayEncoder)func EpochNanosTimeEncoder(t time.Time, enc PrimitiveArrayEncoder)

定制化时间格式解析,需要实现TimeEncoder接口。

EncodeDruation
// A DurationEncoder serializes a time.Duration to a primitive type.type DurationEncoder func(time.Duration, PrimitiveArrayEncoder)// 将日期根据不同时间进行格式化func SecondsDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder)func NanosDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder)func MillisDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder)func StringDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder)

定制日期格式解析。需要实现DruationEncoder接口

定制化zap

编码格式
encoderConfig := zap.NewProductionEncoderConfig()// 打印级别为大写 & 彩色encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder// 时间编码进行指定格式解析 layout -> "[2006-01-02 15:04:05]"encoderConfig.EncodeTime = parseTime(settings.Conf.Layout)

修改日志打印级别和时间编码格式

// parseTime 进行时间格式处理func parseTime(layout string) zapcore.TimeEncoder {   return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {      type appendTimeEncoder interface {         AppendTimeLayout(time.Time, string)      }      if enc, ok := enc.(appendTimeEncoder); ok {         enc.AppendTimeLayout(t, layout)         return      }      enc.AppendString(t.FORMat(layout))   }}

实现zapcore.TimeEncoder接口,将指定的Layout参数进行传入实现闭包即可。

日志分割
// 日志输出配置, 借助另外一个库 lumberjack 协助完成日志切割。lumberjackLogger := &lumberjack.Logger{   Filename:   settings.Conf.Filename,   // -- 日志文件名   MaxSize:    settings.Conf.MaxSize,    // -- 最大日志数 M为单位!!!   MaxAge:     settings.Conf.MaxAge,     // -- 最大存在天数   MaxBackups: settings.Conf.MaxBackups, // -- 最大备份数量   Compress:   false,                    // --是否压缩}syncer := zapcore.AddSync(lumberjackLogger)

zap日志本身不支持日志切割,借助另外一个库 lumberjack 协助完成日志切割。

// -- 用于开发者模式和生产模式之间的切换var core zapcore.Coreif settings.Conf.AppConfig.Mode == "debug" {   encoder := zapcore.NewConsoleEncoder(encoderConfig) // 输出控制台编码格式   core = zapcore.NewTee(      zapcore.NewCore(encoder, syncer, zapcore.DebugLevel), // debug级别打印到日志文件      zapcore.NewCore(encoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel), // debug级别打印到控制台   )} else {   encoder := zapcore.NewJSONEncoder(encoderConfig)// 输出json格式,便于日志检索   core = zapcore.NewCore(encoder, syncer, zapcore.InfoLevel)// info级别打印到日志文件}lg := zap.New(core, zap.AddCaller()) // --添加函数调用信息

根据配置信息去选择具体打印需求。

zap.ReplaceGlobals(lg)               // 替换该日志为全局日志var (_globalMu sync.RWMutex_globalL  = NewNop())// L returns the global Logger, which can be reconfigured with ReplaceGlobals.// It's safe for concurrent use.func L() *Logger {_globalMu.RLock()l := _globalL_globalMu.RUnlock()return l}

设置该日志为全局日志,将原日志进行替换,即可在任意位置使用zap.L()调用该日志。

完整代码

// init 初始化日志库func init() {   encoderConfig := zap.NewProductionEncoderConfig()   // 打印级别为大写 & 彩色   encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder   // 时间编码进行指定格式解析   encoderConfig.EncodeTime = parseTime(settings.Conf.Layout)   // 日志输出配置, 借助另外一个库 lumberjack 协助完成日志切割。   lumberjackLogger := &lumberjack.Logger{      Filename:   settings.Conf.Filename,   // -- 日志文件名      MaxSize:    settings.Conf.MaxSize,    // -- 最大日志数 M为单位!!!      MaxAge:     settings.Conf.MaxAge,     // -- 最大存在天数      MaxBackups: settings.Conf.MaxBackups, // -- 最大备份数量      Compress:   false,                    // --是否压缩   }   syncer := zapcore.AddSync(lumberjackLogger)   // -- 用于开发者模式和生产模式之间的切换   var core zapcore.Core   if settings.Conf.AppConfig.Mode == "debug" {      encoder := zapcore.NewConsoleEncoder(encoderConfig)      core = zapcore.NewTee(         zapcore.NewCore(encoder, syncer, zapcore.DebugLevel),         zapcore.NewCore(encoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel),      )   } else {      encoder := zapcore.NewJSONEncoder(encoderConfig)      core = zapcore.NewCore(encoder, syncer, zapcore.InfoLevel)   }   lg := zap.New(core, zap.AddCaller()) // --添加函数调用信息   zap.ReplaceGlobals(lg)               // 替换该日志为全局日志}// parseTime 进行时间格式处理func parseTime(layout string) zapcore.TimeEncoder {   return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {      type appendTimeEncoder interface {         AppendTimeLayout(time.Time, string)      }      if enc, ok := enc.(appendTimeEncoder); ok {         enc.AppendTimeLayout(t, layout)         return      }      enc.AppendString(t.Format(layout))   }}

测试日志打印情况

zap.L().Info("test info", zap.String("test String", "ok"), zap.Int("test cnt", 1))zap.L().Debug("test debug", zap.String("test String", "ok"), zap.Int("test cnt", 2))zap.L().Error("test error", zap.String("test String", "ok"), zap.Int("test cnt", 3))

[2023-02-10 22:22:17] INFO xxxxx/main.go:22 test info {“test String”: “ok”, “test cnt”: 1}

[2023-02-10 22:22:17] DEBUG xxxxx/main.go:23 test debug {“test String”: “ok”, “test cnt”: 2}

[2023-02-10 22:22:17] ERROR xxxxx/main.go:24 test error {“test String”: “ok”, “test cnt”: 3}

这里就是上述所说的自指定类型进行输出的情况。

结合gin框架进行使用

虽然gin框架有自带的logger中间件,但我们可以根据gin框架实现的原生日志和异常恢复中间件进行改造并进行替换。

Loger

// GinLogger 替换gin中默认的loggerfunc GinLogger() gin.HandlerFunc {return func(c *gin.Context) {start := time.Now()path := c.Request.URL.Pathquery := c.Request.URL.RawQueryc.Next()cost := time.Since(start)if c.Writer.Status() != Http.StatusOK {// 记录异常信息zap.L().Error(query,zap.Int("status", c.Writer.Status()),zap.String("method", c.Request.Method),zap.String("path", path),zap.String("ip", c.ClientIP()),zap.String("user-agent", c.Request.UserAgent()),zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),zap.Duration("cost", cost),)}}

如果有错误请求,只要不是状态码为200的全部进行打印->状态码、请求方法(get、post…)、路径、ip、用户授权方、错误信息、请求花费时间。

// GinRecovery recover掉项目可能出现的panicfunc GinRecovery(stack bool) gin.HandlerFunc {return func(c *gin.Context) {defer func() {if err := recover(); err != nil {// Check for a broken connection, as it is not really a// condition that warrants a panic stack trace.var brokenPipe boolif ne, ok := err.(*net.OpError); ok {if se, ok := ne.Err.(*os.SyscallError); ok {if strings.Contains(strings.ToLower(se.Error()), "broken pipe") ||strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {brokenPipe = true}}}httpRequest, _ := httputil.DumpRequest(c.Request, false)if brokenPipe {zap.L().Error(c.Request.URL.Path,zap.Any("error", err),zap.String("httpRequest", string(httpRequest)),)// If the connection is dead, we can't write a status to it.c.Error(err.(error)) // nolint: errcheckc.Abort()return}                // 这里可以选择全部打印出来不必要分割然后循环输出request := strings.Split(string(httpRequest), "\r\n")split := strings.Split(string(debug.Stack()), "\n\t")if stack {zap.L().Error("[Recovery from panic]",zap.Any("error", err))for _, str := range request {zap.L().Error("[Recovery from request panic]", zap.String("request", str))}for _, str := range split {zap.L().Error("[Recovery from Stack panic]", zap.String("stack", str))}} else {zap.L().Error("[Recovery from panic]",zap.Any("error", err))for _, str := range request {zap.L().Error("[Recovery from request panic]", zap.String("request", str))}}c.AbortWithStatus(http.StatusInternalServerError)}}()c.Next()}}

这里在Panic的时候我采用了分割循环打印的方法,也可以全部输出,但是一堆异常情况,不容易看清楚。也可以选择不打印栈轨迹输出,只需要在使用recover中间件时传入false参数即可。

到此,相信大家对“Golang定制化zap日志库如何使用”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

您可能感兴趣的文档:

--结束END--

本文标题: Golang定制化zap日志库如何使用

本文链接: https://lsjlt.com/news/354599.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Golang定制化zap日志库如何使用
    本篇内容主要讲解“Golang定制化zap日志库如何使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Golang定制化zap日志库如何使用”吧!为什么需要日志一个产品的诞生一定是因为有需求!新...
    99+
    2023-07-05
  • golang默认Logger日志库在项目中使用Zap日志库
    目录在Go语言项目中使用Zap日志库介绍默认的Go Logger日志库实现Go Logger设置Logger使用LoggerLogger的运行Go Logger的优势和劣势优势劣势U...
    99+
    2024-04-02
  • Go语言Zap日志库如何使用
    这篇“Go语言Zap日志库如何使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Go语言Zap日志库如何使用”文章吧。一、日...
    99+
    2023-07-05
  • Go之Zap日志如何使用
    这篇“Go之Zap日志如何使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Go之Zap日志如何使用”文章吧。Log包基本用...
    99+
    2023-07-02
  • golang xorm 自定义日志记录器之使用zap实现日志输出、切割日志(最新)
    目录1.准备并下载好需要的包2. 连接postgresql数据库3. zap日志工具4.实现xorm 自定义日志记录器5.使用完整代码参考文档1.准备并下载好需要的包 xorm.io...
    99+
    2024-04-02
  • 在Go语言项目中怎么使用Zap日志库
    本篇内容介绍了“在Go语言项目中怎么使用Zap日志库”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在Go语言项目中使用Zap日志库介绍在许多...
    99+
    2023-06-30
  • Go语言Zap库Logger的定制化和封装使用详解
    目录前言Go 语言原生的LoggerGo 语言原生Logger的缺点Zap 日志库Zap 的使用方法安装zap设置 Logger定制 Zap 的 Logger日志切割封装 Logge...
    99+
    2024-04-02
  • C++日志库log4cplus如何使用
    本篇内容介绍了“C++日志库log4cplus如何使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!编译&&安装下载完成在u...
    99+
    2023-07-05
  • 如何在golang中使用logger日志包
    这篇文章给大家介绍如何在golang中使用logger日志包,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。golang的优点golang是一种编译语言,可以将代码编译为机器代码,编译后的二进制文件可以直接部署到目标机器...
    99+
    2023-06-14
  • 如何使用MySQL二进制日志
    下文主要给大家带来如何使用MySQL二进制日志,希望这些内容能够带给大家实际用处,这也是我编辑如何使用MySQL二进制日志这篇文章的主要目的。好了,废话不多说,大家直接看下文吧。下载Navicat for ...
    99+
    2024-04-02
  • 如何使用Java重定向日志接口记录日志?
    当我们开发一个Java应用程序时,记录日志是非常重要的一个环节。Java提供了多种日志框架,如Log4j、Logback等。其中,重定向日志接口是一种常用的记录日志的方式。 在这篇文章中,我们将介绍如何使用Java重定向日志接口记录日志。我...
    99+
    2023-11-05
    重定向 日志 接口
  • Python中loguru日志库如何使用
    这篇文章主要介绍“Python中loguru日志库如何使用”,在日常操作中,相信很多人在Python中loguru日志库如何使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python中loguru日志库如...
    99+
    2023-07-05
  • 如何使用日志来调试 Golang 函数?
    使用日志调试 golang 函数:使用 log 包添加日志记录。指定日志记录级别,例如 info()、debug() 或 error()。在代码中打印日志消息以了解函数的状态和行为。使用...
    99+
    2024-04-17
    golang 日志调试 敏感数据
  • 如何使用Python重定向npm日志?
    当你在使用npm时,你可能会遇到一些问题,例如npm输出的日志信息太多,难以阅读。幸运的是,Python提供了一种简单的方法来重定向npm日志,从而使其易于管理。 在本文中,我们将介绍如何使用Python重定向npm日志,以便更轻松地查看...
    99+
    2023-06-18
    重定向 日志 npm
  • 如何使用ASP和numpy重定向日志?
    在编写Web应用程序时,调试和错误处理是不可避免的。ASP是一种流行的Web框架,而numpy是Python中一种常用的科学计算库。本文将介绍如何使用ASP和numpy重定向日志,以方便调试和错误处理。 使用ASP的日志记录功能 AS...
    99+
    2023-11-12
    numpy 重定向 日志
  • 如何使用 Golang 构建 RESTful API 并使用日志记录?
    使用 golang 构建 restful api 时,可以采用以下步骤:创建路由,处理请求。启动 api。使用日志记录:配置日志记录。在 api 处理程序中使用日志记录。 如何使用 G...
    99+
    2024-05-16
    go git golang
  • 如何在PHP和Bash中使用日志记录来简化日志管理?
    好的,以下是文章内容: 日志记录是一项重要的任务,可以帮助开发人员在应用程序中跟踪问题和错误。在PHP和Bash中使用日志记录可以简化日志管理,并使开发人员更容易地找到和解决问题。在本文中,我们将介绍如何在PHP和Bash中使用日志记录。 ...
    99+
    2023-09-01
    bash 日志 http
  • 如何使用 ASP 重定向技术优化大数据日志分析?
    ASP是一种常见的Web开发技术,它可以帮助我们快速地开发各种动态网站。在大数据日志分析领域,ASP的重定向技术可以帮助我们更好地优化数据处理流程,从而提高我们的数据分析效率。 一、ASP的重定向技术 ASP的重定向技术是一种将用户请求重定...
    99+
    2023-08-09
    重定向 大数据 日志
  • MySQL 慢查询日志 使用方法浅析 日志定位与优化技巧
    目录 前言 1、如何开启使用慢查询日志? 1.1 开启慢查询日志 1.2 设置慢查询阈值 1.3 确定慢查询日志的文件名和路径     1.3.1 查询MySQL数据目录     1.3.2 查询慢查询日志文件名      1.3.3 查询...
    99+
    2023-09-17
    mysql 数据库 sql
  • 如何使用Go和NumPy优化Linux日志记录?
    在Linux系统中,日志记录是非常重要的。它可以帮助我们诊断系统问题,了解系统运行情况,以及保护系统安全。然而,随着系统运行时间的增加,日志文件的数量和大小也会不断增加。这可能会导致系统性能下降和存储空间紧张。因此,优化日志记录是非常必要...
    99+
    2023-09-28
    numy 日志 linux
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作