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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • go语言操作es的实现示例

    go语言操作es的实现示例

    本文主要介绍了go语言操作es的实现示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • 详解Golang如何实现支持随机删除元素的堆

    详解Golang如何实现支持随机删除元素的堆

    堆是一种非常常用的数据结构,它能够支持在O(1)的时间复杂度获取到最大值(或最小值)。本文主要介绍了如何实现支持O(log(n))随机删除元素的堆,需要的可以参考一下
    2022-09-09
  • VS Code安装go插件失败原因分析以及解决方案

    VS Code安装go插件失败原因分析以及解决方案

    vscode安装go插件时,由于各种原因,在安装插件时总是失败,下面这篇文章主要给大家介绍了关于VS Code安装go插件失败原因分析以及解决的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-09-09
  • Go 对多个网络命令空间中的端口进行监听的解决方案

    Go 对多个网络命令空间中的端口进行监听的解决方案

    这篇文章主要介绍了Go 如何对多个网络命令空间中的端口进行监听,本文给大家介绍的非常详细,需要的朋友可以参考下
    2024-07-07
  • Golang 并发控制模型的实现

    Golang 并发控制模型的实现

    Go控制并发有三种经典的方式,使用 channel 通知实现并发控制、使用 sync 包中的 WaitGroup 实现并发控制、使用 Context 上下文实现并发控制,下面就来介绍一下
    2024-08-08
  • go语言实现sftp包上传文件和文件夹到远程服务器操作

    go语言实现sftp包上传文件和文件夹到远程服务器操作

    这篇文章主要介绍了go语言实现sftp包上传文件和文件夹到远程服务器操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Go gRPC进阶教程服务超时设置

    Go gRPC进阶教程服务超时设置

    这篇文章主要为大家介绍了Go gRPC进阶,gRPC请求的超时时间设置,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • go语言interface接口继承多态示例及定义解析

    go语言interface接口继承多态示例及定义解析

    这篇文章主要为大家介绍了go语言interface接口继承多态示例及定义解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • Go语言Grpc Stream的实现

    Go语言Grpc Stream的实现

    本文主要介绍了Go语言Grpc Stream的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • 浅谈go中cgo的几种使用方式

    浅谈go中cgo的几种使用方式

    本文主要介绍了浅谈go中cgo的几种使用方式,文中根据实例编码详细介绍的十分详尽,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03

最新评论