golang整合日志zap的实现示例

 更新时间:2024年10月22日 10:17:41   作者:code:404-not-found  
Go语言中的zap库提供了强大的日志管理功能,支持日志记录到文件、日志切割、多日志级别、结构化日志输出等,它通过三种方法zap.NewProduction()、zap.NewDevelopment()和zap.NewExample(),快速构建适用于不同环境的logger,感兴趣的可以了解一下

在许多Go语言项目中,我们需要一个好的日志记录器能够提供下面这些功能:

  • 能够将事件记录到文件中,而不是应用程序控制台;
  • 日志切割-能够根据文件大小、时间或间隔等来切割日志文件;
  • 支持不同的日志级别。例如INFO,DEBUG,ERROR等;
  • 能够打印基本信息,如调用文件/函数名和行号,日志时间等;
  • 日志分级:有 Debug,Info,Warn,Error,DPanic,Panic,Fatal 等
  • 日志记录结构化:日志内容记录是结构化的,比如 json 格式输出
  • 自定义格式:用户可以自定义输出的日志格式
  • 自定义公共字段:用户可以自定义公共字段,大家输出的日志内容就共同拥有了这些字段
  • 调试:可以打印文件名、函数名、行号、日志时间等,便于调试程序
  • 自定义调用栈级别:可以根据日志级别输出它的调用栈信息
  • Namespace:日志命名空间。定义命名空间后,所有日志内容就在这个命名空间下。命名空间相当于一个文件夹
  • 支持 hook 操作

安装

go get -u go.uber.org/zap

基本使用

package main

import "go.uber.org/zap"

var logger *zap.Logger

func main() {
	logger, _ = zap.NewProduction() // 使用生产环境
    // logger, _ := zap.NewDevelopment() // 使用开发环境
	defer logger.Sync() // 刷新缓存
    
    
    suger := logger.Sugar() // 开启日志语法糖
    
    suger.Debug("我是Debug级别的日志")
	suger.Debugw("我是Debug级别的日志", "key1", "value1", "key2", "value2")
	suger.Debugf("我是Debug级别的日志%s", "value")

	suger.Warn("我是Warn级别的日志")
	suger.Warnw("我是Warn级别的日志", "key1", "value1", "key2", "value2")
	suger.Warnf("我是Warn级别的日志%s", "value")

	suger.Info("我是Info级别的日志")
	suger.Infow("我是Info级别的日志", "key1", "value1", "key2", "value2")
	suger.Infof("我是Info级别的日志 %s", "info")

	suger.Error("我是Error级别的日志")
	suger.Errorw("我是Error级别的日志", "key1", "value1", "key2", "value2")
	suger.Errorf("我是Error级别的日志 %s", "value")

	suger.Panic("我是Panic级别的日志")
	suger.Panicw("我是Panic级别的日志", "key1", "value1", "key2", "value2")
	suger.Panicf("我是Panic级别的日志 %s", "value")
    
    // 使用不带语法糖的日志
    logger.Debug("我是Debug级别的日志", zap.String("key", "value"), zap.Int("num", 10))
	logger.Warn("我是Warn级别的日志", zap.String("key", "value"), zap.Int("num", 10))
	logger.Info("我是Info级别的日志", zap.String("key", "value"), zap.Int("num", 10))
	logger.Error("我是Error级别的日志", zap.String("key", "value"), zap.Int("num", 10))
	logger.Panic("我是Panic级别的日志", zap.String("key", "value"), zap.Int("num", 10))
}

NewExample/NewDevelopment/NewProduction使用

zap 为我们提供了三种快速创建 logger 的方法: zap.NewProduction()zap.NewDevelopment()zap.NewExample()

Example 一般用在测试代码中,Development 用在开发环境中,Production 用在生成环境中

NewExample()使用

NewExample 构建一个 logger,专门为在 zap 的测试示例使用。它将 DebugLevel 及以上日志用 JSON 格式标准输出,但它省略了时间戳和调用函数,以保持示例输出的简短和确定性

因为在这个方法里,zap 已经定义好了日志配置项部分默认值

// https://github.com/uber-go/zap/blob/v1.24.0/logger.go#L127
func NewExample(options ...Option) *Logger {
	encoderCfg := zapcore.EncoderConfig{
        MessageKey:     "msg",  // 日志内容key:val, 前面的key设为msg
		LevelKey:       "level", // 日志级别的key设为level
		NameKey:        "logger", // 日志名
		EncodeLevel:    zapcore.LowercaseLevelEncoder, //日志级别,默认小写
		EncodeTime:     zapcore.ISO8601TimeEncoder, // 日志时间
		EncodeDuration: zapcore.StringDurationEncoder,
	}
	core := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), os.Stdout, DebugLevel)
	return New(core).WithOptions(options...)
}

使用例子

package main
import (
	"go.uber.org/zap"
)
func main() {
	logger := zap.NewExample()
	logger.Debug("this is debug message")
}

NewDevelopment()使用

NewDevelopment() 构建一个开发使用的 Logger

使用例子

package main

import (
	"time"

	"go.uber.org/zap"
)
func main() {
	logger, _ := zap.NewDevelopment()
	defer logger.Sync()

	logger.Info("failed to fetch url",
		// 强类型字段
		zap.String("url", "http://example.com"),
		zap.Int("attempt", 3),
		zap.Duration("duration", time.Second),
	)

	logger.With(
		// 强类型字段
		zap.String("url", "http://development.com"),
		zap.Int("attempt", 4),
		zap.Duration("duration", time.Second*5),
	).Info("[With] failed to fetch url")
}

NewProduction()使用

NewProduction() 构建了一个合理的 Prouction 日志记录器,它将 info 及以上的日志内容以 JSON 格式记写入标准错误里

使用例子

package main

import (
	"time"

	"go.uber.org/zap"
)

func main() {
	logger, _ := zap.NewProduction()
	defer logger.Sync()

	url := "http://zap.uber.io"
	sugar := logger.Sugar()
	sugar.Infow("failed to fetch URL",
		"url", url,
		"attempt", 3,
		"time", time.Second,
	)

	sugar.Infof("Failed to fetch URL: %s", url)

	// 或更简洁 Sugar() 使用
	// sugar := zap.NewProduction().Sugar()
	// defer sugar.Sync()
}

传入配置项

在这 3 个函数中,可以传入一些配置项。

func NewExample(options …Option) *Logger

package main

import (
	"os"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

func main() {

	encoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())

	file, _ := os.OpenFile("./log.txt", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
	syncFile := zapcore.AddSync(file)
	syncConsole := zapcore.AddSync(os.Stderr)
	sync := zapcore.NewMultiWriteSyncer(syncConsole, syncFile)

	core := zapcore.NewCore(encoder, sync, zapcore.InfoLevel)
	logger := zap.New(core)
	logger.Info("info 日志", zap.Int("line", 1))
}

zap.Hook() 添加回调函数

Hook (钩子函数)回调函数为用户提供一种简单方法,在每次日志内容记录后运行这个回调函数,执行用户需要的操作。也就是说记录完日志后你还想做其它事情就可以调用这个函数

package main

import (
	"fmt"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

func main() {
	logger := zap.NewExample(zap.Hooks(func(entry zapcore.Entry) error {
		fmt.Println("[zap.Hooks]test Hooks")
		return nil
	}))
	defer logger.Sync()

	logger.Info("test output")

	logger.Warn("warn info")
}

自定义配置项

快速构建 logger 日志记录器最简单的方法就是用 zap 预定义了配置的方法:NewExample(), NewProduction() 和NewDevelopment(),这 3 个方法通过单个函数调用就可以构建一个日志计记录器,也可以简单配置

Config 配置项源码

// zap v1.24.0
type Config struct {
    // 动态改变日志级别,在运行时你可以安全改变日志级别
	Level AtomicLevel `json:"level" yaml:"level"`
    // 将日志记录器设置为开发模式,在 WarnLevel 及以上级别日志会包含堆栈跟踪信息
	Development bool `json:"development" yaml:"development"`
    // 在日志中停止调用函数所在文件名、行数
	DisableCaller bool `json:"disableCaller" yaml:"disableCaller"`
    // 完全禁止自动堆栈跟踪。默认情况下,在 development 中,warnlevel及以上日志级别会自动捕获堆栈跟踪信息
    // 在 production 中,ErrorLevel 及以上也会自动捕获堆栈信息
	DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"`
    // 设置采样策略。没有 SamplingConfing 将禁止采样
	Sampling *SamplingConfig `json:"sampling" yaml:"sampling"`
    // 设置日志编码。可以设置为 console 和 json。也可以通过 RegisterEncoder 设置第三方编码格式
	Encoding string `json:"encoding" yaml:"encoding"`
    // 为encoder编码器设置选项。详细设置信息在 zapcore.zapcore.EncoderConfig
	EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"`
    // 日志输出地址可以是一个 URLs 地址或文件路径,可以设置多个
	OutputPaths []string `json:"outputPaths" yaml:"outputPaths"`
    // 错误日志输出地址。默认输出标准错误信息
	ErrorOutputPaths []string `json:"errorOutputPaths" yaml:"errorOutputPaths"`
    // 可以添加自定义的字段信息到 root logger 中。也就是每条日志都会携带这些字段信息,公共字段
	InitialFields map[string]interface{} `json:"initialFields" yaml:"initialFields"`
}

EncoderConfig 结构源码,它里面也有很多配置选项

// zapcore@v1.24.0
type EncoderConfig struct {
    // 为log entry设置key。如果 key 为空,那么在日志中的这部分信息也会省略
	MessageKey     string `json:"messageKey" yaml:"messageKey"`//日志信息的健名,默认为msg
	LevelKey       string `json:"levelKey" yaml:"levelKey"`//日志级别的健名,默认为level
	TimeKey        string `json:"timeKey" yaml:"timeKey"`//记录日志时间的健名,默认为time
	NameKey        string `json:"nameKey" yaml:"nameKey"`
	CallerKey      string `json:"callerKey" yaml:"callerKey"`
	FunctionKey    string `json:"functionKey" yaml:"functionKey"`
	StacktraceKey  string `json:"stacktraceKey" yaml:"stacktraceKey"`
	SkipLineEnding bool   `json:"skipLineEnding" yaml:"skipLineEnding"`
	LineEnding     string `json:"lineEnding" yaml:"lineEnding"`
    // 日志编码的一些设置项
	EncodeLevel    LevelEncoder    `json:"levelEncoder" yaml:"levelEncoder"`
	EncodeTime     TimeEncoder     `json:"timeEncoder" yaml:"timeEncoder"`
	EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"`
	EncodeCaller   CallerEncoder   `json:"callerEncoder" yaml:"callerEncoder"`
    // 与其它编码器不同, 这个编码器可选
	EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"`
    // 配置 interface{} 类型编码器。如果没设置,将用 json.Encoder 进行编码
	NewReflectedEncoder func(io.Writer) ReflectedEncoder `json:"-" yaml:"-"`
    // 配置 console 中字段分隔符。默认使用 tab 
	ConsoleSeparator string `json:"consoleSeparator" yaml:"consoleSeparator"`
}
type Entry struct {
	Level      Level
	Time       time.Time
	LoggerName string
	Message    string
	Caller     EntryCaller
	Stack      string
}zap 提供了 2 种日志记录器:`SugaredLogger` 和 `Logger`

SugaredLogger日志

在需要性能但不是很重要的情况下,使用 SugaredLogger 较合适。它比其它结构化日志包快 4-10 倍,包括 结构化日志和 printf 风格的 API

使用: suger.[日志级别]x

w 支持键值对方式传入日志

f 支持%s 方式插值

suger := logger.Sugar() // 开启日志语法糖
    
suger.Debug("我是Debug级别的日志")
suger.Debugw("我是Debug级别的日志", "key1", "value1", "key2", "value2")
suger.Debugf("我是Debug级别的日志%s", "value")

suger.Warn("我是Warn级别的日志")
suger.Warnw("我是Warn级别的日志", "key1", "value1", "key2", "value2")
suger.Warnf("我是Warn级别的日志%s", "value")

suger.Info("我是Info级别的日志")
suger.Infow("我是Info级别的日志", "key1", "value1", "key2", "value2")
suger.Infof("我是Info级别的日志 %s", "info")

suger.Error("我是Error级别的日志")
suger.Errorw("我是Error级别的日志", "key1", "value1", "key2", "value2")
suger.Errorf("我是Error级别的日志 %s", "value")

suger.Panic("我是Panic级别的日志")
suger.Panicw("我是Panic级别的日志", "key1", "value1", "key2", "value2")
suger.Panicf("我是Panic级别的日志 %s", "value")

logger日志

当性能和类型安全很重要时,请使用 Logger。它比 SugaredLogger 更快,分配的资源更少,但它只支持结构化日志和强类型字段

第一个参数打印日志名,后边支持传入可变参, …zapcore.Field类型

zap.String(“key”, “value”) 表示打印key为字符串 value为字符串的 map

logger.Debug("我是Debug级别的日志", zap.String("key", "value"), zap.Int("num", 10))
logger.Warn("我是Warn级别的日志", zap.String("key", "value"), zap.Int("num", 10))
logger.Info("我是Info级别的日志", zap.String("key", "value"), zap.Int("num", 10))
logger.Error("我是Error级别的日志", zap.String("key", "value"), zap.Int("num", 10))
logger.Panic("我是Panic级别的日志", zap.String("key", "value"), zap.Int("num", 10))

输出日志到文件

cfg := zap.NewProductionConfig()
cfg.OutputPaths = []string{
   "./OUTPUT.log", "stderr", "stdout",
}
logger, _ = cfg.Build()

logger.Debug("打印日志到文件")

日志切割归档

lumberjack 这个库是按照日志大小切割日志文件。

go get -u github.com/natefinch/lumberjack@v2
log.SetOutput(&lumberjack.Logger{
    Filename:   "/var/log/myapp/foo.log", // 文件位置
    MaxSize:    500,  // megabytes,M 为单位,达到这个设置数后就进行日志切割
    MaxBackups: 3,    // 保留旧文件最大份数
    MaxAge:     28,   //days , 旧文件最大保存天数
    Compress:   true, // disabled by default,是否压缩日志归档,默认不压缩
})

参照它的文档和结合上面自定义配置

package main

import (
	"fmt"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

func main() {
	lumberjacklogger := &lumberjack.Logger{
		Filename:   "./log-rotate-test.json",
		MaxSize:    1, // megabytes
		MaxBackups: 3,
		MaxAge:     28,   //days
		Compress:   true, // disabled by default
	}
	defer lumberjacklogger.Close()

	config := zap.NewProductionEncoderConfig()

	config.EncodeTime = zapcore.ISO8601TimeEncoder // 设置时间格式
	fileEncoder := zapcore.NewJSONEncoder(config)

	core := zapcore.NewCore(
		fileEncoder,                       //编码设置
		zapcore.AddSync(lumberjacklogger), //输出到文件
		zap.InfoLevel,                     //日志等级
	)

	logger := zap.New(core)
	defer logger.Sync()

    // 测试分割日志
	for i := 0; i < 8000; i++ {
		logger.With(
			zap.String("url", fmt.Sprintf("www.test%d.com", i)),
			zap.String("name", "jimmmyr"),
			zap.Int("age", 23),
			zap.String("agradege", "no111-000222"),
		).Info("test info ")
	}

}

全局logger

zap提供了 2 种全局 Logger,一个是 zap.Logger,调用 zap.L() 获取;
另外一个是 zap.SugaredLogger ,调用 zap.S() 获取

直接调用 zap.L() 或 zap.S() 记录日志的话,它是不会记录任何日志信息。需要调用 ReplaceGlobals() 函数将它设置为全局 Logger。
ReplaceGlobals 替换全局 Logger 和 SugaredLogger,并返回一个函数来恢复原始值

简单使用例子

package main

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

func main() {
	// 直接调用是不会记录日志信息的,所以下面日志信息不会输出
	zap.L().Info("no log info")
	zap.S().Info("no log info [sugared]")

	logger := zap.NewExample()
	defer logger.Sync()

	zap.ReplaceGlobals(logger) // 全局logger,zap.L() 和 zap.S() 需要调用 ReplaceGlobals 函数才会记录日志信息
	zap.L().Info("log info")
	zap.S().Info("log info [sugared]")
}

到此这篇关于golang整合日志zap的实现示例的文章就介绍到这了,更多相关golang整合日志zap内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • 解决GOPATH在GOLAND中的坑

    解决GOPATH在GOLAND中的坑

    这篇文章主要介绍了解决GOPATH在GOLAND中的坑,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Golang内存泄漏详解之原因、检测与修复过程

    Golang内存泄漏详解之原因、检测与修复过程

    本文详细介绍了Golang中的内存泄漏问题,包括内存泄漏的定义、分类、影响以及预防和修复方法,通过使用Golang自带的性能分析工具和火焰图工具,可以有效地检测和定位内存泄漏的代码路径,合理的代码设计和定期的代码审查也是预防内存泄漏的关键
    2024-12-12
  • 基于Go语言实现文件上传服务的示例代码

    基于Go语言实现文件上传服务的示例代码

    在 Web 开发中,文件上传 是常见需求,例如头像上传、文档存储、图片分享等功能,Go 语言的标准库 net/http 已经内置了对 multipart/form-data 类型的支持,能让我们轻松构建一个文件上传服务,本文将带你实现一个可运行的文件上传接口,需要的朋友可以参考下
    2025-08-08
  • Golang接口型函数使用小结

    Golang接口型函数使用小结

    接口函数指的是用函数实现接口,这样在调用的时候就会非常简便,这种方式适用于只有一个函数的接口,这里以迭代一个map为例,演示这一实现的技巧,对Golang接口型函数使用知识感兴趣的朋友一起看看吧
    2022-06-06
  • Go语言开发包安装全流程

    Go语言开发包安装全流程

    这篇文章主要介绍了Go语言开发包安装全流程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-05-05
  • 解决 Golang VS Code 插件下载安装失败的问题

    解决 Golang VS Code 插件下载安装失败的问题

    这篇文章主要介绍了解决 Golang VS Code 插件下载安装失败,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • 使用Go实现在命令行输出好看的表格

    使用Go实现在命令行输出好看的表格

    这篇文章主要介绍了使用Go实现在命令行输出好看的表格方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • Go语言集成mysql驱动、调用数据库、查询数据操作示例

    Go语言集成mysql驱动、调用数据库、查询数据操作示例

    这篇文章主要介绍了Go语言集成mysql驱动、调用数据库、查询数据操作,结合实例形式分析了Go语言安装mysql驱动包、连接mysql数据库及查询等相关操作技巧,需要的朋友可以参考下
    2019-06-06
  • 详解Golang如何优雅的终止一个服务

    详解Golang如何优雅的终止一个服务

    后端服务通常会需要创建子协程来进行相应的作业,但进程接受到终止信号或正常结束时,并没有判断或等待子协程执行结束,下面这篇文章主要给大家介绍了关于Golang如何优雅的终止一个服务的相关资料,需要的朋友可以参考下
    2022-03-03
  • golang 中 recover()的使用方法

    golang 中 recover()的使用方法

    这篇文章主要介绍了Guam与golang  recover()的使用方法,Recover 是一个Go语言的内建函数,可以让进入宕机流程中的 goroutine 恢复过来,下文更多相关资料需要的小伙伴可以参考一下
    2022-04-04

最新评论