go第三方日志库 Zap

//官方文档
//https://pkg.go.dev/go.uber.org/zap#section-readme


package main

import (
	"encoding/json"
	"fmt"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"io"
	"io/ioutil"
	"log"
	"os"
	"time"
)

func Overview()  {

	//在性能不错但不重要的情况下,使用 SugaredLogger
	//比其他结构化日志记录包快 4-10 倍,并支持结构化和 printf 风格的日志记录
	//结构化日志 API 是松散类型的,并接受可变数量的键值对
	sugar := zap.NewExample().Sugar()
	defer sugar.Sync()
	sugar.Infow("failed to fetch URL",
		"url", "http://example.com",
		"attempt", 3,
		"backoff", time.Second,
	)
	sugar.Infof("failed to fetch URL: %s", "http://example.com")


	//记录重要,比如每一微秒和每一次分配都很重要,这种很罕见
	//使用 logger 比 SugaredLogger 更快
	//分配的资源也少得多,但它只支持强类型、结构化的日志记录
	logger := zap.NewExample()
	defer logger.Sync()
	logger.Info("failed to fetch URL",
		zap.String("url", "http://example.com"),
		zap.Int("attempt", 3),
		zap.Duration("backoff", time.Second),
	)


	// logger 和 SugaredLogger 转换
	loggerNew := zap.NewExample()
	defer loggerNew.Sync()
	sugarNew := logger.Sugar()
	plain := sugarNew.Desugar()
	fmt.Println(plain)


	//构建 logger
	//使用 zap 的自以为是的预设:NewExample、NewProduction 和 NewDevelopment
	//这些预设使用单个函数调用构建记录器
	//预设适用于小型项目
	logger, err := zap.NewProduction()
	if err != nil {
		log.Fatalf("can't initialize zap logger: %v", err)
	}
	defer logger.Sync()

}


//扩展zap
func AdvancedConfiguration() {

	//捆绑配置结构仅支持最常见的配置选项。更复杂的需求,如在多个文件之间拆分日志 或写入非文件输出,需要使用zapcore包。
	//在本例中,假设我们都向卡夫卡发送日志并编写 把它们放在控制台上。我们想对控制台输出和卡夫卡进行编码主题不同,
	//我们也希望对高优先级日志。

	// 定义级别处理逻辑
	highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= zapcore.ErrorLevel
	})
	lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl < zapcore.ErrorLevel
	})

	//假设我们有两个卡夫卡主题的客户机。客户端实现 zapcore.WriteSyncer和可安全地同时使用。
	//(如果他们 实现io.Writer,我们可以使用zapcore.AddSync添加无操作同步 方法。
	//如果它们对于并发使用不安全,我们可以添加一个带有zapcore.Lock的互斥锁。)
	topicDebugging := zapcore.AddSync(ioutil.Discard)
	topicErrors := zapcore.AddSync(ioutil.Discard)

	//高优先级输出也应转到标准错误,低优先级输出也应转到标准错误 输出也应转到标准输出。
	consoleDebugging := zapcore.Lock(os.Stdout)
	consoleErrors := zapcore.Lock(os.Stderr)

	//针对机器消耗和控制台输出优化卡夫卡输出 供人工操作人员使用。
	kafkaEncoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
	consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())

	//将输出、编码器和电平处理功能连接到 zapcore.Cores 中,然后将四个放置在一起。
	core := zapcore.NewTee(
		zapcore.NewCore(kafkaEncoder, topicErrors, highPriority),
		zapcore.NewCore(consoleEncoder, consoleErrors, highPriority),
		zapcore.NewCore(kafkaEncoder, topicDebugging, lowPriority),
		zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority),
	)

	// 从 zapcore.Core 就很容易构造一个记录器
	logger := zap.New(core)
	defer logger.Sync()
	logger.Info("constructed a logger")

//输出
// 2021-09-23T09:43:39.260+0800	INFO	constructed a logger

}

func BasicConfiguration()  {
	//对于某些用户,新产品、新开发,NewExample构造函数将不合适。
	//对于用户来说,捆绑的配置结构提供了适当的灵活性平衡 和方便。

	rawJSON := []byte(`{
	  "level": "debug",
	  "encoding": "json",
	  "outputPaths": ["stdout", "logs"],
	  "errorOutputPaths": ["stderr"],
	  "initialFields": {"foo": "bar"},
	  "encoderConfig": {
	    "messageKey": "message",
	    "levelKey": "level",
	    "levelEncoder": "lowercase"
	  }
	}`)

	var cfg zap.Config
	if err := json.Unmarshal(rawJSON, &cfg); err != nil {
		panic(err)
	}
	logger, err := cfg.Build()
	if err != nil {
		panic(err)
	}
	defer logger.Sync()

	logger.Info("logger construction succeeded")

//输出
// {"level":"info","message":"logger construction succeeded","foo":"bar"}
}


func Presets()  {
	//使用zap的预设构造函数是松散的软件包,但它们不允许太多的定制。
	logger := zap.NewExample() // or NewProduction, or NewDevelopment
	defer logger.Sync()

	const url = "http://example.com"

	//在大多数情况下,使用SugaredLogger。它比大多数机器快4-10倍
	//其他结构化日志软件包,并有一个熟悉的、松散类型的API。
	sugar := logger.Sugar()
	sugar.Infow("Failed to fetch URL.",
		//作为松散类型的键值对的结构化上下文。
		"url", url,
		"attempt", 3,
		"backoff", time.Second,
	)
	sugar.Infof("Failed to fetch URL: %s", url)

	//在每微秒都很重要的异常情况下,使用 记录器。
	//它甚至比SugaredLogger更快,但只支持 结构化日志记录。
	logger.Info("Failed to fetch URL.",
		//结构化上下文作为强类型字段。
		zap.String("url", url),
		zap.Int("attempt", 3),
		zap.Duration("backoff", time.Second),
	)

//输出
// {"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"}
// {"level":"info","msg":"Failed to fetch URL: http://example.com"}
// {"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"}
}


//日志等级
func LogLevel()  {
	// DEBUGLEVEL日志通常体积大,并且在通常被生产禁用。
	DEBUGLEVEL = zapcore.DebugLevel
	// InfoLevel是默认的日志记录优先级。
	InfoLevel = zapcore.InfoLevel
	// WarnLevel日志不是Info更重要,但不需要单独人工审核。
	WarnLevel = zapcore.WarnLevel
	//ErrorLevel日志是高优先级的。如果一个应用程序运行流畅,不会产生任何错误级别的日志的
	ErrorLevel = zapcore.ErrorLevel
	// DPanicLevel 日志是特别重要的错误。在开发中, 记录器在写入消息后发生恐慌。
	DPanicLevel = zapcore.DPanicLevel
	// PanicLevel 记录一条消息,然后恐慌。
	PanicLevel = zapcore.PanicLevel
	// FatalLevel 记录一条消息,然后调用 os.Exit(1)。
	FatalLevel = zapcore.FatalLevel
}

//CombineWriteSyncers 是一个实用程序,可将多个 WriteSyncer 组合成一个锁定的 WriteSyncer。如果未提供输入,则返回无操作 WriteSyncer。
//它的提供纯粹是为了方便;结果与单独使用 zapcore.NewMultiWriteSyncer 和 zapcore.Lock 没有什么不同。
func CombineWriteSyncers(writers ...zapcore.WriteSyncer) zapcore.WriteSyncer

//LevelFlag 使用标准库的 flag.Var 来声明一个具有指定名称、默认值和使用指南的全局标志。返回值是一个指向标志值的指针。
//如果你不想使用标志包的全局状态,你可以使用任何非 nil *Level 作为带有你自己的 *flag.FlagSet 的 flag.Value。
func LevelFlag(name string, defaultLevel zapcore.Level, usage string) *zapcore.Level

//NewDevelopmentEncoderConfig 为开发环境返回一个自以为是的 EncoderConfig。
func NewDevelopmentEncoderConfig() zapcore.EncoderConfig

//NewProductionEncoderConfig 为生产环境返回一个自以为是的 EncoderConfig。
func NewProductionEncoderConfig() zapcore.EncoderConfig

//NewStdLog 返回一个 *log.Logger,它在 InfoLevel 写入提供的 zap Logger。要重定向标准库的包全局日志功能,请改用 RedirectStdLog。
func NewStdLog(l *Logger) *log.Logger

//示例
func FunctionsExampaleNewStdLog()  {
	logger := zap.NewExample()
	defer logger.Sync()
	std := zap.NewStdLog(logger)
	std.Print("standard logger wrapper")

//输出
// {"level":"info","msg":"standard logger wrapper"}
}

//NewStdLogAt 返回 *log.Logger,它以所需级别写入提供的 zap 记录器。
func NewStdLogAt(l *Logger, level zapcore.Level) (*log.Logger, error)

//Open 是一个高级包装器,它采用可变数量的 URL,打开或创建每个指定的资源,并将它们组合到一个锁定的 WriteSyncer 中。
//它还返回遇到的任何错误和关闭任何打开文件的函数。 不传递 URL 会返回一个无操作的 WriteSyncer。
//Zap 处理没有方案的 URL 和具有“文件”方案的 URL。第三方代码可以使用 RegisterSink 为其他方案注册工厂。
//具有“文件”方案的 URL 必须使用本地文件系统上的绝对路径。不允许使用用户、密码、端口、片段或查询参数,并且主机名必须为空或“localhost”。
//由于将日志写入本地文件系统很常见,因此没有方案的 URL(例如,“/var/log/foo.log”)被视为本地文件路径。
//如果没有方案,特殊路径“stdout”和“stderr”被解释为 os.Stdout 和 os.Stderr。当没有指定方案时,相对文件路径也有效。
func Open(paths ...string) (zapcore.WriteSyncer, func(), error)

//RedirectStdLog 将标准库的包全局记录器的输出重定向到 InfoLevel 提供的记录器。
//由于 zap 已经处理调用者注释、时间戳等,它会自动禁用标准库的注释和前缀。
//它返回一个函数来恢复原始前缀和标志并将标准库的输出重置为 os.Stderr。
func RedirectStdLog(l *Logger) func()



//示例
func FunctionsExampaleRedirectStdLog()  {
	logger := zap.NewExample()
	defer logger.Sync()
	undo := zap.RedirectStdLog(logger)
	defer undo()
	log.Print("redirected standard library")

//输出
// {"level":"info","msg":"redirected standard library"}
}

func RedirectStdLogAt(l * Logger , level zapcore . Level ) (func(), error )
//RedirectStdLogAt 将标准库的包全局记录器的输出重定向到指定级别的提供的记录器。由于 zap 已经处理调用者注释、时间戳等,它会自动禁用标准库的注释和前缀。
//它返回一个函数来恢复原始前缀和标志并将标准库的输出重置为 os.Stderr。

func RegisterEncoder(name string , constructor func( zapcore . EncoderConfig ) ( zapcore . Encoder , error )) error
//RegisterEncoder 注册一个编码器构造函数,然后 Config 结构可以引用它。默认情况下,注册“json”和“console”编码器。
//尝试注册名称已被占用的编码器会返回错误。

func RegisterSink(scheme string , factory func(* url . URL ) ( Sink , error )) error
//RegisterSink 使用特定方案为所有接收器注册用户提供的工厂。
//所有方案都必须是 ASCII,在RFC 3986 ( https://tools.ietf.org/html/rfc3986#section-3.1 ) 的第 3.1 节下有效,
//并且必须没有注册工厂。Zap 自动为“文件”方案注册一个工厂。

func ReplaceGlobals(logger * Logger ) func()
//ReplaceGlobals 替换全局 Logger 和 SugaredLogger,并返回一个函数来恢复原始值。并发使用是安全的。

func FunctionsExampaleReplaceGlobals() {
	logger := zap.NewExample()
	defer logger.Sync()
	undo := zap.ReplaceGlobals(logger)
	defer undo()
	zap.L().Info("replaced zap's global loggers")

//输出:
// {"level":"info","msg":"replaced zap's global loggers"}
}

type AtomicLevel struct {
	// 包含过滤或未导出的字段
}
//AtomicLevel 是原子可更改的动态日志记录级别。它允许您在运行时安全地更改记录器树(根记录器和通过添加上下文创建的任何子记录器)的日志级别。
//AtomicLevel 本身是一个 http.Handler,它为 JSON 端点提供服务以更改其级别。
//必须使用 NewAtomicLevel 构造函数创建 AtomicLevels 以分配其内部原子指针。

func FunctionsExampaleAtomicLevel() {
	atom := zap.NewAtomicLevel()
	// 要使示例具有确定性,请禁用输出中的时间戳。
	encoderCfg := zap.NewProductionEncoderConfig()
	encoderCfg.TimeKey = ""
	logger := zap.New(zapcore.NewCore(
		zapcore.NewJSONEncoder(encoderCfg),
		zapcore.Lock(os.Stdout),
		atom,
	))
	defer logger.Sync()
	logger.Info("info logging enabled")
	atom.SetLevel(zap.ErrorLevel)
	logger.Info("info logging disabled")

//输出
// {"level":"info","msg":"info logging enabled"}
}

func FunctionsExampaleSetLevel() {
	//Config结构包含一个原子级别。要使用它,请保持 对配置文件的引用。
	rawJSON := []byte(`{
		"level": "info",
		"outputPaths": ["stdout"],
		"errorOutputPaths": ["stderr"],
		"encoding": "json",
		"encoderConfig": {
			"messageKey": "message",
			"levelKey": "level",
			"levelEncoder": "lowercase"
		}
	}`)
	var cfg zap.Config
	if err := json.Unmarshal(rawJSON, &cfg); err != nil {
		panic(err)
	}
	logger, err := cfg.Build()
	if err != nil {
		panic(err)
	}
	defer logger.Sync()

	logger.Info("info logging enabled")

	cfg.Level.SetLevel(zap.ErrorLevel)
	logger.Info("info logging disabled")

//输出
// {"level":"info","message":"info logging enabled"}
}

func NewAtomicLevel() AtomicLevel
//NewAtomicLevel 创建一个启用了 InfoLevel 和以上日志记录的 AtomicLevel。

func NewAtomicLevelAt(l zapcore . Level ) AtomicLevel
//NewAtomicLevelAt 是一个方便的函数,它创建一个 AtomicLevel,然后使用给定的级别调用 SetLevel。

func (lvl AtomicLevel ) Enabled (l zapcore . Level ) bool
//Enabled 实现了 zapcore.LevelEnabler 接口,它允许使用 AtomicLevel 代替传统的静态级别。

func (lvl AtomicLevel ) Level() zapcore.Level
//级别返回启用的最小日志级别。

func (lvl AtomicLevel ) MarshalText() (text [] byte , err error )
//MarshalText 将 AtomicLevel 编组为字节切片。它使用与静态 zapcore.Levels 相同的文本表示(“debug”、“info”、“warn”、“error”、“dpanic”、“panic”和“fatal”)。

func (lvl AtomicLevel ) ServeHTTP(w http . ResponseWriter , r * http . Request )
//ServeHTTP 是一个简单的 JSON 端点,可以报告或更改当前的日志记录级别。
//GET 请求返回当前日志记录级别的 JSON 描述,例如:{"level":"info"}
//PUT 请求更改日志记录级别。在程序运行时更改日志记录级别是完全安全的。支持两种内容类型:
//Content-Type: application/x-www-form-urlencoded
//使用此内容类型,可以通过请求正文或查询参数提供级别。日志级别是 URL 编码的,如:
//如果指定了两个参数,则请求正文优先于查询参数。
//此内容类型是 curl PUT 请求的默认类型。以下是两个示例 curl 请求,它们都将日志记录级别设置为调试。
//curl -X PUT localhost:8080/log/level?level=debug
//curl -X PUT localhost:8080/log/level -d level=debug
//对于任何其他内容类型,有效负载应为 JSON 编码,如下所示:{"level":"info"}
//示例 curl 请求可能如下所示:
//curl -X PUT localhost:8080/log/level -H "Content-Type: application/json" -d '{"level":"debug"}'

func(lvl AtomicLevel)SetLevel(L zapcore.Level)
//SetLevel 更改日志记录级别。

func (lvl AtomicLevel ) String() string
//String 返回底层 Level 的字符串表示形式。

func (lvl * AtomicLevel ) UnmarshalText(text [] byte ) error
//UnmarshalText 将文本解组为 AtomicLevel。它使用与静态 zapcore.Levels 相同的文本表示(“debug”、“info”、“warn”、“error”、“dpanic”、“panic”和“fatal”)。

type Config struct {
	// 级别是启用的最小日志级别。请注意,这是一个动态// 级别,因此调用 Config.Level.SetLevel 将自动更改// 从此配置派生的所有记录器的日志级别。
	Level AtomicLevel `json:"level" yaml:"level"`
	// 开发将记录器置于开发模式,这改变了// DPanicLevel 的行为并更自由地获取堆栈跟踪。
	Development bool `json:"development" yaml:"development"`
	// DisableCaller 停止使用调用函数的文件// 名称和行号注释日志。默认情况下,所有日志都带有注释。
	DisableCaller bool `json:"disableCaller" yaml:"disableCaller"`
	// DisableStacktrace 完全禁用自动堆栈跟踪捕获。通过//默认情况下,踪迹被捕获WarnLevel及以上日志//发展和的ErrorLevel及以上生产。
	DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"`
	// 采样设置采样策略。一个 nil SamplingConfig 禁用采样。
	Sampling * SamplingConfig `json:"sampling" yaml:"sampling"`
	// 编码设置记录器的编码。有效值为“json”和// “console”,以及通过// RegisterEncoder 注册的任何第三方编码。
	Encoding string `json:"encoding" yaml:"encoding"`
	// EncoderConfig 为所选编码器设置选项。有关详细信息,请参阅// zapcore.EncoderConfig。
	EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"`
	// OutputPaths 是用于写入日志输出的 URL 或文件路径列表。
	OutputPaths []string `json:"outputPaths" yaml:"outputPaths"`
	// ErrorOutputPaths 是用于写入内部记录器错误的 URL 列表。// 默认为标准错误。// // 注意这个设置只影响内部错误;对于示例代码
	// 将错误级别的日志发送到与信息级别和调试级别不同的位置
	// 日志,请参阅包级别的 AdvancedConfiguration 示例。
	ErrorOutputPaths [] string `json:"errorOutputPaths" yaml:"errorOutputPaths"`
	// InitialFields 是要添加到根记录器的字段集合。
	InitialFields map[ string ]interface{} `json:"initialFields" yaml:"initialFields"`

}
//Config 提供了一种声明式的方式来构造记录器。它不会执行 New、Options 以及各种 zapcore.WriteSyncer
//和 zapcore.Core 包装器无法完成的任何操作,但它是切换常用选项的一种更简单的方法。
//请注意,Config 有意仅支持最常见的选项。更不寻常的日志设置(记录到网络连接或消息队列,在多个文件之间拆分输出等)是可能的,
//但需要直接使用 zapcore 包。有关示例代码,请参阅包级 BasicConfiguration 和 AdvancedConfiguration 示例。
//有关显示运行时日志级别更改的示例,请参阅 AtomicLevel 的文档。

func NewDevelopmentConfig() Config
//NewDevelopmentConfig 是一个合理的开发日志配置。在 DebugLevel 及更高级别启用日志记录。
//它启用开发模式(使 DPanicLevel 日志恐慌),使用控制台编码器,写入标准错误,并禁用采样。
//堆栈跟踪会自动包含在 WarnLevel 及更高级别的日志中。

func NewProductionConfig() Config
//NewProductionConfig 是合理的生产日志配置。在 InfoLevel 及更高级别启用日志记录。
//它使用 JSON 编码器,写入标准错误,并启用采样。堆栈跟踪会自动包含在 ErrorLevel 及更高级别的日志中。

func (cfg Config ) Build(opts ... Option ) (* Logger , error )
//Build 从 Config 和 Options 构造一个记录器。

type Field = zapcore.Field
//Field 是 Field 的别名。别名这种类型极大地提高了这个包的 API 文档的可导航性。

func Any(key string , value interface{}) Field
//Any 接受一个键和一个任意值,并选择将它们表示为字段的最佳方式,仅在必要时回退到基于反射的方法。
//由于 byte/uint8 和 rune/int32 是别名,Any 无法区分它们。为了尽量减少意外,[]byte 值被视为二进制 blob,字节值被视为 uint8,符文始终被视为整数。

func Array(key string , val zapcore . ArrayMarshaler ) Field
//Array 使用给定的键和 ArrayMarshaler 构造一个字段。它提供了一种灵活但仍然是类型安全且高效的方式来向日志上下文添加类数组类型。
//结构体的 MarshalLogArray 方法被惰性调用。

func Binary(key string , val [] byte ) Field
//Binary 构造一个带有不透明二进制 blob 的字段。
//二进制数据以适合编码的格式序列化。例如,zap 的 JSON 编码器 base64 编码二进制 blob。要记录 UTF-8 编码的文本,请使用 ByteString。

func Bool(key string , val bool ) Field
//Bool 构造一个带有布尔值的字段。

func Boolp(key string , val * bool ) Field
//Boolp 构造一个带有 *bool 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Bools(key string , bs [] bool ) Field
//Bools 构造一个带有布尔切片的字段。

func ByteString(key string , val [] byte ) Field
//ByteString 构造一个字段,该字段将 UTF-8 编码的文本作为 [] 字节进行传输。要记录不透明的二进制 blob(不一定是有效的 UTF-8),请使用 Binary。

func ByteStrings(key string , bss [][] byte ) Field
//ByteStrings 构造了一个字段,该字段携带一个 []byte 的片段,每个片段必须是 UTF-8 编码的文本。

func Complex128(key string , val complex128 ) Field
//Complex128 构造一个带有复数的字段。与大多数数字字段不同,这需要分配(将 complex128 转换为 interface{})。

func Complex128p(key string , val * complex128 ) Field
//Complex128p 构造一个带有 *complex128 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Complex128s(key string , nums [] complex128 ) Field
//Complex128s 构造一个带有复数切片的字段。

func Complex64(key string , val complex64 ) Field
//Complex64 构造一个带有复数的字段。与大多数数字字段不同,这需要分配(将 complex64 转换为 interface{})。

func Complex64p(key string , val * complex64 ) Field
//Complex64p 构造一个带有 *complex64 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Complex64s(key string , nums [] complex64 ) Field
//Complex64s 构造一个带有复数切片的字段。

func Duration(key string , val time . Duration ) Field
//Duration 使用给定的键和值构造一个字段。编码器控制持续时间的序列化方式。

func Durationp(key string , val * time . Duration ) Field
//Durationp 构造一个带有 *time.Duration 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Durations(key string , ds [] time . Duration ) Field
//Durations 构造一个携带时间片段的字段。Durations。

func Error(err error ) Field
//Error 是常见习语 NamedError("error", err) 的简写。

func Errors(key string , errs [] error ) Field
//Errors 构造一个带有错误切片的字段。

func Float32(key string , val float32 ) Field
//Float32 构造一个带有 float32 的字段。浮点值的表示方式取决于编码器,因此编组必然是惰性的。

func Float32p(key string , val * float32 ) Field
//Float32p 构造一个带有 *float32 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Float32s(key string , nums [] float32 ) Field
//Float32s 构造了一个带有浮点切片的字段。

func Float64(key string , val float64 ) Field
//Float64 构造了一个带有 float64 的字段。浮点值的表示方式取决于编码器,因此编组必然是惰性的。

func Float64p(key string , val * float64 ) Field
//Float64p 构造一个带有 *float64 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Float64s(key string , nums [] float64 ) Field
//Float64s 构造了一个带有浮点切片的字段。

func Inline(val zapcore . ObjectMarshaler ) Field
//Inline 构造了一个类似于 Object 的 Field,但它会将提供的 ObjectMarshaler 的元素添加到当前命名空间中。

func Int(key string , val int ) Field
//Int 使用给定的键和值构造一个字段。

func Int16(key string , val int16 ) Field
//Int16 使用给定的键和值构造一个字段。

func Int16p(key string , val * int16 ) Field
//Int16p 构造一个带有 *int16 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Int16s(key string , nums [] int16 ) Field
//Int16s 构造一个带有整数切片的字段。

func Int32(key string , val int32 ) Field
//Int32 使用给定的键和值构造一个字段。

func Int32p(key string , val * int32 ) Field
//Int32p 构造一个带有 *int32 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Int32s(key string , nums [] int32 ) Field
//Int32s 构造一个携带整数切片的字段。

func Int64(key string , val int64 ) Field
//Int64 使用给定的键和值构造一个字段。

func Int64p(key string , val * int64 ) Field
//Int64p 构造了一个带有 *int64 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Int64s(key string , nums [] int64 ) Field
//Int64s 构造一个带有整数切片的字段。

func Int8(key string , val int8 ) Field
//Int8 使用给定的键和值构造一个字段。

func Int8p(key string , val * int8 ) Field
//Int8p 构造了一个带有 *int8 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Int8s(key string , nums [] int8 ) Field
//Int8s 构造了一个带有整数切片的字段。

func Intp(key string , val * int ) Field
//Intp 构造了一个带有 *int 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Ints(key string , nums [] int ) Field
//Ints 构造一个带有整数切片的字段。

func NamedError(key string , err error ) Field
//NamedError 构造一个字段,在提供的键下懒惰地存储 err.Error() 。也实现 fmt.Formatter 的错误
//(如 github.com/pkg/errors 产生的错误)也将其详细表示存储在键+“详细”下。
//如果传递 nil 错误,则该字段为空操作。
//对于密钥只是“错误”的常见情况,错误函数更短且重复性更低。

func Namespace(key string ) Field
//命名空间在记录器的上下文中创建一个命名的、隔离的范围。所有后续字段都将添加到新命名空间。
//这有助于在将记录器注入子组件或第三方库时防止密钥冲突。

func FunctionsExampaleNamespace() {
	logger := zap.NewExample()
	defer logger.Sync()

	logger.With(
		zap.Namespace("metrics"),
		zap.Int("counter", 1),
	).Info("tracked some metrics")

//输出
//{"level":"info","msg":"tracked some metrics","metrics":{"counter":1}}
}

func Object(key string , val zapcore . ObjectMarshaler ) Field
//Object 使用给定的键和 ObjectMarshaler 构造一个字段。
//它提供了一种灵活但仍然是类型安全且高效的方式来将类似映射或结构的用户定义类型添加到日志记录上下文中。
//结构体的 MarshalLogObject 方法被延迟调用。

type addr struct {
	IP   string
	Port int
}

type request struct {
	URL    string
	Listen addr
	Remote addr
}

func (a addr) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	enc.AddString("ip", a.IP)
	enc.AddInt("port", a.Port)
	return nil
}

func (r request) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	enc.AddString("url", r.URL)
	zap.Inline(r.Listen).AddTo(enc)
	return enc.AddObject("remote", r.Remote)
}

func FunctionsExampaleObect() {
	logger := zap.NewExample()
	defer logger.Sync()

	req := &request{
		URL:    "/test",
		Listen: addr{"127.0.0.1", 8080},
		Remote: addr{"127.0.0.1", 31200},
	}
	logger.Info("new request, in nested object", zap.Object("req", req))
	logger.Info("new request, inline", zap.Inline(req))

//输出
//{"level":"info","msg":"new request, in nested object","req":{"url":"/test","ip":"127.0.0.1","port":8080,"remote":{"ip":"127.0.0.1","port":31200}}}
//{"level":"info","msg":"new request, inline","url":"/test","ip":"127.0.0.1","port":8080,"remote":{"ip":"127.0.0.1","port":31200}}
}

func Reflect(key string , val interface{}) Field
//Reflect 使用给定的键和任意对象构造一个字段。它使用适合编码的、基于反射的函数将几乎任何对象延迟序列化到日志记录上下文中,
//但它相对较慢且分配繁重。在外部测试中,Any 始终是更好的选择。
//如果编码失败(例如,尝试将 map[int]string 序列化为 JSON),则 Reflect 在最终日志输出中包含错误消息。

func Skip() Field
//Skip 构造一个无操作字段,这在处理其他 Field 构造函数中的无效输入时通常很有用。

func Stack(key string ) Field
//Stack 构造一个字段,该字段在提供的键下存储当前 goroutine 的堆栈跟踪。请记住,
//进行堆栈跟踪既急切又昂贵(相对而言);此函数既进行分配,又需要大约两微秒。

func StackSkip(key string , skip int ) Field
//StackSkip 构造一个类似于 Stack 的字段,但还会从堆栈跟踪的顶部跳过给定数量的帧。

func String(key string , val string ) Field
//String 使用给定的键和值构造一个字段。

func Stringer(key string , val fmt . Stringer ) Field
//Stringer 使用给定的键和值的 String 方法的输出构造一个字段。Stringer 的 String 方法被延迟调用。

func Stringp(key string , val * string ) Field
//Stringp 构造一个带有 *string 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Strings(key string , ss [] string ) Field
//Strings 构造了一个字段,该字段带有一段字符串。

func Time(key string , val time . Time ) Field
//Time 使用给定的键和值构造一个字段。编码器控制时间的序列化方式。

func Timep(key string , val * time . Time ) Field
//Timep 构造了一个带有 *time.Time 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Times(key string , ts [] time . Time ) Field
//Times 构造了一个字段,该字段带有 time.Times 的切片。

func Uint(key string , val uint ) Field
//Uint 使用给定的键和值构造一个字段。

func Uint16(key string , val uint16 ) Field
//Uint16 使用给定的键和值构造一个字段。

func Uint16p(key string , val * uint16 ) Field
//Uint16p 构造一个带有 *uint16 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Uint16s(key string , nums [] uint16 ) Field
//Uint16s 构造一个带有无符号整数切片的字段。

func Uint32(key string , val uint32 ) Field
//Uint32 使用给定的键和值构造一个字段。

func Uint32p(key string , val * uint32 ) Field
//Uint32p 构造一个带有 *uint32 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Uint32s(key string , nums [] uint32 ) Field
//Uint32s 构造一个带有无符号整数切片的字段。

func Uint64(key string , val uint64 ) Field
//Uint64 使用给定的键和值构造一个字段。

func Uint64p(key string , val * uint64 ) Field
//Uint64p 构造一个带有 *uint64 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Uint64s(key string , nums [] uint64 ) Field
//Uint64s 构造一个带有无符号整数切片的字段。

func Uint8(key string , val uint8 ) Field
//Uint8 使用给定的键和值构造一个字段。

func Uint8p(key string , val * uint8 ) Field
//Uint8p 构造一个带有 *uint8 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Uint8s(key string , nums [] uint8 ) Field
//Uint8s 构造一个带有无符号整数切片的字段。

func Uintp(key string , val * uint ) Field
//Uintp 构造一个带有 *uint 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Uintptr(key string , val uintptr ) Field
//Uintptr 使用给定的键和值构造一个字段。

func Uintptrp(key string , val * uintptr ) Field
//Uintptrp 构造一个带有 *uintptr 的字段。在适当的时候,返回的 Field 将安全且明确地表示 `nil`。

func Uintptrs(key string , us [] uintptr ) Field
//Uintptrs 构造了一个字段,其中包含一个指针地址切片。

func Uints(key string , nums [] uint ) Field
//Uints 构造一个带有无符号整数切片的字段。

type LevelEnablerFunc func( zapcore . Level ) bool
//LevelEnablerFunc 是一种使用匿名函数实现 zapcore.LevelEnabler 的便捷方法。
//当在不同的输出(例如,标准错误和标准输出)之间拆分日志输出时,它特别有用。有关示例代码,请参阅包级 AdvancedConfiguration 示例。

func (f LevelEnablerFunc ) 启用(lvl zapcore . Level ) bool
//Enabled 调用封装的函数。

type Logger struct {
	// 包含过滤或未导出的字段
}
//Logger 提供快速、分级、结构化的日志记录。所有方法都可以安全地同时使用。
//Logger 是为每一微秒和每一次分配都很重要的上下文而设计的,
//因此它的 API 有意支持性能和类型安全而不是简洁。对于大多数应用程序,
//SugaredLogger 在性能和人体工程学之间取得了更好的平衡。

func L() *Logger
//L 返回全局 Logger,可以用 ReplaceGlobals 重新配置。并发使用是安全的。

func New(core zapcore . Core , options ... Option ) *Logger
//New 从提供的 zapcore.Core 和 Options 构造一个新的 Logger。如果传递的 zapcore.Core 为零,则它会回退到使用无操作实现。
//这是构建 Logger 最灵活的方式,但也是最冗长的。对于典型用例,高度自以为是的预设(NewProduction、NewDevelopment 和 NewExample)或 Config 结构更方便。
//有关示例代码,请参阅包级 AdvancedConfiguration 示例。

func NewDevelopment(options ... Option ) (* Logger , error )
//NewDevelopment 构建了一个开发 Logger,它以人性化的格式将 DebugLevel 及以上的日志写入标准错误。
//这是 NewDevelopmentConfig().Build(...Option) 的快捷方式。

func NewExample(options ... Option ) *Logger
//NewExample 构建了一个 Logger,专为在 zap 的可测试示例中使用而设计。
//它将 DebugLevel 及以上日志写入标准输出为 JSON,但省略了时间戳和调用函数以保持示例输出的简短和确定性。

func NewNop() *Logger
//NewNop 返回一个无操作记录器。它从不写出日志或内部错误,也从不运行用户定义的钩子。
//使用 WithOptions 替换无操作记录器的核心或错误输出可以重新启用日志记录。

func NewProduction(options ... Option ) (* Logger , error )
//NewProduction 构建了一个合理的生产记录器,它将 InfoLevel 和更高级别的日志作为 JSON 写入标准错误。
//这是 NewProductionConfig().Build(...Option) 的快捷方式。

func (log * Logger ) Check(lvl zapcore.Level , msg string ) *zapcore.CheckedEntry
//如果启用了在指定级别记录消息,则 Check 返回 CheckedEntry。这是一个完全可选的优化;在高性能应用程序中,Check 可以帮助避免分配一个切片来保存字段。

func FunctionsExampaleCheck() {
	logger := zap.NewExample()
	defer logger.Sync()
	if ce := logger.Check(zap.DebugLevel, "debugging"); ce != nil {
		//如果调试级别日志输出未启用,或者如果zap的采样将
		//已删除此日志项,我们不分配保存这些日志项的切片
		ce.Write(
			zap.String("foo", "bar"),
			zap.String("baz", "quux"),
		)
	}

//输出
//{"level":"debug","msg":"debugging","foo":"bar","baz":"quux"}
}

func (log * Logger ) Core() zapcore.Core
//Core 返回 Logger 的底层 zapcore.Core。

func (log * Logger ) DPanic(msg string , fields ... Field )
//DPanic 在 DPanicLevel 记录一条消息。该消息包括在日志站点传递的任何字段,以及记录器上累积的任何字段。
//如果记录器处于开发模式,则它会发生恐慌(DPanic 的意思是“开发恐慌”)。这对于捕获可恢复但不应该发生的错误很有用。

func (log * Logger ) Debug(msg string , fields ... Field )
//Debug 在 DebugLevel 记录一条消息。该消息包括在日志站点传递的任何字段,以及记录器上累积的任何字段。

func (log * Logger ) Error(msg string , fields ... Field )
//Error 在 ErrorLevel 记录一条消息。该消息包括在日志站点传递的任何字段,以及记录器上累积的任何字段。

func (log * Logger ) Fatal(msg string , fields ... Field )
//Fatal 在 FatalLevel 记录一条消息。该消息包括在日志站点传递的任何字段,以及记录器上累积的任何字段。
//然后记录器调用 os.Exit(1),即使禁用了 FatalLevel 的记录。

func (log * Logger ) Info(msg string , fields ... Field )
//Info 在 InfoLevel 记录一条消息。该消息包括在日志站点传递的任何字段,以及记录器上累积的任何字段。

func (log * Logger ) Named(s string ) * Logger
//Named 将一个新的路径段添加到记录器的名称中。段由句点连接。默认情况下,记录器是未命名的。

func (log * Logger ) Panic(msg string , fields ... Field )
//Panic 在 PanicLevel 记录一条消息。该消息包括在日志站点传递的任何字段,以及记录器上累积的任何字段。
//即使禁用了 PanicLevel 的日志记录,记录器也会出现恐慌。

func (log * Logger ) Sugar() * SugaredLogger
//Sugar 包装了 Logger 以提供更符合人体工程学但速度稍慢的 API。对 Logger 加糖是相当便宜的,
//因此单个应用程序同时使用 Loggers 和 SugaredLoggers 是合理的,在性能敏感代码的边界上在它们之间进行转换。

func (log * Logger ) Sync() error
//Sync 调用底层 Core 的 Sync 方法,刷新任何缓冲的日志条目。应用程序应注意在退出前调用 Sync。

func (log * Logger ) Warn(msg string , fields ... Field )
//Warn 在 WarnLevel 记录一条消息。该消息包括在日志站点传递的任何字段,以及记录器上累积的任何字段。

func (log * Logger ) With(fields ... Field ) * Logger
//With 创建一个子记录器并向其添加结构化上下文。添加到子项的字段不会影响父项,反之亦然。

func (log * Logger ) WithOptions(opts ... Option ) * Logger
//WithOptions 克隆当前的 Logger,应用提供的 Options,并返回结果 Logger。同时使用是安全的。

type Option interface {
	// 包含过滤或未导出的方法
}
//一个选项配置一个记录器。

func AddCaller() Option
//AddCaller 将 Logger 配置为使用 zap 调用者的文件名、行号和函数名来注释每条消息。另请参阅 WithCaller。

func AddCallerSkip(skip int ) Option
//AddCallerSkip 增加了呼叫者注释跳过的呼叫者数量(由 AddCaller 选项启用)。在围绕 Logger 和 SugaredLogger 构建包装器时,提供此 Option 可防止 zap 始终将包装器代码报告为调用者。

func AddStacktrace(LVL zapcore.LevelEnabler) Option
//AddStacktrace 将 Logger 配置为记录处于或高于给定级别的所有消息的堆栈跟踪。

func Development() Option
//开发将记录器置于开发模式,这使得 DPanic 级别的日志恐慌,而不是简单地记录错误。

func ErrorOutput(W zapcore.WriteSyncer) Option
//ErrorOutput 为 Logger 生成的错误设置目标。请注意,此选项仅影响内部错误;有关将错误级日志发送到与信息级和调试级日志不同的位置的示例代码,请参阅包级 AdvancedConfiguration 示例。
//提供的 WriteSyncer 必须可以安全地并发使用。Open 和 zapcore.Lock 函数是使用互斥锁保护文件的最简单方法。

func Fields(fs ... Field ) Option
//字段向记录器添加字段。

func Hooks(hooks ...func( zapcore . Entry ) error ) Option
//Hooks 注册函数,每次 Logger 写出条目时都会调用这些函数。重复使用 Hooks 是附加的。
//钩子对于简单的副作用很有用,比如捕获已发出日志数量的指标。更复杂的副作用,
//包括任何需要访问条目结构化字段的东西,都应该作为 zapcore.Core 来实现。有关详细信息,请参阅 zapcore.RegisterHooks。

func IncreaseLevel(lvl zapcore . LevelEnabler) Option
//IncreaseLevel 增加记录器的级别。如果传入的级别试图降低记录器的级别,则无效。

func OnFatal(action zapcore . CheckWriteAction ) Option
//OnFatal 设置对致命日志采取的操作。

func WithCaller(enabled bool ) Option
//WithCaller 将 Logger 配置为使用 zap 调用者的文件名、行号和函数名称来注释每条消息,或者不使用,取决于启用的值。这是 AddCaller 的通用形式。

func WithClock(clock zapcore . Clock ) Option
//WithClock 指定记录器用于确定记录条目的当前时间的时钟。默认为带有 time.Now 的系统时钟。

func WrapCore(f func( zapcore . Core ) zapcore . Core ) Option
//WrapCore 包装或替换 Logger 的底层 zapcore.Core。

func FunctionsExampaleWrapCore1() {
	//更换记录器的内核可以改变基本行为。
	//例如,它可以将记录器转换为no-op。
	nop := zap.WrapCore(func(zapcore.Core) zapcore.Core {
		return zapcore.NewNopCore()
	})
	logger := zap.NewExample()
	defer logger.Sync()
	logger.Info("working")
	logger.WithOptions(nop).Info("no-op")
	logger.Info("original logger still works")

//输出
//{"level":"info","msg":"working"}
//{"level":"info","msg":"original logger still works"}
}

func FunctionsExampaleWrapCore2() {
	//包装记录器的核心可以扩展其功能。作为一件小事
	//例如,它可以双重写入所有日志。
	doubled := zap.WrapCore(func(c zapcore.Core) zapcore.Core {
		return zapcore.NewTee(c, c)
	})
	logger := zap.NewExample()
	defer logger.Sync()
	logger.Info("single")
	logger.WithOptions(doubled).Info("doubled")

//输出
//{"level":"info","msg":"single"}
//{"level":"info","msg":"doubled"}
//{"level":"info","msg":"doubled"}
}

type SamplingConfig struct {
	Initial	int		`json:"initial" yaml:"initial"`
	Thereafter	int		`json:"thereafter" yaml:"thereafter"`
	Hook func( zapcore . Entry , zapcore . SamplingDecision ) `json:"-" yaml :"-"`
}
//SamplingConfig 为记录器设置采样策略。在尝试保留日志的代表性子集的同时,采样会限制日志记录对您的进程造成的全局 CPU 和 I/O 负载。
//如果指定,采样器将在每次决定后调用钩子。
//此处配置的值为每秒。有关详细信息,请参阅 zapcore.NewSamplerWithOptions。

type Sink interface {
	zapcore.WriteSyncer
	io.Closer
}
//Sink 定义了写入和关闭记录器目的地的接口。

type SugaredLogger struct {
	// 包含过滤或未导出的字段
}
//SugaredLogger 将基本的 Logger 功能包装在一个较慢但不那么冗长的 API 中。任何 Logger 都可以通过其 Sugar 方法转换为 SugaredLogger。
//与 Logger 不同,SugaredLogger 不坚持结构化日志记录。对于每个日志级别,它公开了三种方法:一种用于松散类型的结构化日志记录,
//一种用于 println 样式的格式,一种用于 printf 样式的格式。例如,SugaredLoggers 可以使用 Infow(“带有结构化上下文的信息”)、Info 或 Infof 生成 InfoLevel 输出。

func S() * SugaredLogger
//S 返回全局的 SugaredLogger,可以用 ReplaceGlobals 重新配置。并发使用是安全的。

func (s * SugaredLogger ) DPanic(args ...interface{})
//DPanic 使用 fmt.Sprint 来构造和记录消息。在开发过程中,记录器会出现恐慌。(有关详细信息,请参阅 DPanicLevel。)

func (s *SugaredLogger) DPanicf(template string, args ...interface{})
//DPanicf 使用 fmt.Sprintf 来记录模板化消息。在开发过程中,记录器会出现恐慌。(有关详细信息,请参阅 DPanicLevel。)

func (s * SugaredLogger ) DPanicw(msg string , keysAndValues ...interface{})
//DPanicw 记录带有一些附加上下文的消息。在开发过程中,记录器会出现恐慌。(有关详细信息,请参阅 DPanicLevel。)可变参数键值对被视为在 With 中。

func (s * SugaredLogger ) Debug(args ...interface{})
//调试使用 fmt.Sprint 来构造和记录消息。

func (s * SugaredLogger ) Debugf(template string , args ...interface{})
//Debugf 使用 fmt.Sprintf 来记录模板化消息。

func (s * SugaredLogger ) Debugw(msg string , keysAndValues ...interface{})
//Debugw 记录带有一些附加上下文的消息。可变键值对被视为在 With 中。
//当调试级日志被禁用时,对比:s.With(keysAndValues).Debug(msg)

func (s * SugaredLogger ) Desugar() *Logger
//Desugar 打开一个 SugaredLogger,暴露原来的 Logger。Desugaring 非常便宜,
//因此单个应用程序同时使用 Loggers 和 SugaredLoggers 是合理的,在性能敏感代码的边界上在它们之间进行转换。

func (s * SugaredLogger ) Error(args ...interface{})
//Error 使用 fmt.Sprint 构造和记录消息。

func (s * SugaredLogger ) Errorf(template string , args ...interface{})
//Errorf 使用 fmt.Sprintf 来记录模板化消息。

func (s * SugaredLogger ) Errorw(msg string , keysAndValues ...interface{})
//Errorw 记录带有一些附加上下文的消息。可变键值对被视为在 With 中。

func (s * SugaredLogger ) Fatal(args ...interface{})
//Fatal 使用 fmt.Sprint 构造并记录一条消息,然后调用 os.Exit。

func (s * SugaredLogger ) Fatalf(template string , args ...interface{})
//Fatalf 使用 fmt.Sprintf 记录模板化消息,然后调用 os.Exit。

func (s * SugaredLogger ) Fatalw(msg string , keysAndValues ...interface{})
//Fatalw 记录带有一些附加上下文的消息,然后调用 os.Exit。可变键值对被视为在 With 中。

func (s * SugaredLogger ) Info(args ...interface{})
//Info 使用 fmt.Sprint 来构造和记录消息。

func (s * SugaredLogger ) Infof(template string , args ...interface{})
//Infof 使用 fmt.Sprintf 来记录模板化消息。

func (s * SugaredLogger ) Infow(msg string , keysAndValues ...interface{})
//Infow 记录带有一些附加上下文的消息。可变键值对被视为在 With 中。

func (s * SugaredLogger ) Named(name string ) * SugaredLogger
//Named 为记录器的名称添加一个子范围。有关详细信息,请参阅 Logger.Named。

func FunctionsExampaleNamed() {
	logger := zap.NewExample()
	defer logger.Sync()
	// By default, Loggers are unnamed.
	logger.Info("no name")
	// The first call to Named sets the Logger name.
	main := logger.Named("main")
	main.Info("main logger")
	// Additional calls to Named create a period-separated path.
	main.Named("subpackage").Info("sub-logger")

//输出
//{"level":"info","msg":"no name"}
//{"level":"info","logger":"main","msg":"main logger"}
//{"level":"info","logger":"main.subpackage","msg":"sub-logger"}
}

func (s * SugaredLogger ) Panic(args ...interface{})
//Panic 使用 fmt.Sprint 构造并记录一条消息,然后发生恐慌。

func (s * SugaredLogger ) Panicf(template string , args ...interface{})
//Panicf 使用 fmt.Sprintf 记录模板化消息,然后发生恐慌。

func (s * SugaredLogger ) Panicw(msg string , keysAndValues ...interface{})
//Panicw 记录一条带有一些附加上下文的消息,然后发生恐慌。可变键值对被视为在 With 中。

func (s * SugaredLogger ) Sync() error
//同步刷新任何缓冲的日志条目。

func (s * SugaredLogger ) Warn(args ...interface{})
//Warn 使用 fmt.Sprint 来构造和记录一条消息。

func (s * SugaredLogger ) Warnf(template string , args ...interface{})
//Warnf 使用 fmt.Sprintf 来记录模板化消息。

func (s * SugaredLogger ) Warnw(msg string , keysAndValues ...interface{})
//Warnw 记录带有一些附加上下文的消息。可变键值对被视为在 With 中。

func (s * SugaredLogger ) With(args ...interface{}) * SugaredLogger
//With 向日志上下文添加可变数量的字段。它接受强类型 Field 对象和松散类型键值对的混合。处理对时,对的第一个元素用作字段键,第二个元素用作字段值。
//sugaredLogger.With(
//"hello", "world",
//"failure", errors.New("oh no"),
//Stack(),
//"count", 42,
//"user", User{Name: "alice"},
//)
//unsugared.With(
//String("hello", "world"),
//String("failure", "oh no"),
//Stack(),
//Int("count", 42),
//Object("user", User{Name: "alice"}),
//)
//请注意,键值对中的键应该是字符串。在开发中,传递非字符串键会导致恐慌。在生产中,记录器更宽容:记录一个单独的错误,
//但跳过键值对并继续执行。传递孤立的密钥会触发类似的行为:开发中的恐慌和生产中的错误。
//


猜你喜欢

转载自blog.csdn.net/liao__ran/article/details/120436300