GO语言zap日志库理解和使用方法示例

2025-11-17 18:50

本文主要是介绍GO语言zap日志库理解和使用方法示例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《GO语言zap日志库理解和使用方法示例》Zap是一个高性能、结构化日志库,专为Go语言设计,它由Uber开源,并且在Go社区中非常受欢迎,:本文主要介绍GO语言zap日志库理解和使用方法的相关资...

1. zap日志库介绍

目前我所了解的日志库有go内置的默认日志标准库,这个日志包简单,无需额外的依赖,无需安装,缺乏日志格式化能力,无法切割日志,无法日志分级,适合小项目,对日志功能要求不高的场景;

然后就是logrus第三方日志库,支持日志分级,支持结构化日志,支持钩子机制,但性能一般,适合不对性能要求的中小型项目;

最后就是zap日志库,相比于logrus有极致的性能,同样结构化日志,类型安全,灵活配置,但学习成本高,配置复杂,适合高性能要求的生产环境。

2.安装zap库

go get -u go.uber.org/zap

3.配置日志记录器

Zap提供了两种类型的日志记录器:Suared LoggerLogger

在性能很好但不是很关键·1的上下文中,使用SugaredLogger。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。

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

3.1 Logger

  • 通过调用zap.Newproduction()/zap.NewDevelopment()或者zap.Example()创建一个Logger。
  • 上面的每一个函数都将创建一个logger。唯一的区别在于它将记录的信息不同。例如production logger默认记录调用函数信息、日期和时间等。
  • 通过Logger调用Info/Error等。
  • 默认情况下日志都会打印到应用程序的console界面。
package main

import (
	"go.uber.org/zap"
	"net/http"
)

var logger *zap.Logger //声明全局的zap日志记录器变量

func main() {
	InitLogger()
	defer logger.Sync()                    //程序退出前刷新日志缓冲区,确保缓冲区中的信息刷新到输出
	simpleHttpGet("https://www.baidu.com") //自定义函数,用于发送HTTP请求
}

// 日志初始化函数
func InitLogger() {
	//创建生产环境级别的日志器
	//默认输出 JSON 格式的日志,包含时间戳、日志级别、调用位置等信息
	logger, _ = zap.NewProduction()
}

// HTTP请求函数
func simpleHttpGet(url string) {
	//发送HTTP GET请求
	//返回值是请求响应数据和错误信息
	resp, err := http.Get(url)
	if err != nil {
		//如果请求出错,记录错误日志
		logger.Error("http get error", //错误描述
			zap.String("url", url), //记录错误的URL
			zap.Error(err))         //记录错误的信息
	} else {
		//如果请求成功,记录信息日志
		logger.Info("success",
			zap.String("statusCode", resp.Status), //响应状态码
			zap.String("url", url))                //请求的URL
		err := resp.Body.Close() //关闭响应体
		if err != nil {
			// 如果关闭响应体出错,记录错误日志
			logger.Error("body read error")
		}
	}
}

这段代码是使用Go语言编写的简单HTTP请求客户端程序,主要功能是向指定的URL发送HTTP GET 请求,并使用zap日志库记录请求过程中的关键信息。

3.2 Sugared Logger

大部分实现与Logger相同,唯一的区别是我们通过调用logger的Sugar()方法来获取一个SugaredLogger,然后使用SugaredLogger以printf格式记录语句。下面是用SugaredLogger日志记录器实现上述Logger记录器相同功能的代码:

// 声明一个全局日志器变量
// SugaredLogger支持格式化字符串,使用时更接近打印日志习惯
// 比基础的 zap.Logger 更易用(但性能略低)
var sugarLogger *zap.SugaredLogger

func main() {
	InitLoger()              //初始化日志器
	defer sugarLogger.Sync() //程序退出前刷新日志缓冲区
	simpleHttpGet("https://www.baidu.com")
}
func InitLoger() {
	logger, _ := zap.NewProduction() //创建生产环境日志器(JSON格式,包含元数据)
	sugarLogger = logger.Sugar()     //转换为SugaredLogger,支持格式化日志
}
func simpleHttpGet(url string) {
	//记录调试日志:表示开始尝试发送请求
	sugarLogger.Debugf("Tring to GET %s", url)
	//发送HTTP请求
	resp, err := http.Get(url)
	if err != nil {
		sugarLogger.Errorf("Failed to GET %s: %s", url, err)
	} else {
		sugarLogger.Infof("Successfully GET %s", url)
		resp.Body.Close()
		//sugarLogger.Errorf("Failed to GET %s: %s", url, err)
		//后面不需要执行 resp.Body.Close(),核心原因是:当 http.Get(url)
		//返回错误时,resp 变量可能为 nil(空值),
		//此时调用 resp.Body.Close() 会导致程序 panic(崩溃)
	}
}

在zap日志库中的Logger和SugaredLogger时基础与封装的关系,两者本质上共享一套日志核心功能,

loggerzap.Logger)是 zap 的 基础核心,专注于性能和类型安全;sugarLoggerzap.SugaredLogger)是其 便捷封装,专注于开发效率。

4. 定制logger

4.1 将日志写入文件

我们将使用zap.New(...)方法来手动传递所有配置,而不是使用像zap.NewPriduction()这样的预置方法来创建logger

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

zapcore.Core需要三个配置:Encoder,WriteSyncer,LogLevel。

1. Encoder:编码器(如何写入日志)。我们将使用开箱即用的NewJSONEncoder(),并使用预先设置的ProductionEncoderConfig()

GO语言zap日志库理解和使用方法示例

2. WriteSyncer:指定日志将写到哪里去。我们使用zapcore.AddSync()函数并且将打开的文件句柄传进去。

GO语言zap日志库理解和使用方法示例

3. Log Level:哪种级别的日志将被写入

// 声明一个全局日志器变量
// SugaredLogger支持格式化字符串,使用时更接近打印日志习惯
// 比基础的 zap.Logger 更易用(但性能略低)
var sugarLogger *zap.SugaredLogger

func main() {
	InitLoger()              //初始化日志器
	defer sugarLogger.Sync() //程序退出前刷新日志缓冲区
	simpleHttpGet("https://www.baidu.com")
}
func InitLoger() {
	writeSyncer := getLogWriter() //获取输出目标
	encoder := getEncoder()       //获取日志格式编码器(JSON格式)
	//创建日志核心组件,关联编码器,输出目标和日志级别
	core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
	logger := zap.New(core)      //基于核心组件创建基础日志器
	sugarLogger = logger.Sugar() //转换为SugaredLogger,支持格式化日志
}
func getEncoder() zapcore.Encoder {
	// 使用生产环境的编码器配置,返回 JSON 格式编码器
	return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
}
func getLogWriter() zapcore.WriteSyncer {
	file, _ := os.Create("./zap.log")
	编程return zapcore.AddSync(file) //将文件转换为zap支持的同步写入器
}
func simpleHttpGet(url string) {
	//记录调试日志:表示开始尝试发送请求
	sugarLogger.Debugf("Tring to GET %s", url)
	//发送HTTP请求
	resp, err := http.Get(url)
	if err != nil {
		sugarLogger.Errorf("Failed to GET %s: %s", url, err)
	} else {
		sugarLogger.Infof("Successfully GET %s", url)
		resp.Body.Close()
		//sugarLogger.Errorf("Failed to GET %s: %s", url, err)
		//后面不需要执行 resp.Body.Close(),核心原因是:当 http.Get(url)
		//返回错误时,resp 变量可能为 nil(空值),
		//此时调用 resp.Body.Close() 会导致程序 panic(崩溃)
	}
}

4.2 将JSON Encoder更改为普通的Log Encoder

我们希望将编码器从JSON Encoder更改为普通Encoder。为此,我们需要将NewJSONEncoder()更改为NewConsoleEncoder()

return zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig())

然后打印结果(就不是JSON格式了):

GO语言zap日志库理解和使用方法示例

4.3 更改时间编码并添加调用者详细信息

鉴于我们对配置所做的更改,有下面两个问题:

  • 时间是以非人类可读的方式展示,例如1.572161051846623e+09
  • 调用方函数的详细信息没有显示在日志中

我们要做的第一件事是覆盖默认的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())

4.4 将日志输出到多个位置

var sugarLogger *zap.SugaredLogger

func main() {
	InitLoger()              //初始化日志器
	defer sugarLogger.Sync() //程序退出前刷新日志缓冲区
	simpleHttpGet("https://www.baidu.com")
}
func InitLoger() {
	encoder := getEncoder()       //获取日志格式编码器(JSON格式)
	core := getLogWriter(encoder) //获取输出目China编程标
	//创建日志核心组件,关联编码器,输出目标和日志级别
	//core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
	logger := zap.New(core, zap.AddCaller()) //基于核心组件创建基础日志器
	sugarLogger = logger.Sugar()             //转换为SugaredLogger,支持格式化日志
}
func getEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	return zapcore.NewJSONEncoder(encoderConfig)

}
func getLogWriter(encoder zapcore.Encoder) zapcore.Core {
	file01, _ := os.Create("./zap.log")
	c1 := zapcore.NewCore(encoder, zapcore.AddSync(file01), zapcore.DebugLevel)
	file02, _ := os.Create("./zap_error.log")
	c2 := zapcore.NewCore(encoder, zapcore.AddSync(file02), zapcore.ErrorLevel)
	return zapcore.NewTee(c1, c2)

}
func simpleHttpGet(url string) {
	//记录调试日志:表示开始尝试发送请求
	sugarLogger.Debugf("Tring to GET %s", url)
	//发送HTTP请求
	resp, err := http.Get(url)
	if err != nil {
		sugarLogger.Errorf("Failed to GET %s: %s", url, err)
	} else {
		sugarLogger.Infof("Successfully GET %s", url)
		resp.Body.Close()
		//sugarLogger.Errorf("Failed to GET %s: %s", url, err)
		//后面不需要执行 resp.Body.Close(),核心原因是:当 http.Get(url)
		//返回错误时,resp 变量可能为 nil(空值),
		//此时调用 resp.Body.Close() 会导致程序 panic(崩溃)
	}
}

5. 使用Lumberjack进行日志切割文档

5.1 安装

go get gopkg.in/natefinch/lumberjack.v2

5.2  zap logger种加入Lumberjack

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

var sugarLogger *zap.SugaredLogger

funcandroid main() {
	InitLoger()              //初始化日志器
	defer sugarLogger.Sync() //程序退出前刷新日志缓冲区
	simpleHttpGet("https://www.baidu.com")
}
func InitLoger(编程) {
	writeSyncer := getLogWriter() //获取输出目标
	encoder := getEncoder()       //获取日志格式编码器(JSON格式)
	//创建日志核心组件,关联编码器,输出目标和日志级别
	core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
	logger := zap.New(core)      //基于核心组件创建基础日志器
	sugarLogger = logger.Sugar() //转换为SugaredLogger,支持格式化日志
}
func getEncoder() zapcore.Encoder {
	// 使用生产环境的编码器配置,返回 JSON 格式编码器
	return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
}
func getLogWriter() zapcore.WriteSyncer {
	lumberjackLogger := &lumberjack.Logger{
		Filename:   "./log/sugar.log",
		MaxSize:    10,
		MaxBackups: 10,
		MaxAge:     30,
		Compress:   true,
	}
	return zapcore.AddSync(lumberjackLogger) //将文件转换为zap支持的同步写入器
}
func simpleHttpGet(url string) {
	//记录调试日志:表示开始尝试发送请求
	sugarLogger.Debugf("Tring to GET %s", url)
	//发送HTTP请求
	resp, err := http.Get(url)
	if err != nil {
		sugarLogger.Errorf("Failed to GET %s: %s", url, err)
	} else {
		sugarLogger.Infof("Successfully GET %s", url)
		resp.Body.Close()
		//sugarLogger.Errorf("Failed to GET %s: %s", url, err)
		//后面不需要执行 resp.Body.Close(),核心原因是:当 http.Get(url)
		//返回错误时,resp 变量可能为 nil(空值),
		//此时调用 resp.Body.Close() 会导致程序 panic(崩溃)
	}
}

6. 日志中间件实现

// 声明一个日志记录器实例
var sugarLogger *zap.SugaredLogger

// 中间件函数
func Logger() gin.HandlerFunc {
	InitLogger()
	return func(c *gin.Context) {
		//在请求处理前,记录一条Debug级别的日志,包含请求的URL路径
		sugarLogger.Debugf("Trying to Access logger %s", c.Request.URL.Path)
		c.Next()                        //执行后续中间件和路由处理函数
		statusCode := c.Writer.Status() //获取响应状态码
		if len(c.Errors) > 0 {
			sugarLogger.Error(c.Errors.String())
		}
		if statusCode >= 400 {
			sugarLogger.Warnf("客户端警告: %d", statusCode)
		} else if statusCode >= 500 {
			sugarLogger.Errorf("服务器错误: %d", statusCode)
		} else {
			sugarLogger.Infof("请求正常: %d", statusCode)
		}

	}

}

// 初始化日志器
func InitLogger() {
	encoder := getEncoder()       //获取编码器(定义日志格式)
	writeSyncer := getLogWriter() //获取输出目标(日志写到哪里)
	//创建一个核心日志处理器,相当于日志处理引擎
	core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
	//基于core创建Logger实例
	// zap.AddCaller()是可选配置,作用是让日志种自动包含调用日志的代码位置信息(即文件名和行号)
	logger := zap.New(core, zap.AddCaller())
	//从一个标准的zap.Logger实例创建并获取一个zap.SugaredLogger实例
	sugarLogger = logger.Sugar()
}

// 使用JSON格式的编码器,并采用开发环境的默认配置,包括日志级别和时间戳等字段
func getEncoder() zapcore.Encoder {
	config := zap.NewProductionEncoderConfig()
	//修改时间格式
	config.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
		enc.AppendString(t.Format("2006-01-02 15:04:05"))
	}
	//将日志级别显示为中文
	config.EncodeLevel = func(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
		switch level {
		case zapcore.DebugLevel:
			enc.AppendString("调试")
		case zapcore.InfoLevel:
			enc.AppendString("信息")
		case zapcore.WarnLevel:
			enc.AppendString("警告")
		case zapcore.ErrorLevel:
			enc.AppendString("错误")
		case zapcore.DPanicLevel:
			enc.AppendString("严重错误")
		case zapcore.PanicLevel:
			enc.AppendString("恐慌")
		case zapcore.FatalLevel:
			enc.AppendString("致命")
		default:
			enc.AppendString(level.String())
		}
	}
	// 修改字段名为中文
	config.MessageKey = "消息"
	config.LevelKey = "级别"
	config.TimeKey = "时间"
	config.CallerKey = "位置"
	config.StacktraceKey = "堆栈"

	return zapcore.NewJSONEncoder(config)
}

// 日志输出配置
func getLogWriter() zapcore.WriteSyncer {
	//创建日志文件轮转管理器实例
	//使用lumberjack库实现日志轮转
	lumberjackLogger := &lumberjack.Logger{
		Filename:   "./logs/gin.log", //文件路径,不存在会自动创建
		MaxSize:    200,              //单个文件的最大大小(MB)
		MaxBackups: 10,               //保留的最大备份文件数
		MaxAge:  js   30,               //日志文件的最大保存天数
		Compress:   true,             //是否压缩备份文件
	}
	return zapcore.AddSync(lumberjackLogger)
}

总结 

到此这篇关于GO语言zap日志库理解和使用方法的文章就介绍到这了,更多相关GO语言zap日志库使用内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于GO语言zap日志库理解和使用方法示例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1156197

相关文章

检查 Nginx 是否启动的几种方法

《检查Nginx是否启动的几种方法》本文主要介绍了检查Nginx是否启动的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录1. 使用 systemctl 命令(推荐)2. 使用 service 命令3. 检查进程是否存在4

Java方法重载与重写之同名方法的双面魔法(最新整理)

《Java方法重载与重写之同名方法的双面魔法(最新整理)》文章介绍了Java中的方法重载Overloading和方法重写Overriding的区别联系,方法重载是指在同一个类中,允许存在多个方法名相同... 目录Java方法重载与重写:同名方法的双面魔法方法重载(Overloading):同门师兄弟的不同绝

MySQL字符串转数值的方法全解析

《MySQL字符串转数值的方法全解析》在MySQL开发中,字符串与数值的转换是高频操作,本文从隐式转换原理、显式转换方法、典型场景案例、风险防控四个维度系统梳理,助您精准掌握这一核心技能,需要的朋友可... 目录一、隐式转换:自动但需警惕的&ld编程quo;双刃剑”二、显式转换:三大核心方法详解三、典型场景

MySQL中between and的基本用法、范围查询示例详解

《MySQL中betweenand的基本用法、范围查询示例详解》BETWEENAND操作符在MySQL中用于选择在两个值之间的数据,包括边界值,它支持数值和日期类型,示例展示了如何使用BETWEEN... 目录一、between and语法二、使用示例2.1、betwphpeen and数值查询2.2、be

python中的flask_sqlalchemy的使用及示例详解

《python中的flask_sqlalchemy的使用及示例详解》文章主要介绍了在使用SQLAlchemy创建模型实例时,通过元类动态创建实例的方式,并说明了如何在实例化时执行__init__方法,... 目录@orm.reconstructorSQLAlchemy的回滚关联其他模型数据库基本操作将数据添

Spring配置扩展之JavaConfig的使用小结

《Spring配置扩展之JavaConfig的使用小结》JavaConfig是Spring框架中基于纯Java代码的配置方式,用于替代传统的XML配置,通过注解(如@Bean)定义Spring容器的组... 目录JavaConfig 的概念什么是JavaConfig?为什么使用 JavaConfig?Jav

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配

JAVA项目swing转javafx语法规则以及示例代码

《JAVA项目swing转javafx语法规则以及示例代码》:本文主要介绍JAVA项目swing转javafx语法规则以及示例代码的相关资料,文中详细讲解了主类继承、窗口创建、布局管理、控件替换、... 目录最常用的“一行换一行”速查表(直接全局替换)实际转换示例(JFramejs → JavaFX)迁移建

MySQL快速复制一张表的四种核心方法(包括表结构和数据)

《MySQL快速复制一张表的四种核心方法(包括表结构和数据)》本文详细介绍了四种复制MySQL表(结构+数据)的方法,并对每种方法进行了对比分析,适用于不同场景和数据量的复制需求,特别是针对超大表(1... 目录一、mysql 复制表(结构+数据)的 4 种核心方法(面试结构化回答)方法 1:CREATE

Go异常处理、泛型和文件操作实例代码

《Go异常处理、泛型和文件操作实例代码》Go语言的异常处理机制与传统的面向对象语言(如Java、C#)所使用的try-catch结构有所不同,它采用了自己独特的设计理念和方法,:本文主要介绍Go异... 目录一:异常处理常见的异常处理向上抛中断程序恢复程序二:泛型泛型函数泛型结构体泛型切片泛型 map三:文