本文主要研究一下golang的zap的hook
func hookDemo() { count := &atomic.Int64{} logger, _ := zap.NewProduction(zap.Hooks(func(entry zapcore.Entry) error { fmt.Println("count:", count.Inc(), "msg:", entry.Message) return nil })) defer logger.Sync() // flushes buffer, if any sugar := logger.Sugar() sugar.Infow("failed to fetch URL", // Structured context as loosely typed key-value pairs. "url", "https://golang.org", "attempt", 3, "backoff", time.Second, ) sugar.Info("hello world") }
输出
{"level":"info","ts":1608045721.769727,"caller":"zap/zap_demo.go:29","msg":"failed to fetch URL","url":"https://golang.org","attempt":3,"backoff":1} count: 1 msg: failed to fetch URL {"level":"info","ts":1608045721.769826,"caller":"zap/zap_demo.go:35","msg":"hello world"} count: 2 msg: hello world
zap@v1.16.0/options.go
func Hooks(hooks ...func(zapcore.Entry) error) Option { return optionFunc(func(log *Logger) { log.core = zapcore.RegisterHooks(log.core, hooks...) }) }
Hooks方法将log的core使用zapcore.RegisterHooks包装了一下
zap@v1.16.0/zapcore/hook.go
func RegisterHooks(core Core, hooks ...func(Entry) error) Core { funcs := append([]func(Entry) error{}, hooks...) return &hooked{ Core: core, funcs: funcs, } }
RegisterHooks方法创建hooked,hooks赋值给hooked的funcs属性
zap@v1.16.0/zapcore/hook.go
type hooked struct { Core funcs []func(Entry) error } func (h *hooked) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { // Let the wrapped Core decide whether to log this message or not. This // also gives the downstream a chance to register itself directly with the // CheckedEntry. if downstream := h.Core.Check(ent, ce); downstream != nil { return downstream.AddCore(ent, h) } return ce } func (h *hooked) With(fields []Field) Core { return &hooked{ Core: h.Core.With(fields), funcs: h.funcs, } } func (h *hooked) Write(ent Entry, _ []Field) error { // Since our downstream had a chance to register itself directly with the // CheckedMessage, we don't need to call it here. var err error for i := range h.funcs { err = multierr.Append(err, h.funcs[i](ent)) } return err }
hooked内嵌了Core,它覆盖了Check、With、Write方法;Check方法将hooked添加到downstream;Write方法遍历hooks,执行回调
Hooks方法将log的core使用zapcore.RegisterHooks包装了一下;RegisterHooks方法创建hooked,hooks赋值给hooked的funcs属性;hooked包装了core,因而需要在Check的时候把自己注册进去,然后在Write的时候就可以执行到自己注册的hooks。一般可以将metrics等简单的操作通过hook来实现,而复杂的逻辑则最好通过实现zapcore.Core来做。