序
本文主要研究一下golang的zap的NewDevelopment
NewDevelopment
zap@v1.16.0/logger.go
func NewDevelopment(options ...Option) (*Logger, error) {
return NewDevelopmentConfig().Build(options...)
}
NewDevelopment使用NewDevelopmentConfig进行build
NewDevelopmentConfig
zap@v1.16.0/config.go
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
NewDevelopmentEncoderConfig
zap@v1.16.0/config.go
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
encoder
zap@v1.16.0/zapcore/encoder.go
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())
}
encoder
zap@v1.16.0/encoder.go
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
NewConsoleEncoder
zap@v1.16.0/console_encoder.go
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")
}
输出
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.ShortCallerEncoder
doc
有疑问加站长微信联系(非本文作者)