为什么在调用Uber Zap中的logger.With后自定义编码会丢失?

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

Why custom encoding is lost after calling logger.With in Uber Zap?

问题

根据您提供的代码,您替换了uber-zap日志记录器的编码器,以便在每个日志条目前添加一个SystemD友好的错误级别(<LEVEL>)。但是,当您在使用附加字段(With(fields ...Field))的日志记录器之后,自定义的前缀消失了。

您得到的输出是:

<6> 1.640656130756576e+09	info	this is info
<7> 1.640656130756611e+09	debug	this is debug
<4> 1.640656130756615e+09	warn	this is warn
1.6406561307566311e+09	info	this does not have the prefix :(	{"foo": "bar"}

您想知道自己做错了什么。

根据您提供的代码,问题可能出在以下几个地方:

  1. main函数中,您在创建新的logger实例时,使用了logger = logger.With(zap.String("foo", "bar"))。这会创建一个新的logger实例,并且新的实例不会继承自定义的编码器。您可以尝试将其更改为logger = logger.With(zap.String("foo", "bar")),以便在现有的logger实例上添加字段,而不是创建一个新的实例。

  2. EncodeEntry方法中,您调用了嵌入的编码器的EncodeEntry方法来保持原始的编码格式。但是,这可能会导致附加字段的编码不包含自定义前缀。您可以尝试将EncodeEntry方法中的以下行更改为:

consolebuf, err := e.Encoder.EncodeEntry(entry, fields)

更改为:

consolebuf, err := e.Encoder.EncodeEntry(entry, nil)

这样可以确保附加字段的编码不包含自定义前缀。

请尝试对这些地方进行更改,并查看是否解决了您的问题。

英文:

(based on this question: https://stackoverflow.com/questions/70489167/uber-zap-logger-how-to-prepend-every-log-entry-with-a-string)

I replaced the Encoder of my uber-zap logger with a custom one to prepend every log entry with a SystemD-friendly error level (&lt;LEVEL&gt;), but now after I use the logger with additional fields (With(fields ...Field)), the custom prepending is gone:

package main

import (
	&quot;os&quot;

	&quot;go.uber.org/zap&quot;
	&quot;go.uber.org/zap/buffer&quot;
	&quot;go.uber.org/zap/zapcore&quot;
)

func getConfig() zap.Config {
	// your current config options
	return zap.NewProductionConfig()
}

type prependEncoder struct {
	// embed a zapcore encoder
	// this makes prependEncoder implement the interface without extra work
	zapcore.Encoder

	// zap buffer pool
	pool buffer.Pool
}

// EncodeEntry implementing only EncodeEntry
func (e *prependEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
	// new log buffer
	buf := e.pool.Get()

	// prepend the JournalD prefix based on the entry level
	buf.AppendString(e.toJournaldPrefix(entry.Level))
	buf.AppendString(&quot; &quot;)

	// calling the embedded encoder&#39;s EncodeEntry to keep the original encoding format
	consolebuf, err := e.Encoder.EncodeEntry(entry, fields)
	if err != nil {
		return nil, err
	}

	// just write the output into your own buffer
	_, err = buf.Write(consolebuf.Bytes())
	if err != nil {
		return nil, err
	}
	return buf, nil
}

// some mapper function
func (e *prependEncoder) toJournaldPrefix(lvl zapcore.Level) string {
	switch lvl {
	case zapcore.DebugLevel:
		return &quot;&lt;7&gt;&quot;
	case zapcore.InfoLevel:
		return &quot;&lt;6&gt;&quot;
	case zapcore.WarnLevel:
		return &quot;&lt;4&gt;&quot;
	}
	return &quot;&quot;
}

func main() {
	cfg := getConfig()

	// constructing our prependEncoder with a ConsoleEncoder using your original configs
	enc := &amp;prependEncoder{
		Encoder: zapcore.NewConsoleEncoder(cfg.EncoderConfig),
		pool:    buffer.NewPool(),
	}

	logger := zap.New(
		zapcore.NewCore(
			enc,
			os.Stdout,
			zapcore.DebugLevel,
		),
		// this mimics the behavior of NewProductionConfig.Build
		zap.ErrorOutput(os.Stderr),
	)

	logger.Info(&quot;this is info&quot;)
	logger.Debug(&quot;this is debug&quot;)
	logger.Warn(&quot;this is warn&quot;)

	logger = logger.With(zap.String(&quot;foo&quot;, &quot;bar&quot;))

	logger.With(zap.String(&quot;foo&quot;, &quot;bar&quot;)).Info(&quot;this does not have the prefix :(&quot;)
}

The output I get is:

&lt;6&gt; 1.640656130756576e+09	info	this is info
&lt;7&gt; 1.640656130756611e+09	debug	this is debug
&lt;4&gt; 1.640656130756615e+09	warn	this is warn
1.6406561307566311e+09	info	this does not have the prefix :(	{&quot;foo&quot;: &quot;bar&quot;}

What am I doing wrong?

答案1

得分: 2

你还需要实现zapcore.Encoder接口中的Clone()方法。如果你希望保持父记录器不变,你需要构建一个实际的克隆对象,可能具有相同的配置,所以你可能想将其存储为一个字段:

type prependEncoder struct {
	zapcore.Encoder
	pool buffer.Pool
	cfg  zapcore.EncoderConfig
}

func (e *prependEncoder) Clone() zapcore.Encoder {
	return &prependEncoder{
		// 使用基本配置克隆编码器
		Encoder: zapcore.NewConsoleEncoder(e.cfg),
		pool:    buffer.NewPool(),
		cfg:     e.cfg,
	}
}

如果你不实现它,当调用logger.Clone()时,运行的方法将是嵌入的zapcore.Encoder上声明的下一个最浅的方法,这个方法将不再具有你自定义的EncodeEntry

现在运行以下代码:

logger.Info("this is info")
logger.Debug("this is debug")
logger.Warn("this is warn")
child := logger.With(zap.String("foo", "bar"))
logger.Warn("original")
child.Info("new one")

输出结果为:

<6> INFO        this is info
<7> DEBUG       this is debug
<4> WARN        this is warn
cloning...
<4> WARN        original
<6> INFO        new one {"foo": "bar"}
英文:

You have to also implement Clone() from the zapcore.Encoder interface. If you wish to keep the parent logger unaltered, you have to construct an actual clone — possibly with the same config, so you might want to store it as a field:

type prependEncoder struct {
zapcore.Encoder
pool buffer.Pool
cfg  zapcore.EncoderConfig
}
func (e *prependEncoder) Clone() zapcore.Encoder {
return &amp;prependEncoder{
// cloning the encoder with the base config
Encoder: zapcore.NewConsoleEncoder(e.cfg),
pool:    buffer.NewPool(),
cfg:     e.cfg,
}
}

If you don't implement it, the method that runs is the next shallowest one when calling logger.Clone(), which is the Clone() declared on the embedded zapcore.Encoder. That one then doesn't have your custom EncodeEntry anymore.

Now running the following:

	logger.Info(&quot;this is info&quot;)
logger.Debug(&quot;this is debug&quot;)
logger.Warn(&quot;this is warn&quot;)
child := logger.With(zap.String(&quot;foo&quot;, &quot;bar&quot;))
logger.Warn(&quot;original&quot;)
child.Info(&quot;new one&quot;)

Outputs:

&lt;6&gt; INFO        this is info
&lt;7&gt; DEBUG       this is debug
&lt;4&gt; WARN        this is warn
cloning...
&lt;4&gt; WARN        original
&lt;6&gt; INFO        new one {&quot;foo&quot;: &quot;bar&quot;}

huangapple
  • 本文由 发表于 2021年12月28日 09:45:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/70502026.html
匿名

发表评论

匿名网友

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

确定