前言
在之前的篇章中主要按照幾個核心使用點進行源碼的分析,有些細節未能覆寫到,是以對使用時可能需要注意的細節補充說明下。
輸出日志資訊内容的key具體順序問題
先說結論,先後順序為:
Level-Time-LoggerName-Caller-Message-已排序的InitialFields-fields->Stack
注意:以上部分資訊可預設,和具體的配置有關。
具體處理過程如下:
1.config build預處理部分資訊
build中對InitialFields按照key排序處理,存入logger的core.enc.buf
func (cfg Config) Build(opts ...Option) (*Logger, error) {
...
log := New(
zapcore.NewCore(enc, sink, cfg.Level),
cfg.buildOptions(errSink)...,
)
...
return log, nil
}
func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option {
opts := []Option{ErrorOutput(errSink)}
...
if len(cfg.InitialFields) > 0 {
fs := make([]Field, 0, len(cfg.InitialFields))
keys := make([]string, 0, len(cfg.InitialFields))
for k := range cfg.InitialFields {
keys = append(keys, k)
}
sort.Strings(keys)//按key排序
for _, k := range keys {//根據已排序的key處理對應的val
fs = append(fs, Any(k, cfg.InitialFields[k]))
}
opts = append(opts, Fields(fs...))//添加fields的處理func
}
return opts
}
// Fields adds fields to the Logger.
//封裝fields
func Fields(fs ...Field) Option {
return optionFunc(func(log *Logger) {
log.core = log.core.With(fs)
})
}
// 構造core
func (c *ioCore) With(fields []Field) Core {
clone := c.clone()
addFields(clone.enc, fields)
return clone
}
//fields的真正處理的func,将fields添加到編碼器
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 Int64Type:
enc.AddInt64(f.Key, f.Integer)
...
default:
panic(fmt.Sprintf("unknown field type: %v", f))
}
if err != nil {
enc.AddString(fmt.Sprintf("%sError", f.Key), err.Error())
}
}
func (enc *jsonEncoder) AppendInt64(val int64) {
enc.addElementSeparator()
enc.buf.AppendInt(val)
}
//開始調用再建構core後
func New(core zapcore.Core, options ...Option) *Logger {
if core == nil {
return NewNop()
}
log := &Logger{
core: core,
errorOutput: zapcore.Lock(os.Stderr),
addStack: zapcore.FatalLevel + 1,
}
return log.WithOptions(options...)//此處進行InitialFields處理的調用
}
func (log *Logger) WithOptions(opts ...Option) *Logger {
c := log.clone()
for _, opt := range opts {
opt.apply(c)
}
return c
}
func (f optionFunc) apply(log *Logger) {
f(log)
}
2.Write時進行整體資訊順序控制
EncodeEntry中按照固定的順序依次對key及其value進行編碼。key的順序依次為:
LevelKey->TimeKey->NameKey->CallerKey->MessageKey->InitialFields->fields->StacktraceKey
func (log *Logger) Info(msg string, fields ...Field) {
if ce := log.check(InfoLevel, msg); ce != nil {
ce.Write(fields...)
}
}
func (ce *CheckedEntry) Write(fields ...Field) {
...
var err error
for i := range ce.cores {
err = multierr.Append(err, ce.cores[i].Write(ce.Entry, fields))
}
...
}
func (c *ioCore) Write(ent Entry, fields []Field) error {
buf, err := c.enc.EncodeEntry(ent, fields)
...
return nil
}
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 && 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.MessageKey != "" {
final.addKey(enc.MessageKey)
final.AppendString(ent.Message)
}
if enc.buf.Len() > 0 {//處理InitialFields
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
}
公衆号
鄙人剛剛開通了公衆号,專注于分享Go開發相關内容,望大家感興趣的支援一下,在此特别感謝。
