ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

zap扩展生态与总结

zap扩展生态与总结 1. zaptest/observer测试断言日志官方示范自定义 Coreobserver.go包注释一语道破一个把日志条目保存在内存里的、与编码格式无关的 Core——不关心输出长什么样只关心记了什么。1.1 结构// ObservedLogs并发安全的有序日志集合observer.go:30-33 type ObservedLogs struct { mu sync.RWMutex logs []LoggedEntry } // logged_entry.goLoggedEntry Entry Context []Field字段被解码成结构化形式 type LoggedEntry struct { zapcore.Entry Context []zapcore.Field } // Newobserver.go func New(lvl zapcore.LevelEnabler) (zapcore.Core, *ObservedLogs) // 内部用 memoryEncoderzapcore/memory_encoder.go把 Field 编码成 map/切片再还原1.2 查询 API都是拷贝后返回断言安全方法作用Len() / All() / TakeAll()数量 / 全量拷贝 / 取走并清空AllUntimed()时间清零——断言不被时间戳干扰FilterLevelExact/FilterMessage/FilterMessageSuffix/FilterField链式过滤1.3 用法与源码启示core, logs : observer.New(zapcore.InfoLevel) logger : zap.New(core) Login(logger, alice) for _, e : range logs.FilterMessage(login ok).All() { // e.Context[0] 是 trace_id 字段... }源码启示observer 的 Core 在 Write 里把 Field 用memoryEncoder落成map[string]interface{}——这就是编码无关的实现方式。你的业务测试、或日志审计类需求都可以照抄这个模式。2. zaptest让日志进 t.Logzaptest/logger.go// NewLogger(t) 构造一个把日志写到 testing.T 的 logger // 核心testingT 适配器 writerSyncer{t.Log}价值单测跑挂时失败现场的日志自然出现在go test -v输出里测试通过时不刷屏。还有timeout.go带超时的 t 封装等周边。3. zapiologger 变身 io.Writerzapio/writer.go:51-63type Writer struct { Log *zap.Logger // 目标 logger必填 Level zapcore.Level // 写入级别默认 Info buff bytes.Buffer // 行缓冲 }行为75-100按换行切分每凑满一行发一条日志没遇到\n就先攒着Sync/Close 强制冲刷。// 典型用途源码注释 38-49接管子进程输出 writer : zapio.Writer{Log: logger, Level: zap.DebugLevel} defer writer.Close() cmd : exec.CommandContext(ctx, ping, -c, 4, example.com) cmd.Stdout writer // 每行输出 一条结构化日志 cmd.Stderr writer cmd.Run()细节Write先查Enabled77-79 行——级别禁用时直接吞掉连切分都不做又是那个快路径思想。4. zapgrpc适配 gRPC 日志接口zapgrpc/zapgrpc.go// 级别映射表31-36 var _grpcToZapLevel map[int]zapcore.Level{ grpcLvlInfo: zapcore.InfoLevel, grpcLvlWarn: zapcore.WarnLevel, grpcLvlError: zapcore.ErrorLevel, grpcLvlFatal: zapcore.FatalLevel, } type Logger struct { // 适配器把 grpclog 的方法翻译给 zap delegate *zap.Logger // 内部按级别分组 printerprint/printf/fatal...支持 Option 微调 }用法07 篇提过grpclog.SetLoggerV2(zapgrpc.NewLogger(logger))。这个包是接口适配器类扩展的标准样例不碰 Core/Encoder纯 API 翻译。5. exp实验区exp/放不稳定的新能力有独立 CHANGELOG不承诺兼容sl : slog.New(zapslog.NewHandler(zapLogger, nil)) sl.Info(via slog, k, v) // 走 zap 的编码、采样、输出zapslogslog.Handler实现——把任意 slog 调用转发给 zap Core。意义新代码写标准库 API底层保留 zap 的性能与生态——渐进迁移的标准姿势zapfield字段构造函数的实验性增强如zap.Field组合工具6. 实战自己动手写两个扩展6.1 敏感信息打码 Core装饰器模式// 需求输出前把 password/token 字段的值替换为 *** type redactCore struct { zapcore.Core sensitive map[string]struct{} } func (c redactCore) With(fields []zapcore.Field) zapcore.Core { return redactCore{c.Core.With(fields), c.sensitive} } func (c redactCore) Write(ent zapcore.Entry, fields []zapcore.Field) error { filtered : make([]zapcore.Field, 0, len(fields)) for _, f : range fields { if _, ok : c.sensitive[f.Key]; ok { f zap.String(f.Key, ***) } filtered append(filtered, f) } return c.Core.Write(ent, filtered) } // 组装 core : redactCore{ Core: baseCore, sensitive: map[string]struct{}{password: {}, token: {}}, } logger : zap.New(core) logger.Info(login, zap.String(password, 123456)) // {msg:login,password:***}注意局限只处理日志点字段With 进上下文的字段已被预编码成字节13 篇——要对上下文生效就得在 Encoder 层动刀或约定敏感字段只走日志点。6.2 自定义 Encoderlogfmt 风格实现接口// 需求keyvalue 格式类似 syslog/logfmt type logfmtEncoder struct { zapcore.EncoderConfig *buffer.Pool // 简化直接内嵌池 } func (e logfmtEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) { buf : bufferpool.Get() buf.AppendString(time); buf.AppendTime(ent.Time, time.RFC3339) buf.AppendString( level); buf.AppendString(ent.Level.String()) buf.AppendString( msg); buf.AppendString(ent.Message) for _, f : range fields { buf.AppendByte( ) buf.AppendString(f.Key); buf.AppendByte() // 简化只演示 string/int完整实现按 f.Type switch switch f.Type { case zapcore.StringType: buf.AppendString(f.String) case zapcore.Int64Type: buf.AppendInt(f.Integer) } } buf.AppendString(\n) return buf, nil } // 还需实现 ObjectEncoder 的几十个方法可内嵌 *zapcore.jsonEncoder 借力或像 consoleEncoder 那样组合 // 注册进 Config 体系 zap.RegisterEncoder(logfmt, func(cfg zapcore.EncoderConfig) (zapcore.Encoder, error) { return logfmtEncoder{cfg, nil}, nil }) // 之后 Config.Encoding logfmt 即用工程提示完整实现 ObjectEncoder 工作量不小——组合内嵌 jsonEncoder优于重写consoleEncoder 就是这么干的。6.3 扩展点选择指南需求扩展点参考改输出格式EncoderconsoleEncoder改行为过滤/脱敏/采样/审计Core 装饰器sampler / observer新输出目标WriteSyncer RegisterSink06 篇 kafka/tcp 示例字段级编码ObjectMarshaler04 篇翻译外部日志 API适配器zapgrpc / RedirectStdLog指标/告警Hooks 或 Coreoptions.go / hooked
返回列表