zap——从Config自定义日志格式

前言

我们依然从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开发相关内容,望大家感兴趣的支持一下,在此特别感谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值