go - 如何访问 zap Hooks 中的字段?

标签 go logging go-zap

如何在 uber-zap 的 Hook 中访问有关日志记录事件的完整信息?

例如,我试图将 zapcore.Field 添加到日志记录事件中,但它没有显示在 zapcore.Entry 中。

如果不可能,我至少可以以某种方式获得完整格式的字符串吗?目标是在出现错误时发送电子邮件/自动消息/哨兵/等。

package main

import (
    "log"

    "github.com/davecgh/go-spew/spew"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
)

func main() {
    prodLogger, err := zap.NewProduction(zap.Hooks(func(entry zapcore.Entry) error {
        if entry.Level == zapcore.ErrorLevel {
            spew.Dump(entry) // fancy console printer
        }

        return nil
    }))

    if err != nil {
        log.Fatal(err)
    }

    prodLogger.
        Named("logger_name").
        Error("something happened", zap.String("foo", "bar"))
}

输出 - 没有 foobar 的痕迹:

{"level":"error","ts":1640722252.899601,"logger":"logger_name","caller":"awesomep2/main.go:23","msg":"something happened","foo":"bar","stacktrace":"main.main\n\t/Users/xxx/GitHub/awesomep2/main.go:23\nruntime.main\n\t/usr/local/go/src/runtime/proc.go:255"}
(zapcore.Entry) {
 Level: (zapcore.Level) error,
 Time: (time.Time) 2021-12-28 13:10:52.899601 -0700 MST m=+0.000629089,
 LoggerName: (string) (len=11) "logger_name",
 Message: (string) (len=18) "something happened",
 Caller: (zapcore.EntryCaller) /Users/xxx/GitHub/awesomep2/main.go:23,
 Stack: (string) (len=103) "main.main\n\t/Users/xxx/GitHub/awesomep2/main.go:23\nruntime.main\n\t/usr/local/go/src/runtime/proc.go:255"
}

最佳答案

字段在 Zap Hook 中不可用。 zap.Hooks 的文档明确地说:

[...] Hooks are useful for simple side effects, like capturing metrics for the number of emitted logs. More complex side effects, including anything that requires access to the Entry's structured fields, should be implemented as a zapcore.Core instead. [...]

因此,要使用 go-spew 转储日志,您需要一个自定义核心。您有两个主要选择。

带有自定义编码器的自定义内核

这具有允许更多自定义的优势。

条目的字段在 zapcore.Encoder.EncodeEntry 中可用。与往常一样,该策略是将 zapcore.Encoder 嵌入到您的结构中并重新实现 EncodeEntry:

type spewDumpEncoder struct {
    zapcore.Encoder
}

func (e *spewDumpEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
    if entry.Level == zapcore.ErrorLevel {
        spew.Dump(entry, fields)
    }
    return e.Encoder.EncodeEntry(entry, fields)
}

如果您计划使用结构化日志记录,请记住还要实现 Clone()

使用 Write 的自定义核心

这样做的好处是允许更简单的初始化。

与第一个选项类似,zapcore.Core也是一个接口(interface),所以你可以通过嵌入到你的结构中来实现它,并且只重新实现Write:

type MyCore struct {
    zapcore.Core
}

func (c *MyCore) Check(entry zapcore.Entry, checked *zapcore.CheckedEntry) *zapcore.CheckedEntry {
    if c.Enabled(entry.Level) {
        return checked.AddCore(entry, c)
    }
    return checked
}

func (c *MyCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
    if entry.Level == zapcore.ErrorLevel {
        spew.Dump(entry, fields)
    }
    return c.Core.Write(entry, fields)
}

并通过从默认的 zap 记录器中获取现有核心来实例化它:

    l, _ := zap.NewProduction()
    logger := zap.New(&MyCore{Core: l.Core()})

关于go - 如何访问 zap Hooks 中的字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70512120/

相关文章:

go - 在 Go 中通过反射创建结构

go - 如何初始化一个 zap 记录器并在其他 Go 文件中重用它?

go - SQLMock 和 Gorm : Mocking Postgres Insert

database - 使用 sql.Open 进行 SQLite3 数据库连接

tomcat - 在哪里配置内部 tomcat7 stdout/stderr 日志文件

Python HTTP 请求和调试级别记录到日志文件

go - 如何将 zap 记录器与 go-kit 一起使用?

go - 如何测试从自定义配置构建的 zap Logger 的日志记录?

makefile - go test 在 Makefile 中找不到包测试

c - 如何记录使用 VS 2010 执行的代码部分的行号