共计 5800 个字符,预计需要花费 15 分钟才能阅读完成。
序
本文次要钻研一下 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