文主要研究一下uber的zap的NewProduction。
golang的log有zap、logrus等,不过logrus现在已经处于维护状态。
func main() { logger, _ := zap.NewProduction() defer logger.Sync() // flushes buffer, if any sugar := logger.Sugar() url := "https://abc.com" sugar.Infow("failed to fetch URL", // Structured context as loosely typed key-value pairs. "url", url, "attempt", 3, "backoff", time.Second, ) }
SugaredLogger比普通的结构化logging包快4-10倍
输出
{"level":"info","ts":1607095287.638658,"caller":"log-demo/zap_demo.go:14","msg":"failed to fetch URL","url":"https://abc.com","attempt":3,"backoff":1}
func main() { logger, _ := zap.NewProduction() defer logger.Sync() url := "https://abc.com" 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), ) }
当对性能和类型安全都要求比较高的场景就使用Logger,它比SugaredLogger速度更快,但是它只支持结构化logging
输出
{"level":"info","ts":1607095675.5623732,"caller":"log-demo/zap_demo.go:25","msg":"failed to fetch URL","url":"https://abc.com","attempt":3,"backoff":1}
zap@v1.16.0/logger.go
func NewProduction(options ...Option) (*Logger, error) { return NewProductionConfig().Build(options...) }
NewProduction内部执行NewProductionConfig().Build方法
zap@v1.16.0/config.go
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"}, } }
NewProductionConfig方法按默认参数创建了Config,其中encoding为json,output为std
zap@v1.16.0/config.go
func NewProductionEncoderConfig() zapcore.EncoderConfig { return zapcore.EncoderConfig{ TimeKey: "ts", LevelKey: "level", NameKey: "logger", CallerKey: "caller", FunctionKey: zapcore.OmitKey, MessageKey: "msg", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.EpochTimeEncoder, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, } }
NewProductionEncoderConfig这里创建了zapcore.EncoderConfig
zap@v1.16.0/config.go
func (cfg Config) Build(opts ...Option) (*Logger, error) { enc, err := cfg.buildEncoder() if err != nil { return nil, err } sink, errSink, err := cfg.openSinks() if err != nil { return nil, err } if cfg.Level == (AtomicLevel{}) { return nil, fmt.Errorf("missing Level") } log := New( zapcore.NewCore(enc, sink, cfg.Level), cfg.buildOptions(errSink)..., ) if len(opts) > 0 { log = log.WithOptions(opts...) } return log, nil }
Build方法执行cfg.buildEncoder()、cfg.openSinks()、zapcore.NewCore及New方法
zap@v1.16.0/logger.go
func New(core zapcore.Core, options ...Option) *Logger { if core == nil { return NewNop() } log := &Logger{ core: core, errorOutput: zapcore.Lock(os.Stderr), addStack: zapcore.FatalLevel + 1, } return log.WithOptions(options...) } func NewNop() *Logger { return &Logger{ core: zapcore.NewNopCore(), errorOutput: zapcore.AddSync(ioutil.Discard), addStack: zapcore.FatalLevel + 1, } }
这里对于core为nil的返回的是NewNop,否则实例化Logger;NewNop返回的logger的core为zapcore.NewNopCore()
zap@v1.16.0/logger.go
type Logger struct { core zapcore.Core development bool name string errorOutput zapcore.WriteSyncer addCaller bool addStack zapcore.LevelEnabler callerSkip int onFatal zapcore.CheckWriteAction // default is WriteThenFatal }
Logger定义了core、development、name、errorOutput、addCaller、addStack、callerSkip、onFatal属性
zap.NewProduction()通过创建NewProductionEncoderConfig再Build出Logger,其中Logger的New方法主要设置了core、errorOutput、addStack属性