共计 6203 个字符,预计需要花费 16 分钟才能阅读完成。
序
本文次要钻研一下 golang 的 zap 的 encoder
encoder
zap@v1.16.0/zapcore/encoder.go
type Encoder interface { | |
ObjectEncoder | |
// Clone copies the encoder, ensuring that adding fields to the copy doesn't | |
// affect the original. | |
Clone() Encoder | |
// EncodeEntry encodes an entry and fields, along with any accumulated | |
// context, into a byte buffer and returns it. Any fields that are empty, | |
// including fields on the `Entry` type, should be omitted. | |
EncodeEntry(Entry, []Field) (*buffer.Buffer, error) | |
} |
Encoder 接口内嵌了 ObjectEncoder,定义了 Clone、EncodeEntry 办法
ObjectEncoder
zap@v1.16.0/zapcore/encoder.go
type ObjectEncoder interface { | |
// Logging-specific marshalers. | |
AddArray(key string, marshaler ArrayMarshaler) error | |
AddObject(key string, marshaler ObjectMarshaler) error | |
// Built-in types. | |
AddBinary(key string, value []byte) // for arbitrary bytes | |
AddByteString(key string, value []byte) // for UTF-8 encoded bytes | |
AddBool(key string, value bool) | |
AddComplex128(key string, value complex128) | |
AddComplex64(key string, value complex64) | |
AddDuration(key string, value time.Duration) | |
AddFloat64(key string, value float64) | |
AddFloat32(key string, value float32) | |
AddInt(key string, value int) | |
AddInt64(key string, value int64) | |
AddInt32(key string, value int32) | |
AddInt16(key string, value int16) | |
AddInt8(key string, value int8) | |
AddString(key, value string) | |
AddTime(key string, value time.Time) | |
AddUint(key string, value uint) | |
AddUint64(key string, value uint64) | |
AddUint32(key string, value uint32) | |
AddUint16(key string, value uint16) | |
AddUint8(key string, value uint8) | |
AddUintptr(key string, value uintptr) | |
// AddReflected uses reflection to serialize arbitrary objects, so it can be | |
// slow and allocation-heavy. | |
AddReflected(key string, value interface{}) error | |
// OpenNamespace opens an isolated namespace where all subsequent fields will | |
// be added. Applications can use namespaces to prevent key collisions when | |
// injecting loggers into sub-components or third-party libraries. | |
OpenNamespace(key string) | |
} |
ObjectEncoder 接口定义了各种类型的一系列 Add 办法
MapObjectEncoder
zap@v1.16.0/zapcore/memory_encoder.go
type MapObjectEncoder struct { | |
// Fields contains the entire encoded log context. | |
Fields map[string]interface{} | |
// cur is a pointer to the namespace we're currently writing to. | |
cur map[string]interface{}} |
MapObjectEncoder 实现了 ObjectEncoder 接口,外部应用 map[string]interface{} 来存放数据
jsonEncoder
zap@v1.16.0/zapcore/json_encoder.go
type jsonEncoder struct { | |
*EncoderConfig | |
buf *buffer.Buffer | |
spaced bool // include spaces after colons and commas | |
openNamespaces int | |
// for encoding generic values by reflection | |
reflectBuf *buffer.Buffer | |
reflectEnc *json.Encoder | |
} |
jsonEncoder 内嵌了 EncoderConfig,定义了 buf、spaced、openNamespaces、reflectBuf、reflectEnc 属性
Clone
zap@v1.16.0/zapcore/json_encoder.go
func (enc *jsonEncoder) Clone() Encoder {clone := enc.clone() | |
clone.buf.Write(enc.buf.Bytes()) | |
return clone | |
} | |
func (enc *jsonEncoder) clone() *jsonEncoder {clone := getJSONEncoder() | |
clone.EncoderConfig = enc.EncoderConfig | |
clone.spaced = enc.spaced | |
clone.openNamespaces = enc.openNamespaces | |
clone.buf = bufferpool.Get() | |
return clone | |
} |
Clone 办法执行外部的 clone,而后将原 enc 的 buf 拷贝到 clone 的 encoder
EncodeEntry
zap@v1.16.0/zapcore/json_encoder.go
func (enc *jsonEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) {final := enc.clone() | |
final.buf.AppendByte('{') | |
if final.LevelKey != "" {final.addKey(final.LevelKey) | |
cur := final.buf.Len() | |
final.EncodeLevel(ent.Level, final) | |
if cur == final.buf.Len() { | |
// User-supplied EncodeLevel was a no-op. Fall back to strings to keep | |
// output JSON valid. | |
final.AppendString(ent.Level.String()) | |
} | |
} | |
if final.TimeKey != "" {final.AddTime(final.TimeKey, ent.Time) | |
} | |
if ent.LoggerName != ""&& final.NameKey !="" {final.addKey(final.NameKey) | |
cur := final.buf.Len() | |
nameEncoder := final.EncodeName | |
// if no name encoder provided, fall back to FullNameEncoder for backwards | |
// compatibility | |
if nameEncoder == nil {nameEncoder = FullNameEncoder} | |
nameEncoder(ent.LoggerName, final) | |
if cur == final.buf.Len() { | |
// User-supplied EncodeName was a no-op. Fall back to strings to | |
// keep output JSON valid. | |
final.AppendString(ent.LoggerName) | |
} | |
} | |
if ent.Caller.Defined { | |
if final.CallerKey != "" {final.addKey(final.CallerKey) | |
cur := final.buf.Len() | |
final.EncodeCaller(ent.Caller, final) | |
if cur == final.buf.Len() { | |
// User-supplied EncodeCaller was a no-op. Fall back to strings to | |
// keep output JSON valid. | |
final.AppendString(ent.Caller.String()) | |
} | |
} | |
if final.FunctionKey != "" {final.addKey(final.FunctionKey) | |
final.AppendString(ent.Caller.Function) | |
} | |
} | |
if final.MessageKey != "" {final.addKey(enc.MessageKey) | |
final.AppendString(ent.Message) | |
} | |
if enc.buf.Len() > 0 {final.addElementSeparator() | |
final.buf.Write(enc.buf.Bytes()) | |
} | |
addFields(final, fields) | |
final.closeOpenNamespaces() | |
if ent.Stack != ""&& final.StacktraceKey !="" {final.AddString(final.StacktraceKey, ent.Stack) | |
} | |
final.buf.AppendByte('}') | |
if final.LineEnding != "" {final.buf.AppendString(final.LineEnding) | |
} else {final.buf.AppendString(DefaultLineEnding) | |
} | |
ret := final.buf | |
putJSONEncoder(final) | |
return ret, nil | |
} |
EncodeEntry 办法先执行 clone 拷贝一份 encoder,而后在拷贝进去的 encoder 上进行各种 addKey 及 AppendString 操作,最初拼接完通过 putJSONEncoder 将该 encoder 偿还到_jsonPool
实例
func encoderDemo() {zap.RegisterEncoder("my-encoder", constructMyEncoder) | |
// buf := &bytes.Buffer{} | |
cfg := zap.NewDevelopmentConfig() | |
cfg.Encoding = "my-encoder" | |
logger, err := cfg.Build() | |
defer logger.Sync() | |
if err != nil {panic(err) | |
} | |
logger.Info("hello") | |
logger.Info("failed to fetch URL", | |
// Structured context as strongly typed Field values. | |
zap.String("url", "https://example.com"), | |
zap.Int("attempt", 3), | |
zap.Duration("backoff", time.Second), | |
) | |
} | |
type MyEncoder struct {*zapcore.MapObjectEncoder} | |
var (_pool = buffer.NewPool() | |
// Get retrieves a buffer from the pool, creating one if necessary. | |
Get = _pool.Get | |
) | |
func constructMyEncoder(config zapcore.EncoderConfig) (zapcore.Encoder, error) { | |
return MyEncoder{MapObjectEncoder: zapcore.NewMapObjectEncoder(), | |
}, nil | |
} | |
func (enc MyEncoder) Clone() zapcore.Encoder { | |
return MyEncoder{MapObjectEncoder: zapcore.NewMapObjectEncoder(), | |
} | |
} | |
func (enc MyEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {myEnc := enc.Clone().(MyEncoder) | |
buf := _pool.Get() | |
buf.AppendString(entry.Message) | |
buf.AppendString(" ") | |
for _, field := range fields {field.AddTo(myEnc) | |
value := myEnc.MapObjectEncoder.Fields[field.Key] | |
buf.AppendString(field.Key) | |
buf.AppendString("=") | |
if value == "" {buf.AppendString("''") | |
} else {buf.AppendString(fmt.Sprintf("%v", value)) | |
} | |
} | |
buf.AppendByte('\n') | |
if entry.Stack != "" {buf.AppendString(entry.Stack) | |
buf.AppendByte('\n') | |
} | |
return buf, nil | |
} |
这里定义了 MyEncoder 实现了 Encoder 接口,而后通过 zap.RegisterEncoder 进行注册,之后创立 logger 的时候设置 Config 的 Encoding 就能够依据指定的 name 找到对应的办法进行创立
小结
Encoder 接口内嵌了 ObjectEncoder,定义了 Clone、EncodeEntry 办法;ObjectEncoder 接口定义了各种类型的一系列 Add 办法;MapObjectEncoder 实现了 ObjectEncoder 接口,外部应用 map[string]interface{} 来存放数据。
doc
- zap