如何为Zap Logger的.Error()方法添加自定义逻辑?
问题描述
现有日志代码如下:
logger, _ := zap.NewProduction() logger.Error("hello")
希望.Error()方法执行额外操作,比如将特定消息日志发送至邮箱、仅将错误日志发送至Telegram机器人等。
尝试使用钩子实现,但无法获取完整字段,示例代码如下:
core := zapcore.RegisterHooks(logger.Core(), func(e zapcore.Entry) error { if e.Message == "[Recovery from panic]" { teleBot.SendMessageToAllChatIDs(fmt.Sprintf( "log_name: %s\nlevel: %s\nts: %s\nmsg: %s\nstack: %s", e.LoggerName, e.Level.String(), e.Time.Format(time.RFC1123), e.Message, e.Stack, )) } return nil })
解决方案
zap原生RegisterHooks提供的钩子仅接收zapcore.Entry,确实无法获取日志的完整结构化字段。要拿到完整字段并执行自定义操作,可通过以下两种方式实现:
方法1:自定义Core包装器
通过包装原有Core,在写入日志时同时获取Entry和字段,是最灵活的实现方式:
type HookedCore struct { zapcore.Core hook func(zapcore.Entry, []zapcore.Field) error } func (c *HookedCore) Check(entry zapcore.Entry, checked *zapcore.CheckedEntry) *zapcore.CheckedEntry { if c.Enabled(entry.Level) { return checked.AddCore(entry, c) } return checked } func (c *HookedCore) Write(entry zapcore.Entry, fields []zapcore.Field) error { // 先执行原有Core的日志写入逻辑 if err := c.Core.Write(entry, fields); err != nil { return err } // 执行自定义钩子逻辑,这里能拿到完整的Entry和结构化字段 return c.hook(entry, fields) } // 生成带自定义钩子的Core func NewHookedCore(core zapcore.Core, hook func(zapcore.Entry, []zapcore.Field) error) zapcore.Core { return &HookedCore{Core: core, hook: hook} }
使用示例:
// 初始化原有logger logger, _ := zap.NewProduction() // 包装Core并添加钩子逻辑 hookedCore := NewHookedCore(logger.Core(), func(entry zapcore.Entry, fields []zapcore.Field) error { // 过滤错误级别或特定消息 if entry.Level == zapcore.ErrorLevel || entry.Message == "[Recovery from panic]" { // 格式化结构化字段为可读字符串 fieldStr := "" for _, f := range fields { fieldStr += fmt.Sprintf("%s: %v\n", f.Key, f.Interface) } // 组装完整消息 fullMsg := fmt.Sprintf( "日志名称: %s\n级别: %s\n时间: %s\n消息: %s\n堆栈: %s\n附加字段:\n%s", entry.LoggerName, entry.Level.String(), entry.Time.Format(time.RFC1123), entry.Message, entry.Stack, fieldStr, ) // 发送Telegram消息 teleBot.SendMessageToAllChatIDs(fullMsg) // 这里可添加发送邮箱的逻辑 } return nil }) // 用新Core创建logger newLogger := logger.WithOptions(zap.WrapCore(func(_ zapcore.Core) zapcore.Core { return hookedCore })) // 调用Error时会自动触发钩子 newLogger.Error("请求处理失败", zap.String("user_id", "123"), zap.Int("status_code", 500))
方法2:封装SugaredLogger(适合简单场景)
如果不需要处理复杂的结构化字段,可直接封装自定义的Error方法:
type CustomLogger struct { *zap.SugaredLogger } func (l *CustomLogger) Errorf(format string, args ...interface{}) { // 先执行原有日志记录 l.SugaredLogger.Errorf(format, args...) // 执行额外操作,比如发送消息到Telegram msg := fmt.Sprintf(format, args...) teleBot.SendMessageToAllChatIDs(msg) // 这里可添加发送邮箱的逻辑 } // 使用示例 logger, _ := zap.NewProduction() customLogger := &CustomLogger{logger.Sugar()} customLogger.Errorf("用户[%s]登录失败", "foo")
注意:方法1适合需要处理完整结构化字段的场景,方法2适合快速实现简单的额外操作。
内容的提问来源于stack exchange,提问作者poo
相关产品推荐
相关产品推荐

