前言
我们依然从zap的官方示例开始:
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("failed to fetch URL",
// Structured context as strongly typed Field values.
zap.String("url", url),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
zap在使用时,需要先构造一个logger,然后才能处理具体的日志内容。
示例中NewProduction的代码如下:
func NewProduction(options ...Option) (*Logger, error) {
return NewProductionConfig().Build(options...)
}
func NewProductionConfig() Config {
return Config{
Level: NewAtomicLevelAt(InfoLevel),
Development: false,
Sampling: &SamplingConfig{
Initial: 100,
Thereafter: 100,
},
Encoding: "json",
EncoderConfig: NewProductionEncoderConfig(),
OutputPaths: []string{"stderr"},
ErrorOutputPaths: []string{"stderr"},
}
}
从代码中可以看出NewProduction实际就是构造了一个Config,然后通过Config创建Logger。Config中设定了log的格式等信息,最后由logger负责格式化具体的日志信息。
Config
从Config的注释来看:
Config提供了一种声明性的方式来构造Logger,通过调用New、Options和各种zapcore.WriteSyncer和zapcore.Core的封装来完成Logger的创建,主要是简化了调用者Logger的创建过程。注意:Config仅提供了通用的一些配置项,一些不常用的配置项,仍需要直接使用对应的封装来实现配置的变更。
// Config offers a declarative way to construct a logger. It doesn't do
// anything that can't be done with New, Options, and the various
// zapcore.WriteSyncer and zapcore.Core wrappers, but it's a simpler way to
// toggle common options.
//
// Note that Config intentionally supports only the most common options. More
// unusual logging setups (logging to network connections or message queues,
// splitting output between multiple files, etc.) are possible, but require
// direct use of the zapcore package. For sample code, see the package-level
// BasicConfiguration and AdvancedConfiguration examples.
//
// For an example showing runtime log level changes, see the documentation for
// AtomicLevel.
type Config struct {
// Level is the minimum enabled logging level. Note that this is a dynamic
// level, so calling Config.Level.SetLevel will atomically change the log
// level of all loggers descended from this config.
// log级别,可以通过Config.Level.SetLevel动态设置
Level AtomicLevel `json:"level" yaml:"level"`
// Development puts the logger in development mode, which changes the
// behavior of DPanicLevel and takes stacktraces more liberally.
//是否开发模式,开发模式可以追逐更多的栈信息
Development bool `json:"development" yaml:"development"`
// DisableCaller stops annotating logs with the calling function's file
// name and line number. By default, all logs are annotated.
// 是否关闭注释性logs,默认所有logs都是注释性的。
DisableCaller bool `json:"disableCaller" yaml:"disableCaller"`
// DisableStacktrace completely disables automatic stacktrace capturing. By
// default, stacktraces are captured for WarnLevel and above logs in
// development and ErrorLevel and above in production.
// 是否关闭栈追踪
DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"`
// Sampling sets a sampling policy. A nil SamplingConfig disables sampling.
// 采样设置,记录全局的CPU、IO负载
Sampling *SamplingConfig `json:"sampling" yaml:"sampling"`
// Encoding sets the logger's encoding. Valid values are "json" and
// "console", as well as any third-party encodings registered via
// RegisterEncoder.
// 支持编码json或者console
Encoding string `json:"encoding" yaml:"encoding"`
// EncoderConfig sets options for the chosen encoder. See
// zapcore.EncoderConfig for details.
// 具体编码器配置
EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"`
// OutputPaths is a list of URLs or file paths to write logging output to.
// See Open for details.
// 输出路径
OutputPaths []string `json:"outputPaths" yaml:"outputPaths"`
// ErrorOutputPaths is a list of URLs to write internal logger errors to.
// The default is standard error.
//
// Note that this setting only affects internal errors; for sample code that
// sends error-level logs to a different location from info- and debug-level
// logs, see the package-level AdvancedConfiguration example.
// 错误输出路径,仅用于内部错误
ErrorOutputPaths []string `json:"errorOutputPaths" yaml:"errorOutputPaths"`
// InitialFields is a collection of fields to add to the root logger.
// 可以额外添加的参数
InitialFields map[string]interface{} `json:"initialFields" yaml:"initialFields"`
}
EncoderConfig是针对Encoding的具体配置项,主要包含如制定特定key的名称,时间、日期的格式等,指定后这些信息会出现在log中。
注意:如果不指定对应key的name的话,对应key的信息不处理,即不会写入到文件中,如MessageKey为空的话,内容主体不处理,即看不到log内容。
// An EncoderConfig allows users to configure the concrete encoders supplied by
// zapcore.
type EncoderConfig struct {
// Set the keys used for each log entry. If any key is empty, that portion
// of the entry is omitted.
MessageKey string `json:"messageKey" yaml:"messageKey"`
LevelKey string `json:"levelKey" yaml:"levelKey"`
TimeKey string `json:"timeKey" yaml:"timeKey"`
NameKey string `json:"nameKey" yaml:"nameKey"`
CallerKey string `json:"callerKey" yaml:"callerKey"`
StacktraceKey string `json:"stacktraceKey" yaml:"stacktraceKey"`
LineEnding string `json:"lineEnding" yaml:"lineEnding"`
// Configure the primitive representations of common complex types. For
// example, some users may want all time.Times serialized as floating-point
// seconds since epoch, while others may prefer ISO8601 strings.
EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"`
EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"`
EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"`
EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"`
// Unlike the other primitive type encoders, EncodeName is optional. The
// zero value falls back to FullNameEncoder.
EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"`
}
根据以上的了解,我们回头看看NewProductionConfig。
func NewProductionConfig() Config {
return Config{
Level: NewAtomicLevelAt(InfoLevel),//日志级别INFO
Development: false,
Sampling: &SamplingConfig{//采样设置
Initial: 100,
Thereafter: 100,
},
Encoding: "json",//采用json格式
EncoderConfig: NewProductionEncoderConfig(),
OutputPaths: []string{"stderr"},//输出到标准错误
ErrorOutputPaths: []string{"stderr"},
}
}
func NewProductionEncoderConfig() zapcore.EncoderConfig {
return zapcore.EncoderConfig{
TimeKey: "ts",//时间对应的key名
LevelKey: "level",//日志级别对应的key名
NameKey: "logger",//logger名对应的key名
CallerKey: "caller",//时间对应的key名
MessageKey: "msg",//日志内容对应的key名,此参数必须不为空,否则日志主体不处理
StacktraceKey: "stacktrace",//栈追踪的key名
LineEnding: zapcore.DefaultLineEnding,//默认换行,即使不设置
EncodeLevel: zapcore.LowercaseLevelEncoder,//小写
EncodeTime: zapcore.EpochTimeEncoder,//时间转为s
EncodeDuration: zapcore.SecondsDurationEncoder,//日期转为s
EncodeCaller: zapcore.ShortCallerEncoder,//记录调用路径格式为package/file:line
}
}
NewProductionConfig创建的是一个这样的Config实例:
日志级别InfoLevel,非开发环境,采样间隔100s,编码格式为json,输出及错误输出路径均为stderr。EncoderConfig中带key的设置均为指定对应参数在信息中key名称即自定义key名,这些值未设置或为空时不显示在log中。
示例结果:
{"level":"info","ts":1577697584.997612,"caller":"test/test.go:14","msg":"failed to fetch URL","url":"http://test","attempt":3,"backoff":1}
根据以上对Config的了解,我们根据自己的需求,自定义一个config
自定义Config
config: =zap.Config{
Level: zap.NewAtomicLevelAt(zap.DebugLevel),
Development: true,
Encoding: "console",
EncoderConfig: zapcore.EncoderConfig{
MessageKey: "msg",
},
OutputPaths: []string{"stdout", "./log.txt"},
ErrorOutputPaths: []string{"stderr"},
}
l,_ := Config.Build
l.Info("this is a test config")
以上自定义Config表示意思如下:
DebugLevel及以上级别日志输出,Development模式,采用console模式编码及原始格式,仅展示日志内容,日志会显示在标准输出中及保存在log.txt文件中,内部发生错误显示在标准错误中。注意:console模式不会打印key名的,仅打印对应的value。
输出示例:
this is a test config
需要注意的是:zap中目前并不能自定义这些通用key出现的先后顺序,如对顺序有要求的话,建议自行对结果进行处理。
总结
本篇主要通过对Config的参数进行调整,实现自定义的log格式。不过需要注意的是,zap对log的格式及key均有一定的限制,在某些自由化使用支持度并不好。
公众号
鄙人刚刚开通了公众号,专注于分享Go开发相关内容,望大家感兴趣的支持一下,在此特别感谢。