如何测试从自定义配置构建的 zap Logger 的日志记录?
Posted
技术标签:
【中文标题】如何测试从自定义配置构建的 zap Logger 的日志记录?【英文标题】:How to test logging of a zap Logger built from custom Config? 【发布时间】:2019-03-15 00:41:46 【问题描述】:我有一个从自定义配置(即config.Build()
)生成的 Zap 记录器。我想通过在测试方法中调用例如logger.Info()
来测试记录器并断言结果以查看它是否符合配置集。我怎样才能做到这一点?
代码示例:
func GetLogger() *zap.Logger
config := &zap.Config
Encoding: "json",
Level: zap.NewAtomicLevelAt(zapcore.InfoLevel),
OutputPaths: []string"stdout",
ErrorOutputPaths: []string"stdout",
EncoderConfig: zapcore.EncoderConfig
MessageKey: "@m",
LevelKey: "@l",
EncodeLevel: zapcore.CapitalLevelEncoder,
TimeKey: "@t",
EncodeTime: zapcore.EpochMillisTimeEncoder,
CallerKey: "@c",
EncodeCaller: zapcore.ShortCallerEncoder,
StacktraceKey: "@x",
,
return config.Build()
【问题讨论】:
这两个答案也适用于任何记录器,而不仅仅是那些从自定义配置构建的记录器 【参考方案1】:Zap 有一个sinks 的概念,即日志消息的目的地。为了测试,实现一个只记住消息的接收器(例如在 bytes.Buffer 中):
package main
import (
"bytes"
"net/url"
"strings"
"testing"
"time"
"go.uber.org/zap"
)
// MemorySink implements zap.Sink by writing all messages to a buffer.
type MemorySink struct
*bytes.Buffer
// Implement Close and Sync as no-ops to satisfy the interface. The Write
// method is provided by the embedded buffer.
func (s *MemorySink) Close() error return nil
func (s *MemorySink) Sync() error return nil
func TestLogger(t *testing.T)
// Create a sink instance, and register it with zap for the "memory"
// protocol.
sink := &MemorySinknew(bytes.Buffer)
zap.RegisterSink("memory", func(*url.URL) (zap.Sink, error)
return sink, nil
)
conf := zap.NewProductionConfig() // TODO: replace with real config
// Redirect all messages to the MemorySink.
conf.OutputPaths = []string"memory://"
l, err := conf.Build()
if err != nil
t.Fatal(err)
l.Info("failed to fetch URL",
zap.String("url", "http://example.com"),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
// Assert sink contents
output := sink.String()
t.Logf("output = %s", output)
if !strings.Contains(output, `"url":"http://example.com"`)
t.Error("output missing: url=http://example.com")
【讨论】:
谢谢,这很有帮助。如果OutputPaths
设置为[]string"stdout"
,有没有办法进行测试?
一般来说是的。 os.Stdout 只是一个可以重新分配的变量。但我强烈建议不要这样做,因为它有副作用。例如,我不确定您是否会看到测试报告。输出字段不应该影响记录器的工作方式,所以我不确定你为什么要在测试中使用"stdout"
。
知道了,我刚刚在问题中添加了一个示例来澄清我的意思,基本上我想测试函数GetLogger()
返回的记录器。但是从你的回复看来,我还是直接把outputPaths
作为函数参数传进去会更好,这样单元测试就可以有不同的值了?
要么返回配置,要么返回配置而不是完成的记录器,并让调用者调用 Build()。【参考方案2】:
zap 有一个特殊的zaptest/observer
模块用于单元测试:
package test
import (
"testing"
"go.uber.org/zap"
"go.uber.org/zap/zaptest/observer"
)
func setupLogsCapture() (*zap.Logger, *observer.ObservedLogs)
core, logs := observer.New(zap.InfoLevel)
return zap.New(core), logs
func Test(t *testing.T)
logger, logs := setupLogsCapture()
logger.Warn("This is the warning")
if logs.Len() != 1
t.Errorf("No logs")
else
entry := logs.All()[0]
if entry.Level != zap.WarnLevel || entry.Message != "This is the warning"
t.Errorf("Invalid log entry %v", entry)
【讨论】:
这是正确的答案。使用观察者进行测试。 方法observer.New
返回一个新的zap核心。如果我有一个现有的核心并想观察它怎么办?似乎这种方法抽象出了创建自定义配置的能力以上是关于如何测试从自定义配置构建的 zap Logger 的日志记录?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用从自定义 MySQL Docker 映像创建容器的 GenericContainer 配置 spring 数据源