你好!你可以使用Uber Zap来复制条目键并在同一日志中显示它。

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

How can I duplicate entry keys and show it in the same log with Uber Zap?

问题

我想要将caller这个键复制一份,用另一个键名method,并在日志中同时显示两个键...

{"level":"error", "caller": "testing/testing.go:1193", "method": "testing/testing.go:1193", "message": "foo"}

有什么想法吗?

英文:

I would like to duplicate entry keys like caller with another key name method and show both in the log...

{"level":"error", "caller: "testing/testing.go:1193", "method": "testing/testing.go:1193", "message": "foo"}

Any ideas?

答案1

得分: 2

你不能更改zapcore.Entry的字段。你可以更改它的编组方式,但是在结构体中添加幽灵字段是一种糟糕的hack。你可以使用自定义编码器,并将调用者的副本作为新的字符串项附加到[]zapcore.Field中。特别是,默认的JSON编码器输出是从Caller.TrimmedPath()获取的:

type duplicateCallerEncoder struct {
	zapcore.Encoder
}

func (e *duplicateCallerEncoder) Clone() zapcore.Encoder {
	return &duplicateCallerEncoder{Encoder: e.Encoder.Clone()}
}

func (e *duplicateCallerEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
    // 将项附加到字段列表中
	fields = append(fields, zap.String("method", entry.Caller.TrimmedPath()))
	return e.Encoder.EncodeEntry(entry, fields)
}

请注意,上述代码实现了Encoder.Clone()。详细信息请参见:https://stackoverflow.com/questions/70502026/why-custom-encoding-is-lost-after-calling-logger-with-in-uber-zap/70503730#70503730

然后,你可以通过构建一个新的Zap核心或注册自定义编码器来使用它。注册的构造函数将JSONEncoder嵌入到你的自定义编码器中,这是生产日志记录器的默认编码器:

func init() {
    // name可以自定义
	err := zap.RegisterEncoder("duplicate-caller", func(config zapcore.EncoderConfig) (zapcore.Encoder, error) {
		return &duplicateCallerEncoder{Encoder: zapcore.NewJSONEncoder(config)}, nil
	})
    // 如果出错,panic是合理的,因为程序无法初始化
	if err != nil {
		panic(err)
	}
}

func main() {
	cfg := zap.NewProductionConfig()
	cfg.Encoding = "duplicate-caller"
	logger, _ := cfg.Build()
	logger.Info("this is info")
}

上述代码复制了使用自定义配置初始化生产日志记录器的过程。

对于这样简单的配置,我更喜欢使用init()方法和zap.RegisterEncoder。如果需要的话,这样可以更快地重构代码,或者如果一开始就将其放在其他包中。当然,你也可以在main()函数中进行注册;或者如果需要额外的自定义,则可以使用zap.New(zapcore.NewCore(myCustomEncoder, /* other args */))

你可以在这个playground中查看完整的程序:https://go.dev/play/p/YLDXbdZ-qZP

它的输出是:

{"level":"info","ts":1257894000,"caller":"sandbox3965111040/prog.go:24","msg":"this is info","method":"sandbox3965111040/prog.go:24"}
英文:

You can't change the fields of a zapcore.Entry. You may change how it is marshalled, but honestly adding ghost fields to a struct is a bad hack. What you can do is use a custom encoder, and append to []zapcore.Field a new string item with a copy of the caller. In particular, the default output of the JSON encoder is obtained from Caller.TrimmedPath():

type duplicateCallerEncoder struct {
	zapcore.Encoder
}

func (e *duplicateCallerEncoder) Clone() zapcore.Encoder {
	return &duplicateCallerEncoder{Encoder: e.Encoder.Clone()}
}

func (e *duplicateCallerEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
    // appending to the fields list
	fields = append(fields, zap.String("method", entry.Caller.TrimmedPath()))
	return e.Encoder.EncodeEntry(entry, fields)
}

Note that the above implements Encoder.Clone(). See this for details: https://stackoverflow.com/questions/70502026/why-custom-encoding-is-lost-after-calling-logger-with-in-uber-zap/70503730#70503730

And then you can use it by either constructing a new Zap core, or by registering the custom encoder. The registered constructor embeds a JSONEncoder into your custom encoder, which is the default encoder for the production logger:

func init() {
    // name is whatever you like
	err := zap.RegisterEncoder("duplicate-caller", func(config zapcore.EncoderConfig) (zapcore.Encoder, error) {
		return &duplicateCallerEncoder{Encoder: zapcore.NewJSONEncoder(config)}, nil
	})
    // it's reasonable to panic here, since the program can't initialize
	if err != nil {
		panic(err)
	}
}

func main() {
	cfg := zap.NewProductionConfig()
	cfg.Encoding = "duplicate-caller"
	logger, _ := cfg.Build()
	logger.Info("this is info")
}

The above replicates the initialization of a production logger with your custom config.

For such a simple config, I prefer the init() approach with zap.RegisterEncoder. It makes it faster to refactor code, if needed, and/or if you place this in some other package to begin with. You can of course do the registration in main(); or if you need additional customization, then you may use zap.New(zapcore.NewCore(myCustomEncoder, /* other args */))

You can see the full program in this playground: https://go.dev/play/p/YLDXbdZ-qZP

It outputs:

{"level":"info","ts":1257894000,"caller":"sandbox3965111040/prog.go:24","msg":"this is info","method":"sandbox3965111040/prog.go:24"}

huangapple
  • 本文由 发表于 2022年1月18日 10:51:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/70749793.html
匿名

发表评论

匿名网友

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

确定