本文次要钻研一下golang的zap的Core

Core

zap@v1.16.0/zapcore/core.go

type Core interface {    LevelEnabler    // With adds structured context to the Core.    With([]Field) Core    // Check determines whether the supplied Entry should be logged (using the    // embedded LevelEnabler and possibly some extra logic). If the entry    // should be logged, the Core adds itself to the CheckedEntry and returns    // the result.    //    // Callers must use Check before calling Write.    Check(Entry, *CheckedEntry) *CheckedEntry    // Write serializes the Entry and any Fields supplied at the log site and    // writes them to their destination.    //    // If called, Write should always log the Entry and Fields; it should not    // replicate the logic of Check.    Write(Entry, []Field) error    // Sync flushes buffered logs (if any).    Sync() error}
Core接口内嵌了LevelEnabler,定义了With、Check、Write、Sync办法

LevelEnabler

zap@v1.16.0/zapcore/level.go

type LevelEnabler interface {    Enabled(Level) bool}
LevelEnabler定义了Enabled办法

nopCore

zap@v1.16.0/zapcore/core.go

type nopCore struct{}// NewNopCore returns a no-op Core.func NewNopCore() Core                                        { return nopCore{} }func (nopCore) Enabled(Level) bool                            { return false }func (n nopCore) With([]Field) Core                           { return n }func (nopCore) Check(_ Entry, ce *CheckedEntry) *CheckedEntry { return ce }func (nopCore) Write(Entry, []Field) error                    { return nil }func (nopCore) Sync() error                                   { return nil }
nopCore实现了Core接口,为空操作

ioCore

zap@v1.16.0/zapcore/core.go

type ioCore struct {    LevelEnabler    enc Encoder    out WriteSyncer}func (c *ioCore) With(fields []Field) Core {    clone := c.clone()    addFields(clone.enc, fields)    return clone}func (c *ioCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {    if c.Enabled(ent.Level) {        return ce.AddCore(ent, c)    }    return ce}func (c *ioCore) Write(ent Entry, fields []Field) error {    buf, err := c.enc.EncodeEntry(ent, fields)    if err != nil {        return err    }    _, err = c.out.Write(buf.Bytes())    buf.Free()    if err != nil {        return err    }    if ent.Level > ErrorLevel {        // Since we may be crashing the program, sync the output. Ignore Sync        // errors, pending a clean solution to issue #370.        c.Sync()    }    return nil}func (c *ioCore) Sync() error {    return c.out.Sync()}func (c *ioCore) clone() *ioCore {    return &ioCore{        LevelEnabler: c.LevelEnabler,        enc:          c.enc.Clone(),        out:          c.out,    }}
ioCore内嵌了LevelEnabler,定义了Encoder、WriteSyncer(out)属性,实现了Core接口;其With办法执行的是clone以及addFields;Check办法先判断c.Enabled(ent.Level),若为true才执行ce.AddCore(ent, c);Write办法先通过encoder的EncodeEntry序列化entry,而后将bytes写入到WriteSyncer,若entry的level大于ErrorLevel的还会执行Sync办法;Sync办法执行out.Sync;clone办法则用原来core来从新一个新的ioCore

addFields

zap@v1.16.0/zapcore/field.go

func addFields(enc ObjectEncoder, fields []Field) {    for i := range fields {        fields[i].AddTo(enc)    }}func (f Field) AddTo(enc ObjectEncoder) {    var err error    switch f.Type {    case ArrayMarshalerType:        err = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler))    case ObjectMarshalerType:        err = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler))    case BinaryType:        enc.AddBinary(f.Key, f.Interface.([]byte))    case BoolType:        enc.AddBool(f.Key, f.Integer == 1)    case ByteStringType:        enc.AddByteString(f.Key, f.Interface.([]byte))    case Complex128Type:        enc.AddComplex128(f.Key, f.Interface.(complex128))    case Complex64Type:        enc.AddComplex64(f.Key, f.Interface.(complex64))    case DurationType:        enc.AddDuration(f.Key, time.Duration(f.Integer))    case Float64Type:        enc.AddFloat64(f.Key, math.Float64frombits(uint64(f.Integer)))    case Float32Type:        enc.AddFloat32(f.Key, math.Float32frombits(uint32(f.Integer)))    case Int64Type:        enc.AddInt64(f.Key, f.Integer)    case Int32Type:        enc.AddInt32(f.Key, int32(f.Integer))    case Int16Type:        enc.AddInt16(f.Key, int16(f.Integer))    case Int8Type:        enc.AddInt8(f.Key, int8(f.Integer))    case StringType:        enc.AddString(f.Key, f.String)    case TimeType:        if f.Interface != nil {            enc.AddTime(f.Key, time.Unix(0, f.Integer).In(f.Interface.(*time.Location)))        } else {            // Fall back to UTC if location is nil.            enc.AddTime(f.Key, time.Unix(0, f.Integer))        }    case TimeFullType:        enc.AddTime(f.Key, f.Interface.(time.Time))    case Uint64Type:        enc.AddUint64(f.Key, uint64(f.Integer))    case Uint32Type:        enc.AddUint32(f.Key, uint32(f.Integer))    case Uint16Type:        enc.AddUint16(f.Key, uint16(f.Integer))    case Uint8Type:        enc.AddUint8(f.Key, uint8(f.Integer))    case UintptrType:        enc.AddUintptr(f.Key, uintptr(f.Integer))    case ReflectType:        err = enc.AddReflected(f.Key, f.Interface)    case NamespaceType:        enc.OpenNamespace(f.Key)    case StringerType:        err = encodeStringer(f.Key, f.Interface, enc)    case ErrorType:        encodeError(f.Key, f.Interface.(error), enc)    case SkipType:        break    default:        panic(fmt.Sprintf("unknown field type: %v", f))    }    if err != nil {        enc.AddString(fmt.Sprintf("%sError", f.Key), err.Error())    }}
addFields办法遍历fields,执行Field的AddTo办法;AddTo办法会依据f.Type来执行ObjectEncoder的对应办法

实例

func newCoreDemo() {    temp, err := ioutil.TempFile("", "zapcore-test-iocore")    if err != nil {        panic(err)    }    fmt.Println(temp.Name())    //defer os.Remove(temp.Name())    cfg := zapcore.EncoderConfig{        MessageKey:     "msg",        LevelKey:       "level",        NameKey:        "name",        TimeKey:        "ts",        CallerKey:      "caller",        FunctionKey:    "func",        StacktraceKey:  "stacktrace",        LineEnding:     "\n",        EncodeTime:     zapcore.EpochTimeEncoder,        EncodeLevel:    zapcore.LowercaseLevelEncoder,        EncodeDuration: zapcore.SecondsDurationEncoder,        EncodeCaller:   zapcore.ShortCallerEncoder,    }    cfg.TimeKey = ""    core := zapcore.NewCore(        zapcore.NewJSONEncoder(cfg),        temp,        zapcore.InfoLevel,    ).With([]zapcore.Field{zapcore.Field{Type: zapcore.Int64Type, Integer: int64(1), Key: "k"}})    if ce := core.Check(zapcore.Entry{Level: zapcore.DebugLevel, Message: "debug"}, nil); ce != nil {        ce.Write(zapcore.Field{Type: zapcore.Int64Type, Integer: int64(2), Key: "k"})    }    if ce := core.Check(zapcore.Entry{Level: zapcore.InfoLevel, Message: "info"}, nil); ce != nil {        ce.Write(zapcore.Field{Type: zapcore.Int64Type, Integer: int64(3), Key: "k"})    }    if ce := core.Check(zapcore.Entry{Level: zapcore.WarnLevel, Message: "warn"}, nil); ce != nil {        ce.Write(zapcore.Field{Type: zapcore.Int64Type, Integer: int64(4), Key: "k"})    }}
这里NewJSONEncoder应用指定的zapcore.EncoderConfig,zapcore.NewCore的out设置为temp文件,level级别为InfoLevel,因此只会输入level及以上的entry

输入

{"level":"info","msg":"info","k":1,"k":3}{"level":"warn","msg":"warn","k":1,"k":4}
其中k为1的是withField全局指定的,而后k为2因为是debug级别所以没有输入进去

小结

Core接口内嵌了LevelEnabler,定义了With、Check、Write、Sync办法;它有nopCore及ioCore两种实现,ioCore内嵌了LevelEnabler,定义了Encoder、WriteSyncer(out)属性,其中encoder用来序列化entry为bytes,而WriteSyncer则用于写入bytes。

doc

  • zap