如何访问 zap Hooks 中的字段?

huangapple go评论74阅读模式
英文:

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"))
}

输出 - 没有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"
}
英文:

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()})

huangapple
  • 本文由 发表于 2021年12月29日 04:20:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/70512120.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定