日志管理工具Zap笔记

文章目录

    • Uber-go Zap日志库
      • 为什么选择 Zap
      • 配置 Zap Logger
        • 1. Logger
        • 2. SugaredLogger
      • 定制logger
        • 1. 将日志写入文件而不是终端
        • 2. 将JSON Encoder更改为普通的Log Encoder
        • 3. 更改时间编码并添加调用者详细信息
        • 4. AddCallerSkip
        • 5. 将err日志单独输出到文件
    • 使用Lumberjack进行日志切割归档
      • zap logger中加入Lumberjack
    • 总结

Uber-go Zap日志库

为什么选择 Zap

Zap是非常快的、结构化的,分日志级别的Go日志库。

根据Uber-go Zap的文档,它的性能比类似的结构化日志包更好,也比标准库更快。 以下是Zap发布的基准测试信息

记录一条消息和10个字段:

image-20231012103814504

记录一个静态字符串,没有任何上下文或printf风格的模板:

image-20231012103928379

配置 Zap Logger

Zap提供了两种类型的日志记录器:Sugared Logger和Logger。

  • 在性能很好但不是很关键的上下文中,使用SugaredLogger。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。
  • 在每一微秒和每一次内存分配都很重要的上下文中,使用Logger。它比SugaredLogger更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。

什么是printf风格的日志记录?就是实现相同的日志描述,sugarLogger只要写更少的代码就能实现更清晰的描述功能。demo代码如下:

// 使用logger
logger.Info("Failed to fetch URL", zap.String("url", url))
logger.Debug("Retrying:", zap.Int("delay", delay))// 使用sugarLogger
sugarLogger.Infof("Failed to fetch URL: %s", url)
sugarLogger.Debugf("Retrying in %d seconds...", delay)
1. Logger
  • 通过调用 zap.NewProduction()/zap.NewDevelopment()或者zap.Example()创建一个Logger。
    • zap.NewProduction(): 创建一个适合在生产环境中使用的Logger。这个Logger会输出JSON格式的日志,包含时间戳和调用者信息。默认情况下,它会将InfoLevel及以上的日志写入标准输出。这个Logger的配置更注重性能和日志的机器解析性。
    • zap.NewDevelopment(): 创建一个适合在开发环境中使用的Logger。这个Logger的输出格式更适合人类阅读,而不是机器解析。它也包含了时间戳和调用者信息,但是默认情况下,它会将DebugLevel及以上的日志写入标准错误输出。这个Logger的配置更注重人类的可读性和错误的详细信息。
    • zap.Example(): 创建一个简单的Logger主要用于库的示例。这个Logger会将所有级别的JSON格式的日志写入标准输出,不包含时间戳和调用者信息。这个Logger配置主要用于演示库的基础功能,通常不会在生产或开发环境中使用。
  • 通过Logger调用Info/Error等。
  • 默认情况下日志都会打印到应用程序的console界面。
var logger *zap.Loggerfunc main() {InitLogger()defer func(logger *zap.Logger) {//每个使用zap库的应用程序在结束前都应调用Logger.Sync(),来确保所有的日志都被写入目标设备。//因为zap库为了提高性能,可能会缓存一些日志在内存中,而不是立即写入目标设备。_ = logger.Sync()}(logger)simpleHttpGet("https://www.baidu.com")simpleHttpGet("https://www.google.com")
}func InitLogger() {logger, _ = zap.NewProduction()
}func simpleHttpGet(url string) {resp, err := http.Get(url)if err != nil {logger.Error("Error fetching url..",zap.String("url", url),zap.Error(err))} else {logger.Info("Success..",zap.String("statusCode", resp.Status),zap.String("url", url))_ = resp.Body.Close()}
}

当发送请求错误时使用error级别的日志记录;当请求成功时使用info级别的日志记录。运行结果如下:

image-20231012114209538

2. SugaredLogger
var sugarLogger *zap.SugaredLoggerfunc main() {InitLogger()defer func(sugarLogger *zap.SugaredLogger) {_ = sugarLogger.Sync()}(sugarLogger)simpleHttpGet("https://www.baidu.com")simpleHttpGet("https://www.google.com")
}func InitLogger() {logger, _ := zap.NewProduction()sugarLogger = logger.Sugar()
}func simpleHttpGet(url string) {sugarLogger.Debugf("Trying to hit GET request for %s", url)resp, err := http.Get(url)if err != nil {sugarLogger.Errorf("Error fetching URL %s : Error = %s", url, err)} else {sugarLogger.Infof("Success! statusCode = %s for URL %s", resp.Status, url)_ = resp.Body.Close()}
}

运行结果如下:

image-20231012134843262

定制logger

1. 将日志写入文件而不是终端

我们将使用 zap.New() 方法来手动传递所有配置,而不是使用像 zap.NewProduction() 这样的预置方法来创建logger。logger常见完成要做的第一个更改是把日志写入文件,而不是打印到应用程序控制台。zap.New() 方法的原型如下:

func New(core zapcore.Core, options ...Option) *Logger

zapcore.Core 这个参数需要三个配置,分别是Encoder、WriteSyncer和LogLevel。

  • Encoder:编码器(如何写入日志)。这里使用开箱即用的 NewJSONEncoder(),zapcore.NewJSONEncoder()返回一个将日志消息编码为JSON格式的编码器。然后并使用预先设置的 NewProductionEncoderConfig(),这个NewProductionEncoderConfig()有如下配置可供选择:

    • MessageKey: 默认为"msg",用于指定输出的消息的键名。
    • LevelKey: 默认为"level",用于指定输出的日志级别的键名。
    • TimeKey: 默认为"ts",用于指定输出的时间戳的键名。
    • NameKey: 默认为"logger",用于指定输出的日志记录器名称的键名。
    • CallerKey: 默认为"caller",用于指定输出的调用者信息的键名。
    • StacktraceKey: 默认为"stacktrace",用于指定输出的堆栈信息的键名。
    • LineEnding: 默认为"\n",用于指定行结束符。
    • EncodeLevel: 默认为zapcore.LowercaseLevelEncoder,用于指定日志级别的编码方式。
    • EncodeTime: 默认为zapcore.EpochTimeEncoder,用于指定时间戳的编码方式。
    • EncodeDuration: 默认为zapcore.SecondsDurationEncoder,用于指定时间持续期的编码方式。
    • EncodeCaller: 默认为zapcore.ShortCallerEncoder,用于指定调用者信息的编码方式。
    // 这里我们都使用默认的即可
    zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
    
  • WriterSyncer:指定日志将写到哪里去。我们使用 zapcore.AddSync() 函数并且将打开的文件句柄传进去。

    file, _ := os.Create("./log/demo.log")
    writeSyncer := zapcore.AddSync(file)
    
  • Log Level:哪种级别的日志将被写入。有以下几种日志级别:

    1. DebugLevel: 通常只在开发环境中使用,用于输出详细的调试信息。
    2. InfoLevel: 适用于生产环境,用于记录关键的系统信息。
    3. WarnLevel: 对可能存在问题的情况进行警告,但不会影响系统运行。
    4. ErrorLevel: 在系统无法正常运行时进行记录,比如无法进行数据库连接、缺失必要的配置文件等。
    5. DPanicLevel: 用于开发环境,当代码运行到绝不应该运行的部分时,记录Panic日志。在生产环境,不会引起Panic,只会记录错误。
    6. PanicLevel: 与DPanicLevel相似,用于非开发环境,当代码运行到绝不应该运行的部分时,会引发Panic。
    7. FatalLevel: 当系统无法运行时,记录致命错误,然后调用os.Exit。

    修改上述部分中的Logger代码:重写InitLogger()方法。

    func InitLogger() {encoder := getEncoder()writeSyncer := getLogWriter()core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)logger := zap.New(core)sugarLogger = logger.Sugar()
    }func getEncoder() zapcore.Encoder {return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
    }func getLogWriter() zapcore.WriteSyncer {file, _ := os.Create("./log/demo.log")return zapcore.AddSync(file)
    }
    

当使用这些修改过的logger配置调用上述部分的main()函数时,以下输出将打印在文件./log/demo.log中。

{"level":"debug","ts":1697091706.9003525,"msg":"Trying to hit GET request for https://www.baidu.com"}
{"level":"info","ts":1697091707.2923145,"msg":"Success! statusCode = 200 OK for URL https://www.baidu.com"}
{"level":"debug","ts":1697091707.2925751,"msg":"Trying to hit GET request for https://www.google.com"}
{"level":"error","ts":1697091728.6855457,"msg":"Error fetching URL https://www.google.com : Error = Get \"https://www.google.com\": dial tcp [2a03:2880:f126:83:face:b00c:0:25de]:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond."}
2. 将JSON Encoder更改为普通的Log Encoder

现在,我们希望将编码器从JSON Encoder更改为普通Encoder。其实只需要将NewJSONEncoder()更改为NewConsoleEncoder()即可。

func getEncoder() zapcore.Encoder {return zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig())
}

最后的日志结果如下:

1.6970938827359314e+09	debug	Trying to hit GET request for https://www.baidu.com
1.697093883124089e+09	info	Success! statusCode = 200 OK for URL https://www.baidu.com
1.6970938831244597e+09	debug	Trying to hit GET request for https://www.google.com
1.6970939045269628e+09	error	Error fetching URL https://www.google.com : Error = Get "https://www.google.com": dial tcp 162.125.32.15:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
3. 更改时间编码并添加调用者详细信息

第一步:覆盖默认的ProductionConfig()

func getEncoder() zapcore.Encoder {// 得到编码配置encoderConfig := zap.NewProductionEncoderConfig()// 通过配置修改时间编码规则encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder// 通过配置添加调用者信息encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoderreturn zapcore.NewConsoleEncoder(encoderConfig)
}

第二步:修改zap logger代码,添加将调用函数信息记录到日志中的功能。也就是在zap.New()函数中添加一个Option。

logger := zap.New(core, zap.AddCaller())

最后的运行结果如下:

2023-10-12T15:11:29.098+0800	DEBUG	zap/main.go:47	Trying to hit GET request for https://www.baidu.com
2023-10-12T15:11:29.624+0800	INFO	zap/main.go:52	Success! statusCode = 200 OK for URL https://www.baidu.com
2023-10-12T15:11:29.624+0800	DEBUG	zap/main.go:47	Trying to hit GET request for https://www.google.com
2023-10-12T15:11:51.053+0800	ERROR	zap/main.go:50	Error fetching URL https://www.google.com : Error = Get "https://www.google.com": dial tcp [2a03:2880:f10d:83:face:b00c:0:25de]:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
4. AddCallerSkip

当我们不是直接使用初始化好的logger实例记录日志,而是将其包装成一个函数等,此时日录日志的函数调用链会增加,想要获得准确的调用信息就需要通过AddCallerSkip函数来跳过。

logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
5. 将err日志单独输出到文件

有时候我们除了将全量日志输出到xxx.log文件中之外,还希望将ERROR级别的日志单独输出到一个名为xxx.err.log的日志文件中。我们可以通过以下方式实现。

func InitLogger() {encoder := getEncoder()// demo.log记录全量日志logF, _ := os.Create("./log/demo.log")c1 := zapcore.NewCore(encoder, zapcore.AddSync(logF), zapcore.DebugLevel)// demo.err.log记录ERROR级别的日志errF, _ := os.Create("./log/demo.err.log")c2 := zapcore.NewCore(encoder, zapcore.AddSync(errF), zap.ErrorLevel)// 使用NewTee将c1和c2合并到corecore := zapcore.NewTee(c1, c2)logger := zap.New(core, zap.AddCaller())sugarLogger = logger.Sugar()
}

demo.err.log的内容如下:

2023-10-12T15:21:09.108+0800	ERROR	zap/main.go:55	Error fetching URL https://www.google.com : Error = Get "https://www.google.com": dial tcp [2a03:2880:f112:83:face:b00c:0:25de]:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.

demo.log的内容如下:

2023-10-12T15:20:47.300+0800	DEBUG	zap/main.go:52	Trying to hit GET request for https://www.baidu.com
2023-10-12T15:20:47.675+0800	INFO	zap/main.go:57	Success! statusCode = 200 OK for URL https://www.baidu.com
2023-10-12T15:20:47.675+0800	DEBUG	zap/main.go:52	Trying to hit GET request for https://www.google.com
2023-10-12T15:21:09.108+0800	ERROR	zap/main.go:55	Error fetching URL https://www.google.com : Error = Get "https://www.google.com": dial tcp [2a03:2880:f112:83:face:b00c:0:25de]:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.

使用Lumberjack进行日志切割归档

Zap日志工具唯一缺少的就是日志切割归档功能。这里使用第三方库Lumberjack来实现,但是这个库目前只支持按文件大小切割,原因是按时间切割效率低且不能保证日志数据不被破坏。

zap logger中加入Lumberjack

要在zap中加入Lumberjack支持,我们需要修改WriteSyncer代码。我们将按照下面的代码修改getLogWriter()函数:

// MaxSize:定义了日志文件的最大大小,单位是MB。
// MaxBackups:定义了最多保留的备份文件数量。当备份文件数量超过MaxBackups后,lumberjack会自动删除最旧的备份文件。
// MaxAge:定义了备份文件的最大保存天数。当备份文件的保存天数超过MaxAge后,lumberjack会自动删除备份文件。
// Compress:定义了备份文件是否需要压缩。如果设置为true,备份的日志文件会被压缩为.gz格式。
func getLogWriter() zapcore.WriteSyncer {lumberJackLogger := &lumberjack.Logger{Filename:   "./log/demo.log",MaxSize:    1,MaxBackups: 5,MaxAge:     30,Compress:   false,}return zapcore.AddSync(lumberJackLogger)
}

最后只要将getLogWriter返回就行。

writeSyncer := getLogWriter()

总结

以上就是Zap库的基本使用。下面做一个总结:

  • Zap提供了两种类型的日志记录器:Sugared Logger和Logger。
  • 对于性能要求不是非常高的应用可以使用Sugared Logger。相比于Logger,Sugared Logger提供了printf风格的日志记录。
  • 对于性能要求非常高的应用需要使用Logger来保证性能。
  • 不管是Logger还是Sugared Logger,它们都有三个预制化的日志:zap.NewProduction()、zap.NewDevelopment()和zap.Example()。
  • 如果不想使用预制化的日志记录,可以自己定制日志记录。
  • 定制日志需要使用zap.New方法,该方法有一个core参数和多个options。
  • core参数需要通过zapcore.NewCore()方法生成,该方法需要三个参数:encoder、writeSyncer和level。
  • 通过配置encoder参数可以指定日志级别的编码方式(是JSON格式还是控制台格式),还可以指定更改时间编码方式等。
  • 通过配置writeSyncer参数可以指定日志的输出位置。并且通过引入Lumberjack库还可以实现日志切割。
  • 通过配置level参数可以控制日志级别,有DebugLevel、InfoLevel、ErrorLevel等级别,可以根据需要进行选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/102338.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【安全】 Java 过滤器 解决存储型xss攻击问题

文章目录 XSS简介什么是XSS?分类反射型存储型 XSS(cross site script)跨站脚本攻击攻击场景解决方案 XSS简介 跨站脚本( cross site script )为了避免与样式css(Cascading Style Sheets层叠样式表)混淆,所以简称为XSS。 XSS是一种经常出现在web应用中的计算机安全…

MacOS安装conda

下载conda 地址https://repo.anaconda.com/miniconda/ 选择合适的安装文件下载 运行安装 执行命令安装 bash Miniconda3-latest-MacOSX-arm64.sh 设置环境变量 echo export PATH"/Users/your_user_name/miniconda3/bin:$PATH" >> ~/.zshrc source ~/.zsh…

nextjs构建服务端渲染,同时使用Material UI进行项目配置

一、创建一个next项目 使用create-next-app来启动一个新的Next.js应用,它会自动为你设置好一切 运行命令: npx create-next-applatest 执行结果如下: 启动项目: pnpm dev 执行结果: 启动成功! 二、安装Mater…

C语言中常用的字符串处理函数(strlen、strcpy、strcat、strcmp)

文章目录 写在前面1. strlen1.1 函数介绍1.2 模拟实现 2. strcpy2.1 函数介绍2.2 模拟实现 3. strcat3.1 函数介绍3.2 模拟实现 4. strcmp4.1 函数介绍4.2 模拟实现 写在前面 本篇文章介绍了C语言中常用的字符串处理函数,包括strlen、strcpy、strcat和strcmp。文章…

【vue3+ts】项目初始化

1、winr呼出cmd,输入构建命令 //用vite构建 npm init vitelatest//用cli脚手架构建 npm init vurlatest2、设置vscode插件 搜索volar,安装前面两个 如果安装了vue2的插件vetur,要禁用掉,否则插件会冲突

使用react-router-dom在新标签页打开链接,而不是本页跳转

一般单页面应用&#xff0c;当你使用useNavigate时候的时候&#xff0c;用useNavigate来跳转&#xff0c;只能是在当前页面刷新跳转的&#xff0c;要想单独在一个tab页打开新页面&#xff0c;大概用三种方式。 第一种 使用link标签&#xff0c;配合target实现 <Link to&q…

2023年中国石油催化裂化剂行业供需、竞争格局及市场规模分析[图]

催化裂化是石油炼制过程之一&#xff0c;是在热和催化剂的作用下使重质油发生裂化反应&#xff0c;转变为裂化气、汽油和柴油等的过程。中国原油加工量在这一阶段逐年提升&#xff0c;2022年国内原油加工量67589.7万吨。 2016-2022年中国原油加工量情况 资料来源&#xff1a;国…

linux,write:xxx has messages disabled 与 Ubuntu多用户同时登录的问题 ubuntu 20.04

write&#xff1a;xxx has messages disabled 问题 被这问题折磨了好久&#xff0c;搜都搜不到&#xff0c;还是灵机一动想到的。 很多 帖子说&#xff0c;要使用 mesg y用了还是没有用&#xff0c;后面我登录了很多用户&#xff0c;发现只有root用户可以给别的用户使用write…

el-data-picker限制日期可选范围

<el-date-pickerclass"date"v-model"date"type"date"change"dateChange"value-format"yyyy-MM-dd"format"yyyy-MM-dd"placeholder"选择日期":picker-options"datePickerOptions"></…

【重拾C语言】八、表单数据组织——结构体(类型、类型别名、直接/间接访问;典例:复数、成绩单)

目录 前言 八、结构体 8.1 结构体类型 8.2 结构体类型名 8.2.1 typedef关键字 8.2.1 结构体类型别名 8.3 结构体变量 8.3.1 使用结构体类型引用 8.3.2 使用结构体类型定义 8.3.3 使用typedef定义的结构体类型别名 8.4 访问结构体变量 8.4.1 直接成员选择表达式 8.…

JOSEF约瑟 矿用一般型选择性漏电继电器 LXY2-660 Φ45 JKY1-660

系列型号&#xff1a; JY82A检漏继电器 JY82B检漏继电器 JY82-380/660检漏继电器 JY82-IV检漏继电器 JY82-2P检漏继电器 JY82-2/3检漏继电器 JJKY检漏继电器 JD型检漏继电器 JY82-IV;JY82J JY82-II;JY82-III JY82-1P;JY82-2PA;JY82-2PB JJB-380;JJB-380/660 JD-12…

uni-app 实现考勤打卡功能

一、在页面中引入地图组件 <map id"map" style"width: 100%; height: 100%" :latitude"myLatitude" :longitude"myLongitude" :circles"circles" :markers"markers"> </map>属性名类型说明longitudeN…

VRRP 虚拟路由器冗余协议的解析和配置

VRRP的解析 个人简介 原理和HSRP的差不多&#xff0c;少了一些状态就只有了三种状态 还有不同的就是VRRP严格按照抢占要求 一个VRRP组中具有最高优先级的设备成为Master路由器缺省优先级为100若优先级相同&#xff0c;具有最高接口IP地址最大的路由器成为Master路由器抢占(Pr…

Centos指令合集

2023-10-09 防火墙 开启 systemctl start firewalld自启动 systemctl enable firewalld.service关闭 systemctl stop firewalld禁用 systemctl disable firewalld.service查看状态 systemctl status firewalld

360测试开发技术面试题目

最近面试了360测试开发的职位&#xff0c;将面试题整理出来分享~ 一、java方面 1、java重载和重写的区别 重载overloading 多个方法、相同的名字&#xff0c;不同的参数 重写overwrite 子类继承父类&#xff0c;对方法进行重写 2、java封装的特性 可以改变内部实现&#xff0c;…

UnrealEngine iOS 打包 —— 签名证书(cer、p12)生成

官方文档 docs.unrealengine.com/5.3/zh-CN/setting-up-ios-tvos-and-ipados-provisioning-profiles-and-signing-certificates-for-unreal-engine-projects 打开 ProjectSettings -> Platforms -> iOS 可以看到签名证书配置 需要拓展名为 .cer 和 .p12 的一对证书和密钥…

精益生产与MES生产管理系统相互融合

近年来&#xff0c;精益生产理念在企业管理中越来越受欢迎。它强调以最小的浪费&#xff0c;在最短的时间内&#xff0c;生产出高质量的产品。这一理念的实施手段包括准时制生产方式、适时生产方式等&#xff0c;消除浪费、看板、快换工装等都是精益提高的工具方针。 然而&…

Spark任务优化分析

一、背景 首先需要掌握 Spark DAG、stage、task的相关概念 Spark的job、stage和task的机制论述 - 知乎 task数量和rdd 分区数相关 二、任务慢的原因分析 找到运行时间比较长的stage 再进去看里面的task 可以看到某个task 读取的数据量明显比其他task 较大。 如果是sql 任…

自动售货机销售数据分析与应用

⭐简单说两句⭐ 作者&#xff1a;后端小知识 CSDN个人主页&#xff1a;后端小知识 &#x1f50e;GZH&#xff1a;后端小知识 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; 摘要&#xff1a; 本案例将主要结合自动售货机的实际情况&#xff0…

虹科方案丨自动驾驶多传感器数据融合方法

文章来源&#xff1a;雅名特自动驾驶 点此阅读原文&#xff1a;https://mp.weixin.qq.com/s/QsPMWZDGZaPdEx47L2VmeA 近年来&#xff0c;深度学习技术在涉及高维非结构化数据领域展现出了最先进的性能&#xff0c;如计算机视觉、语音、自然语言处理等方面&#xff0c;并且开始涉…