golang常用库包:log日志记录-uber的Go日志库zap使用详解

科技资讯 投稿 21900 0 评论

golang常用库包:log日志记录-uber的Go日志库zap使用详解

一、简介

zap 是 uber 开源的一个高性能,结构化,分级记录的日志记录包。

zap v1.24.0

zap的特性

  • 日志分级:有 Debug,Info,Warn,Error,DPanic,Panic,Fatal 等

  • 自定义格式:用户可以自定义输出的日志格式

  • 调试:可以打印文件名、函数名、行号、日志时间等,便于调试程序

  • Namespace:日志命名空间。定义命名空间后,所有日志内容就在这个命名空间下。命名空间相当于一个文件夹

高性能介绍

与其它日志库对比

看github官网的对比图,下面的对比图来自:https://github.com/uber-go/zap#performance

Package Time Time % to zap Objects Allocated
⚡ zap 2900 ns/op +0% 5 allocs/op
⚡ zap (sugared 3475 ns/op +20% 10 allocs/op
zerolog 10639 ns/op +267% 32 allocs/op
go-kit 14434 ns/op +398% 59 allocs/op
logrus 17104 ns/op +490% 81 allocs/op
apex/log 32424 ns/op +1018% 66 allocs/op
log15 33579 ns/op +1058% 76 allocs/op

Log a message with a logger that already has 10 fields of context:

Package Time Time % to zap Objects Allocated
⚡ zap 373 ns/op +0% 0 allocs/op
⚡ zap (sugared 452 ns/op +21% 1 allocs/op
zerolog 288 ns/op -23% 0 allocs/op
go-kit 11785 ns/op +3060% 58 allocs/op
logrus 19629 ns/op +5162% 70 allocs/op
log15 21866 ns/op +5762% 72 allocs/op
apex/log 30890 ns/op +8182% 55 allocs/op

printf-style templating:

Package Time Time % to zap Objects Allocated
⚡ zap 381 ns/op +0% 0 allocs/op
⚡ zap (sugared 410 ns/op +8% 1 allocs/op
zerolog 369 ns/op -3% 0 allocs/op
standard library 385 ns/op +1% 2 allocs/op
go-kit 606 ns/op +59% 11 allocs/op
logrus 1730 ns/op +354% 25 allocs/op
apex/log 1998 ns/op +424% 7 allocs/op
log15 4546 ns/op +1093% 22 allocs/op

做了哪些优化

Zap 咋解决呢?Zap 使用一个无反射、零分配的 JOSN 编码器,基础 Logger 尽可能避免序列化开销和内存分配开销。在此基础上,zap 还构建了更高级的 SuggaredLogger。

二、quickstart快速开始

go get -u go.uber.org/zap

SugaredLogger 和 Logger

logger, _ := zap.NewProduction(
defer logger.Sync( // zap底层有缓冲。在任何情况下执行 defer logger.Sync( 是一个很好的习惯
sugar := logger.Sugar(
sugar.Infow("failed to fetch URL",
 // 字段是松散类型,不是强类型
  "url", url,
  "attempt", 3,
  "backoff", time.Second,

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

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

logger, _ := zap.NewProduction(
defer logger.Sync(
logger.Info("failed to fetch URL",
  // 字段是强类型,不是松散类型
  zap.String("url", url,
  zap.Int("attempt", 3,
  zap.Duration("backoff", time.Second,

三、NewExample/NewDevelopment/NewProduction使用

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

NewExample(使用

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

zap.NewExample( 是 zap 为我们提供快速创建 logger 的方法呢?

// 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"
	logger.Info("this is info message"
	logger.Info("this is info message with fileds",
		zap.Int("age", 37, 
        zap.String("agender", "man",
    
	logger.Warn("this is warn message"
	logger.Error("this is error message"
}

输出:

{"level":"debug","msg":"this is debug message"}
{"level":"info","msg":"this is info message"}
{"level":"info","msg":"this is info message with fileds","age":37,"agender":"man"}
{"level":"warn","msg":"this is warn message"}
{"level":"error","msg":"this is error message"}

NewDevelopment(使用

NewDevelopment( 构建一个开发使用的 Logger,它以人性化的格式将 DebugLevel 及以上日志信息输出。它的底层使用

NewDevelopmentConfig(.Build(...Option 构建。它的日志格式各种设置在函数 NewDevelopmentEncoderConfig( 里,想查看详情设置,请点进去查看。

使用例子:

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"
}

输出:

2023-03-22T16:02:45.760+0800    INFO    zapdemos/newdevelopment1.go:13  failed to fetch url     {"url": "http://example.com", "attempt": 3, "duration": "1s"}
2023-03-22T16:02:45.786+0800    INFO    zapdemos/newdevelopment1.go:25  [With] failed to fetch url      {"url": "http://development.com", "attempt": 4, "duration": "5s"}

NewProduction(使用

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

NewProductionConfig(.Build(...Option 构建。它的日志格式设置在函数 NewProductionEncoderConfig 里。

使用例子:

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(
}

输出:

{"level":"info","ts":1679472893.2944522,"caller":"zapdemos/newproduction1.go:16","msg":"failed to fetch URL","url":"http://zap.uber.io","attempt":3,"time":1}
{"level":"info","ts":1679472893.294975,"caller":"zapdemos/newproduction1.go:22","msg":"Failed to fetch URL: http://zap.uber.io"}

使用配置

在这 3 个函数中,可以传入一些配置项。为什么能传入配置项?我们来看看 NewExample( 函数定义:

func NewExample(options ...Option *Logger

...Option 选项,是一个 interface 类型,它关联的是 Logger struct。只要返回 Option 就可以传进 NewExample( 里。在 zap/options.go 文件中可以看到很多返回 Option 的函数,也就是说这些函数都可以传入 NewExample 函数里。这里用到了 Go 里面的一个编码技巧,函数选项模式。

package main

import (
	"go.uber.org/zap"


func main( {
	logger, _ := zap.NewProduction(zap.Fields(
		zap.String("log_name", "testlog",
		zap.String("log_author", "prometheus",
	
	defer logger.Sync(

	logger.Info("test fields output"

	logger.Warn("warn info"
}

输出:

{"level":"info","ts":1679477929.842166,"caller":"zapdemos/fields.go:14","msg":"test fields output","log_name":"testlog","log_author":"prometheus"}
{"level":"warn","ts":1679477929.842166,"caller":"zapdemos/fields.go:16","msg":"warn info","log_name":"testlog","log_author":"prometheus"}

zap.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"
}

输出:

{"level":"info","msg":"test output"}
[zap.Hooks]test Hooks
{"level":"warn","msg":"warn info"}
[zap.Hooks]test Hooks

四、logger和sugaredlogger区别

从上面例子中看出,zap 有 2 种格式化日志方式:logger 和 sugared logger。

    sugared logger:
    它有很好的性能,比一般日志包快 4-10 倍。
  1. 支持结构化的日志。
  2. 支持 printf 风格的日志。
  3. 日志字段不需要定义类型
    logger(没有sugar
    它的性能比 sugared logger 还要快
  1. 它只支持强类型的结构化日志。

比如如果每一次内存分配都很重要的话可以使用这个。对类型安全有严格要求也可以使用这个。

// 创建 logger
logger := zap.NewExample(
defer logger.Sync(

// 转换 SugaredLogger
sugar := logger.Sugar(

// 转换 logger
plain := sugar.Desugar(

怎么快速构建一个 logger 呢?有下面种几种方法:

    zap.NewProduction(
  • zap.NewDevelopment(
  • zap.Example(

    记录日志信息和结构不同。

相同点:

    默认情况下都会打印日志信息到 console 界面
  • 都是通过 logger 调用 Info、Error 等方法

    需要不错的性能但不是很重要的情况下,可以选择 sugaredlogger。它支持结构化日志和 printf 风格的日志记录。sugaredlogger 的日志记录是松散类型的,不是强类型,能接受可变数量的键值对。如果你要用强类型字段记录,可以使用 SugaredLogger.With 方法。
  • 如果是每次或每微秒记录日志都很重要情况下,可以使用 logger,它比 sugaredlogger 每次分配内存更少,性能更高。但它仅支持强类型的结构化日志记录。

五、自定义配置

快速构建 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
}

例子1:基本配置

    zap.Config 自定义配置,看官方的一个基本例子:
package main

import (
	"encoding/json"

	"go.uber.org/zap"


// https://pkg.go.dev/go.uber.org/zap@v1.24.0#hdr-Configuring_Zap
func main( {
	// 表示 zap.Config 的 json 原始编码
	// outputPath: 设置日志输出路径,日志内容输出到标准输出和文件 logs.log
	// errorOutputPaths:设置错误日志输出路径
	rawJSON := []byte(`{
      "level": "debug",
      "encoding": "json",
      "outputPaths": ["stdout", "./logs.log"],
      "errorOutputPaths": ["stderr"],
      "initialFields": {"foo": "bar"},
      "encoderConfig": {
        "messageKey": "message-customer",
        "levelKey": "level",
        "levelEncoder": "lowercase"
      }
    }`

	// 把 json 格式数据解析到 zap.Config struct
	var cfg zap.Config
	if err := json.Unmarshal(rawJSON, &cfg; err != nil {
		panic(err
	}
	// cfg.Build( 为配置对象创建一个 Logger
	// zap.Must( 封装了 Logger,Must(函数如果返回值不是 nil,就会报 panic。也就是检查Build是否错误
	logger := zap.Must(cfg.Build(
	defer logger.Sync(

	logger.Info("logger construction succeeded"
}

/*
Must( 函数
//  var logger = zap.Must(zap.NewProduction(
func Must(logger *Logger, err error *Logger {
    if err != nil {
        panic(err
    }

    return logger
}
*/

consol 输出如下:

{"level":"info","message-customer":"logger construction succeeded","foo":"bar"}

并且在程序目录下生成了一个文件 logs.log,里面记录的日志内容也是上面consol输出内容。每运行一次就在日志文件末尾append一次内容。

例子2:高级配置

或者输出到不是 file 的文件中,比如输出到 kafka 中,那么就需要使用 zapcore 包。

官方例子:

package main

import (
	"io"
	"os"

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


func main( {
	// 首先,定义不同级别日志处理逻辑
	highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level bool {
		return lvl >= zapcore.ErrorLevel
	}
	lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level bool {
		return lvl < zapcore.ErrorLevel
	}

	// 假设有2个kafka 的 topic,一个 debugging,一个 errors

	// zapcore.AddSync 添加一个文件句柄。
	topicDebugging := zapcore.AddSync(io.Discard
	topicErrors := zapcore.AddSync(io.Discard

	// 如果他们对并发使用不安全,我们可以用 zapcore.Lock 添加一个 mutex 互斥锁。
	consoleDebugging := zapcore.Lock(os.Stdout
	consoleErrors := zapcore.Lock(os.Stderr

	// 设置 kafka 和 console 输出配置
	kafkaEncoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig(
	consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig(

	// 把上面的设置加入到 zapcore.NewCore( 函数里,然后再把他们加入到 zapcore.NewTee( 函数里
	core := zapcore.NewTee(
		zapcore.NewCore(kafkaEncoder, topicErrors, highPriority,
		zapcore.NewCore(consoleEncoder, consoleErrors, highPriority,
		zapcore.NewCore(kafkaEncoder, topicDebugging, lowPriority,
		zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority,
	

	// 最后调用 zap.New( 函数
	logger := zap.New(core
	defer logger.Sync(
	logger.Info("constructed a logger"
}

例子3:日志写入文件

与上面例子2相似,但是比它简单

package main

import (
	"os"

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


func main( {
	writetofile(
}

func writetofile( {
	// 设置一些配置参数
	config := zap.NewProductionEncoderConfig(
	config.EncodeTime = zapcore.ISO8601TimeEncoder
	fileEncoder := zapcore.NewJSONEncoder(config
	defaultLogLevel := zapcore.DebugLevel // 设置 loglevel

	logFile, _ := os.OpenFile("./log-test-zap.json", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 06666
	// or os.Create(
	writer := zapcore.AddSync(logFile

	logger := zap.New(
		zapcore.NewCore(fileEncoder, writer, defaultLogLevel,
		zap.AddCaller(,
		zap.AddStacktrace(zapcore.ErrorLevel,
	
	defer logger.Sync(

	url := "http://www.test.com"
	logger.Info("write log to file",
		zap.String("url", url,
		zap.Int("attemp", 3,
	
}

例子4:根据日志级别写入不同文件

这个与上面例子2相似

package main

import (
	"os"

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


func main( {
	writeToFileWithLogLevel(
}

func writeToFileWithLogLevel( {
	// 设置配置
	config := zap.NewProductionEncoderConfig(
	config.EncodeTime = zapcore.ISO8601TimeEncoder
	fileEncoder := zapcore.NewJSONEncoder(config

	logFile, _ := os.OpenFile("./log-debug-zap.json", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666 //日志记录debug信息

	errFile, _ := os.OpenFile("./log-err-zap.json", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666 //日志记录error信息

	teecore := zapcore.NewTee(
		zapcore.NewCore(fileEncoder, zapcore.AddSync(logFile, zap.DebugLevel,
		zapcore.NewCore(fileEncoder, zapcore.AddSync(errFile, zap.ErrorLevel,
	

	logger := zap.New(teecore, zap.AddCaller(
	defer logger.Sync(

	url := "http://www.diff-log-level.com"
	logger.Info("write log to file",
		zap.String("url", url,
		zap.Int("time", 3,
	

	logger.With(
		zap.String("url", url,
		zap.String("name", "jimmmyr",
	.Error("test error "
}

主要是设置日志级别,和把 2 个设置的 NewCore 放入到方法 NewTee 中。

六、Hook和Namespace

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"
}

zap.Namespace(:

package main

import (
	"go.uber.org/zap"


func main( {
	logger := zap.NewExample(
	defer logger.Sync(

	logger.Info("some message",
		zap.Namespace("shop",
		zap.String("name", "LiLei",
		zap.String("grade", "No2",
	

	logger.Error("some error message",
		zap.Namespace("shop",
		zap.String("name", "LiLei",
		zap.String("grade", "No3",
	
}

输出:

{"level":"info","msg":"some message","shop":{"name":"LiLei","grade":"No2"}}
{"level":"error","msg":"some error message","shop":{"name":"LiLei","grade":"No3"}}

七、日志切割归档

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 "
	}

}

八、zap使用总结

    zap 的使用,先创建 logger,再调用各个日志级别方法记录日志信息。比如 logger.Info(。

  • zap.Newproduction(,zap.NewDevelopment(zap.NewExample(。见名思义,Example 一般用在测试代码中,Development 用在开发环境中,Production 用在生成环境中。这三种方法都预先设置好了配置信息。它们的日志数据类型输出都是强类型。

  • zap.New(。比如用户可以自定义一些配置信息等。

  • defer logger.Sync( 这段代码,为什么?因为 zap 底层 API 允许缓冲日志以提高性能,在默认情况下,日志记录器是没有缓冲的。但是在进程退出之前调用 Sync( 方法是一个好习惯。

  • sugaredlogger,把 zap 创建 logger 的三种方法用 logger.Sugar( 包装下,那么 zap 就支持 printf 风格的格式化输出,也支持以 w 结尾的方法。如 Infow,Infof 等。这种就是通用类型日志输出,不是强类型输出,不需要强制指定输出的数据类型。它们的性能区别,通用类型会比强类型下降 50% 左右。

sugar := logger.Sugar(
sugar.Infow("failed to fetch URL",
            "url", url,
            "attempt", 3,
            "backoff", time.Second,

强类型输出,比如 Info 方法输出字段和值就需要指定数据类型:

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

    强类型输出和通用类型输出区别

    zap 强类型输出,zap 为了提供日志输出性能,zap 的强类型输出没有使用 interface{} 和反射。zap 默认输出就是强类型。

    zap.Newproduction(,zap.NewDevelopment(zap.NewExample(就是强类型日志字段,当然,也可以转化为通用类型,用 logger.Sugar( 方法创建 SugaredLogger。

  • zap.Namespace( 创建一个命名空间,后面的字段都在这名字空间中。Namespace 就像一个文件夹,后面文件都放在这个文件夹里。

logger.Info("some message",
    zap.Namespace("shop",
    zap.String("shopid", "s1234323",
  
{"level":"info","msg":"some message","shop":{"shopid":"s1234323"}}

九、Demo源码地址

zap demos

    https://github.com/jiujuan/go-exercises/tree/main/zapdemos

十、参考

    https://github.com/uber-go/zap
  • https://github.com/uber-go/zap/blob/master/FAQ.md
  • https://pkg.go.dev/go.uber.org/zap@v1.24.0
  • https://pkg.go.dev/go.uber.org/zap@v1.24.0#Config zap Config 结构
  • https://pkg.go.dev/go.uber.org/zap/zapcore@v1.24.0#EncoderConfig zapcore EncoderConfig 结构
  • https://github.com/uber-go/zap/blob/v1.24.0/logger.go#L127
  • https://github.com/natefinch/lumberjack/tree/v2.0 rotate log
  • https://github.com/jiujuan/go-exercises/tree/main/zapdemos

编程笔记 » golang常用库包:log日志记录-uber的Go日志库zap使用详解

赞同 (97) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽