日志管理工具Zap笔记

news2024/10/5 5:25:37

文章目录

    • 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.Logger

func 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.SugaredLogger

func 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.CapitalLevelEncoder
	return 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合并到core
	core := 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.coloradmin.cn/o/1085489.html

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

相关文章

ARM按键中断实验

设置按键中断,按键1按下,LED亮,再按一次,灭 按键2按下,蜂鸣器响。再按一次,不响 按键3按下,风扇转,再按一次,风扇停 src/do_irq.c #include "key_it.h" ex…

基于Java+SpringBoot+Vue精品水果线上销售网站的设计与实现 前后端分离【Java毕业设计·文档报告·代码讲解·安装调试】

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…

对验证码的识别爆破

声明:该系列文章首发于公众号:Y1X1n安全,转载请注明出处!本公众号所分享内容仅用于每一个爱好者之间的技术讨论及教育目的,所有渗透及工具的使用都需获取授权,禁止用于违法途径,否则需自行承担&…

【Python爬虫原理与基本请求库urllib详解】

Python爬虫原理与基本请求库urllib详解 这篇文章主要介绍了Python爬虫原理与基本请求库urllib详解,爬虫就是通过模拟浏览器,按照一定的规则,自动、大批量的获取网络资源,包括文本、图片、链接、音频、视频等等,需要的朋友可以参考下 文章目录…

java并发之AQS详解(待更)

一、为什么要用AQS同步框架? 开发者如果不了解JMM和多线程编程,就会写出很多线程不安全的程序,即使是经验丰富的程序员,并发编程也难免会出错。 而对于java程序员来说,并发编程就变得容易得多了,因为并发编…

《YOLOv7高阶自研》专栏介绍 CSDN独家改进创新实战 专栏目录

YOLOv7高阶自研专栏介绍: http://t.csdnimg.cn/tYI0c ✨✨✨前沿最新计算机顶会复现 🚀🚀🚀YOLOv7自研创新结合,轻松搞定科研 🍉🍉🍉持续更新中,定期更新不同数据集涨点情况 目录 全网首发&独家改进

C++DAY44

#include <iostream>using namespace std;class Animal//封装 动物 基类 { private:string name; public:Animal() {}Animal(string n):name(n){}virtual void perform() //虚函数{cout << "欢迎来到动物园" << endl;} };class Lion:public Animal…

LeetCode 1517. 查找拥有有效邮箱的用户

题目链接&#xff1a;1517. 查找拥有有效邮箱的用户 题目描述 表: Users Column NameTypeuser_idintnamevarcharmailvarchar user_id 是该表的主键&#xff08;具有唯一值的列&#xff09;。 该表包含了网站已注册用户的信息。有一些电子邮件是无效的。 编写一个解决方案&am…

LabVIEW将视觉生成器AI用作OPC服务器

LabVIEW将视觉生成器AI用作OPC服务器 介绍如何将视觉生成器AI配置为OPC服务器&#xff0c;并使用共享变量共享视觉生成器AI生成的结果。OPC是一系列标准规范&#xff0c;定义了来自不同制造商的控制设备之间的实时数据通信。OPC数据访问通信是基于客户端服务器的通信。 共享系…

适合自学的网络安全基础技能“蓝宝书”:《CTF那些事儿》

CTF比赛是快速提升网络安全实战技能的重要途径&#xff0c;已成为各个行业选拔网络安全人才的通用方法。但是&#xff0c;本书作者在从事CTF培训的过程中&#xff0c;发现存在几个突出的问题&#xff1a; 1&#xff09;线下CTF比赛培训中存在严重的 “最后一公里”问题 &#…

nodejs+vue+elementui养老院老年人服务系统er809

“养老智慧服务平台”是运用nodejs语言和vue框架&#xff0c;以MySQL数据库为基础而发出来的。为保证我国经济的持续性发展&#xff0c;必须要让互联网信息时代在我国日益壮大&#xff0c;蓬勃发展。伴随着信息社会的飞速发展&#xff0c;养老智慧服务平台所面临的问题也一个接…

Text-to-SQL小白入门(八)RLAIF论文:AI代替人类反馈的强化学习

学习RLAIF论文前&#xff0c;可以先学习一下基于人类反馈的强化学习RLHF&#xff0c;相关的微调方法&#xff08;比如强化学习系列RLHF、RRHF、RLTF、RRTF&#xff09;的论文、数据集、代码等汇总都可以参考GitHub项目&#xff1a;GitHub - eosphoros-ai/Awesome-Text2SQL: Cur…

【PX4】解决Resource not found: px4问题【踩坑实录】

【PX4】解决Resource not found: px4问题【踩坑实录】 文章目录 【PX4】解决Resource not found: px4问题【踩坑实录】1. 问题描述2. 错误排查 1. 问题描述 笔者在配置好px4的所有环境后&#xff0c;使用自己写的launch文件时&#xff0c;出现了报错 sjhsjhR9000X:~$ roslaunc…

【面试算法——动态规划 21】正则表达式匹配(hard) 交错字符串

10. 正则表达式匹配 链接: 10. 正则表达式匹配 给你一个字符串 s 和一个字符规律 p&#xff0c;请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。 ‘.’ 匹配任意单个字符 ‘*’ 匹配零个或多个前面的那一个元素 所谓匹配&#xff0c;是要涵盖 整个 字符串 s的&#xf…

2023.10.10 关于 线程安全 问题

目录 线程安全问题实例一 引发线程安全的原因 抢占式执行 多线程修改同一变量 操作的原子性 指令重排序 内存可见性问题 线程安全问题实例二 如何解决上述线程安全问题 volatile 关键字 Java 内存模型 JMM&#xff08;Java Memory Model&#xff09; 线程安全问题实…

解决echarts配置滚动(dataZoom)后导出图片数据不全问题

先展现一个echarts&#xff0c;并配置dataZoom&#xff0c;每页最多10条数据&#xff0c;超出滚动 <div class"echartsBox" id"echartsBox"></div>onMounted(() > {nextTick(() > {var chartDom document.getElementById(echartsBox);…

前端URL拼接路径参数

前端URL拼接路径参数 一、应用场景二、具体实现1.字符串拼接2.URL对象实现 四、完整代码 一、应用场景 我们有时候会遇到浏览器URL拼接参数的场景&#xff0c;例如页面跳转时&#xff0c;带上一个特定的标识&#xff1a;https://www.baidu.com?fromcsdn 二、具体实现 1.字符…

微软发布2023年10月补丁,修复了103个缺陷,包括2个活跃的漏洞利用

导语 最近&#xff0c;微软发布了2023年10月的补丁更新&#xff0c;共修复了103个缺陷。这些补丁包括两个正在被黑客利用的漏洞。让我们来看看这些补丁的具体内容和影响。 修复103个缺陷 微软此次的补丁更新共修复了103个缺陷&#xff0c;其中13个被评为严重&#xff0c;90个被…

gradle对应jdk版本

官网地址-兼容性矩阵&#xff1a;Compatibility Matrix Gradle运行在所有主要的操作系统上。它需要Java开发工具包&#xff08;JDK&#xff09;版本8或更高版本才能运行。有关详细信息&#xff0c;您可以查看兼容性矩阵。

DailyPractice.2023.10.12

文章目录 1.[1. 两数之和]2.[49. 字母异位词分组]3.[128. 最长连续序列]4.[283. 移动零]5.[11. 盛最多水的容器]6.[15. 三数之和]7.[3. 无重复字符的最长子串]8.[206. 反转链表]9.[141. 环形链表]10.[160. 相交链表] 1.[1. 两数之和] 1. 两数之和 class Solution { public:ve…