本文主要研究一下golang的zap的NewDevelopmentgit
zap@v1.16.0/logger.gogithub
func NewDevelopment(options ...Option) (*Logger, error) { return NewDevelopmentConfig().Build(options...) }
NewDevelopment使用NewDevelopmentConfig進行build
zap@v1.16.0/config.gogolang
func NewDevelopmentConfig() Config { return Config{ Level: NewAtomicLevelAt(DebugLevel), Development: true, Encoding: "console", EncoderConfig: NewDevelopmentEncoderConfig(), OutputPaths: []string{"stderr"}, ErrorOutputPaths: []string{"stderr"}, } }
NewDevelopmentConfig建立Config,其Level爲NewAtomicLevelAt(DebugLevel),Development爲true,Encoding爲console,EncoderConfig爲NewDevelopmentEncoderConfig,OutputPaths及ErrorOutputPaths均爲stderr
zap@v1.16.0/config.gojson
func NewDevelopmentEncoderConfig() zapcore.EncoderConfig { return zapcore.EncoderConfig{ // Keys can be anything except the empty string. TimeKey: "T", LevelKey: "L", NameKey: "N", CallerKey: "C", FunctionKey: zapcore.OmitKey, MessageKey: "M", StacktraceKey: "S", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.CapitalLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.StringDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, } }
NewDevelopmentEncoderConfig建立zapcore.EncoderConfig,其LineEnding爲zapcore.DefaultLineEnding,EncodeLevel爲zapcore.CapitalLevelEncoder,EncodeTime爲zapcore.ISO8601TimeEncoder,EncodeDuration爲zapcore.StringDurationEncoder,EncodeCaller爲zapcore.ShortCallerEncoder
zap@v1.16.0/zapcore/encoder.goapi
const DefaultLineEnding = "\n" func CapitalLevelEncoder(l Level, enc PrimitiveArrayEncoder) { enc.AppendString(l.CapitalString()) } func ISO8601TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { encodeTimeLayout(t, "2006-01-02T15:04:05.000Z0700", enc) } func StringDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) { enc.AppendString(d.String()) } func ShortCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) { // TODO: consider using a byte-oriented API to save an allocation. enc.AppendString(caller.TrimmedPath()) }
zap@v1.16.0/encoder.goide
var ( errNoEncoderNameSpecified = errors.New("no encoder name specified") _encoderNameToConstructor = map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error){ "console": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) { return zapcore.NewConsoleEncoder(encoderConfig), nil }, "json": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) { return zapcore.NewJSONEncoder(encoderConfig), nil }, } _encoderMutex sync.RWMutex )
_encoderNameToConstructor內置了console、json兩種encoder
zap@v1.16.0/console_encoder.goui
func NewConsoleEncoder(cfg EncoderConfig) Encoder { if len(cfg.ConsoleSeparator) == 0 { // Use a default delimiter of '\t' for backwards compatibility cfg.ConsoleSeparator = "\t" } return consoleEncoder{newJSONEncoder(cfg, true)} } type consoleEncoder struct { *jsonEncoder } func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) { line := bufferpool.Get() // We don't want the entry's metadata to be quoted and escaped (if it's // encoded as strings), which means that we can't use the JSON encoder. The // simplest option is to use the memory encoder and fmt.Fprint. // // If this ever becomes a performance bottleneck, we can implement // ArrayEncoder for our plain-text format. arr := getSliceEncoder() if c.TimeKey != "" && c.EncodeTime != nil { c.EncodeTime(ent.Time, arr) } if c.LevelKey != "" && c.EncodeLevel != nil { c.EncodeLevel(ent.Level, arr) } if ent.LoggerName != "" && c.NameKey != "" { nameEncoder := c.EncodeName if nameEncoder == nil { // Fall back to FullNameEncoder for backward compatibility. nameEncoder = FullNameEncoder } nameEncoder(ent.LoggerName, arr) } if ent.Caller.Defined { if c.CallerKey != "" && c.EncodeCaller != nil { c.EncodeCaller(ent.Caller, arr) } if c.FunctionKey != "" { arr.AppendString(ent.Caller.Function) } } for i := range arr.elems { if i > 0 { line.AppendString(c.ConsoleSeparator) } fmt.Fprint(line, arr.elems[i]) } putSliceEncoder(arr) // Add the message itself. if c.MessageKey != "" { c.addSeparatorIfNecessary(line) line.AppendString(ent.Message) } // Add any structured context. c.writeContext(line, fields) // If there's no stacktrace key, honor that; this allows users to force // single-line output. if ent.Stack != "" && c.StacktraceKey != "" { line.AppendByte('\n') line.AppendString(ent.Stack) } if c.LineEnding != "" { line.AppendString(c.LineEnding) } else { line.AppendString(DefaultLineEnding) } return line, nil }
consoleEncoder內嵌了
*jsonEncoder
,其EncodeEntry方法經過getSliceEncoder()獲取`*sliceArrayEncoder,而後依次往arr添加time、level、loggerName、caller,最後再添加業務的message自己,對於有stacktrace還會追加stacktrace
func developmentDemo() { logger, _ := zap.NewDevelopment() defer logger.Sync() // flushes buffer, if any sugar := logger.Sugar() sugar.Info("this will be logged") sugar.Panic("test panic") }
輸出this
2020-12-06T23:29:08.081+0800 INFO log-demo/zap_demo.go:17 this will be logged 2020-12-06T23:29:08.082+0800 PANIC log-demo/zap_demo.go:18 test panic main.developmentDemo /zap_demo.go:18 main.main /zap_demo.go:10 runtime.main /usr/local/go/src/runtime/proc.go:204 panic: test panic goroutine 1 [running]: go.uber.org/zap/zapcore.(*CheckedEntry).Write(0xc0000f20c0, 0x0, 0x0, 0x0) /go/pkg/mod/go.uber.org/zap@v1.16.0/zapcore/entry.go:234 +0x585 go.uber.org/zap.(*SugaredLogger).log(0xc0000fbed0, 0x4, 0x0, 0x0, 0xc0000fbed8, 0x1, 0x1, 0x0, 0x0, 0x0) /go/pkg/mod/go.uber.org/zap@v1.16.0/sugar.go:234 +0xf6 go.uber.org/zap.(*SugaredLogger).Panic(...) /go/pkg/mod/go.uber.org/zap@v1.16.0/sugar.go:123 main.developmentDemo() /zap_demo.go:18 +0x199 main.main() /zap_demo.go:10 +0x25 exit status 2
NewDevelopmentEncoderConfig建立zapcore.EncoderConfig,其LineEnding爲zapcore.DefaultLineEnding,EncodeLevel爲zapcore.CapitalLevelEncoder,EncodeTime爲zapcore.ISO8601TimeEncoder,EncodeDuration爲zapcore.StringDurationEncoder,EncodeCaller爲zapcore.ShortCallerEncodercode