10

聊聊golang的zap的NewDevelopment

 3 years ago
source link: https://segmentfault.com/a/1190000038393987
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文主要研究一下golang的zap的NewDevelopment

NewDevelopment

[email protected]/logger.go

func NewDevelopment(options ...Option) (*Logger, error) {
    return NewDevelopmentConfig().Build(options...)
}

NewDevelopment使用NewDevelopmentConfig进行build

NewDevelopmentConfig

[email protected]/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

[email protected]/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

[email protected]/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

[email protected]/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

[email protected]/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/[email protected]/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/[email protected]/sugar.go:234 +0xf6
go.uber.org/zap.(*SugaredLogger).Panic(...)
        /go/pkg/mod/go.uber.org/[email protected]/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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK