英文:
How to access Fields in zap Hooks?
问题
我可以如何访问uber-zap的钩子中有关日志事件的完整信息?
例如,我正在尝试向日志事件中添加一个zapcore.Field
,但它不会显示在zapcore.Entry
中。
如果不可能的话,我是否可以以某种方式获得完全格式化的字符串?目标是在出现错误时发送电子邮件/自动消息/Sentry等。
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"))
}
输出 - 没有foo
或bar
的痕迹:
{"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"
}
英文:
How can I access the full information about the logging event in uber-zap's hooks?
For example, I am trying to add a zapcore.Field
to the logging event, but it does not show up in the zapcore.Entry
.
If it is not possible, can I at least have the fully-formatted string somehow? The goal is to send an email/automated message/Sentry/etc in case of errors.
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"))
}
Output - no traces of foo
or bar
:
{"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"
}
答案1
得分: 7
字段在Zap hooks中不可用。zap.Hooks
的文档明确说明了这一点:
> [...] Hooks对于简单的副作用非常有用,比如捕获发出的日志数量的指标。更复杂的副作用,包括需要访问Entry的结构化字段的任何内容,应该作为zapcore.Core来实现。[...]
因此,要使用go-spew来转储日志,您需要一个自定义的core。您有两个主要选项。
使用自定义编码器的自定义core
这种方法的优点是允许更多的自定义。
entry的字段在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
的自定义core
这种方法的优点是允许更简单的初始化。
与第一种选项类似,zapcore.Core
也是一个接口,因此您可以通过在您的结构体中嵌入它来实现它,并仅重新实现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 logger中获取现有的core来实例化它:
l, _ := zap.NewProduction()
logger := zap.New(&MyCore{Core: l.Core()})
英文:
Fields are not available in Zap hooks. The docs of zap.Hooks
say this explicitly:
> [...] 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. [...]
So to dump logs with go-spew, you need a custom core. You have two main options.
Custom core with custom encoder
This has the advantage of allowing more customization.
The entry's fields are available in zapcore.Encoder.EncodeEntry
. The strategy is, as usual, to embed a zapcore.Encoder
into your struct and reimplement 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)
}
Remember to implement Clone()
as well, if you plan to use structured logging.
Custom core with Write
This has the advantage of allowing simpler initialization.
Similarly to the first option, zapcore.Core
is also an interface, so you can implement it by embedding in your struct, and reimplement only 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)
}
and instantiate it by taking the existing core from a default zap logger:
l, _ := zap.NewProduction()
logger := zap.New(&MyCore{Core: l.Core()})
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论