Go Zap
Go zap 是一个用 Go 语言编写的快速、结构化、分级的日志库。
安装
go get -u go.uber.org/zap
注意,zap 仅支持 Go 的两个最新小版本。
选择 Logger
在性能要求较高但并不重要的情况下,可以使用 SugaredLogger。它比其他结构化日志程序包快 4-10 倍,并包含结构化和 printf 风格的 API。默认情况下,日志记录器是无缓冲的。不过,由于 zap 的底层 API 允许缓冲,因此在进程退出前调用 Sync 是个好习惯。
func main() {
logger, _ := zap.NewProduction()
defer logger.Sync()
sugar := logger.Sugar()
sugar.Infow("failed to fetch URL",
"url", "https://www.google.com",
"attempt", 3,
"backoff", time.Second,
)
sugar.Infof("Failed to fetch URL: %s", "https://www.google.com")
}
go run main.go
#{"level":"info","ts":1692068155.4790313,"caller":"zap-demo/main.go:14","msg":"failed to fetch URL","url":"https://www.google.com","attempt":3,"backoff":1}
#{"level":"info","ts":1692068155.4790313,"caller":"zap-demo/main.go:19","msg":"Failed to fetch URL: https://www.google.com"}
在每一微秒和每次分配都很重要的罕见情况下,请使用 Logger。它比 SugaredLogger 更快,分配的资源也少得多,但只支持强类型、结构化日志记录,如:
func main() {
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("failed to fetch URL",
zap.String("url", "https://www.google.com"),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
}
go run main.go
#{"level":"info","ts":1692069088.4173353,"caller":"zap-demo/main.go:12","msg":"failed to fetch URL","url":"https://www.google.com","attempt":3,"backoff":1}
在 Logger 和 SugaredLogger 之间做出选择并不需要在整个应用程序中进行:在两者之间进行转换既简单又便宜,如:
func main() {
logger := zap.NewExample()
defer logger.Sync()
sugar := logger.Sugar()
plain := sugar.Desugar()
}
配置 Zap
构建记录仪的最简单方法是使用 zap 的固定预设:NewExample、NewProduction 和 NewDevelopment。这些预设通过单个函数调用构建一个记录器,它们的说明和用法如下:
-
NewExample是用于演示和测试 zap 的功能的预设。它会将日志以人类可读的格式输出到标准输出,并且包含所有日志级别和上下文信息。它不会对日志进行采样或添加调用者信息,也不会使用任何编码器配置。func main() { logger := zap.NewExample() defer logger.Sync() logger.Info("failed to fetch URL", zap.String("url", "https://www.google.com"), zap.Int("attempt", 3), zap.Duration("backoff", time.Second), ) }go run main.go #{"level":"info","msg":"failed to fetch URL","url":"https://www.google.com","attempt":3,"backoff":"1s"} -
NewProduction是用于生产环境的预设。它会将日志以 JSON 格式输出到标准错误,并且只包含 InfoLevel 或更高级别的日志。它会对日志进行采样,以减少性能开销和磁盘占用。它还会添加调用者信息,以便追踪日志来源。它还会使用一个默认的生产环境编码器配置,包括时间戳、日志级别、消息等字段。func main() { logger, _ := zap.NewProduction() defer logger.Sync() logger.Info("failed to fetch URL", zap.String("url", "https://www.google.com"), zap.Int("attempt", 3), zap.Duration("backoff", time.Second), ) }go run main.go #{"level":"info","ts":1692070272.7691774,"caller":"zap-demo/main.go:12","msg":"failed to fetch URL","url":"https://www.google.com","attempt":3,"backoff":1} -
NewDevelopment是用于开发环境的预设。它会将日志以人类可读的格式输出到标准错误,并且包含所有日志级别和上下文信息。它不会对日志进行采样,以便查看所有日志细节。它也会添加调用者信息,以及堆栈跟踪信息(如果有)。它还会使用一个默认的开发环境编码器配置,包括颜色化的时间戳、日志级别、消息等字段。func main() { logger, _ := zap.NewDevelopment() defer logger.Sync() logger.Info("failed to fetch URL", zap.String("url", "https://www.google.com"), zap.Int("attempt", 3), zap.Duration("backoff", time.Second), ) }go run main.go #2023-08-15T11:32:45.372+0800 INFO zap-demo/main.go:12 failed to fetch URL {"url": "https://www.google.com", "attempt": 3, "backoff": "1s"}
预设对于小型项目来说没有问题,但大型项目和组织自然需要更多的定制。对于大多数用户来说,zap 的 Config 结构在灵活性和便利性之间取得了恰当的平衡。
func main() {
config := zap.Config{
Level: zap.NewAtomicLevelAt(zap.DebugLevel), // set the minimum enabled log level
Development: false, // set the development/production mode
Encoding: "console", // set the encoding format (console or json)
EncoderConfig: zapcore.EncoderConfig{
MessageKey: "msg", // set the key for the message field
LevelKey: "level", // set the key for the level field
TimeKey: "time", // set the key for the time field
CallerKey: "caller", // set the key for the caller field
EncodeLevel: zapcore.CapitalColorLevelEncoder, // set the level encoder (capitalized and colorized)
EncodeTime: zapcore.ISO8601TimeEncoder, // set the time encoder (ISO 8601 format)
EncodeCaller: zapcore.ShortCallerEncoder, // set the caller encoder (short file name and line number)
},
OutputPaths: []string{"stdout"}, // set the output paths for the logs
ErrorOutputPaths: []string{"stderr"}, // set the output paths for the errors
InitialFields: map[string]interface{}{"app": "zap-example"}, // set the initial fields for the logs
}
logger, _ := config.Build()
defer logger.Sync()
logger.Info("failed to fetch URL",
zap.String("url", "https://www.google.com"),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
}
go run main.go
#2023-08-15T11:55:52.980+0800 INFO zap-demo/main.go:33 failed to fetch URL {"app": "zap-example", "url": "https://www.google.com", "attempt": 3, "backoff": 1000000000}
更特殊的配置(在文件间分割输出、将日志发送到消息队列等)也是可能的,但需要直接使用 go.uber.org/zap/zapcore,如:
func customLevelEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
switch level {
case zapcore.DebugLevel:
enc.AppendString("D")
case zapcore.InfoLevel:
enc.AppendString("I")
case zapcore.WarnLevel:
enc.AppendString("W")
case zapcore.ErrorLevel:
enc.AppendString("E")
case zapcore.DPanicLevel:
enc.AppendString("P")
case zapcore.PanicLevel:
enc.AppendString("P")
case zapcore.FatalLevel:
enc.AppendString("F")
default:
enc.AppendString("?")
}
}
func main() {
errorFile, _ := os.Create("error.log")
defer errorFile.Close()
infoFile, _ := os.Create("info.log")
defer infoFile.Close()
customEncoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
MessageKey: "msg",
LevelKey: "level",
TimeKey: "time",
CallerKey: "caller",
EncodeLevel: customLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
ConsoleSeparator: " | ",
})
errorCore := zapcore.NewCore(
customEncoder,
zapcore.AddSync(errorFile),
zap.ErrorLevel,
)
infoCore := zapcore.NewCore(
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
zapcore.AddSync(infoFile),
zap.InfoLevel,
)
logger := zap.New(zapcore.NewTee(
errorCore,
infoCore,
))
defer logger.Sync()
logger.Error("failed to fetch URL",
zap.String("url", "https://www.google.com"),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
logger.Info("failed to fetch URL",
zap.String("url", "https://www.google.com"),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
}
执行命令 go run main.go,项目根目录下会创建两个文件 error.log 和 info.log,它们的内容如下:
// error.log
2023-08-15T13:09:10.778+0800 | E | failed to fetch URL | {"url": "https://www.google.com", "attempt": 3, "backoff": 1000000000}
// info.log
{"level":"error","ts":1692076150.7782364,"msg":"failed to fetch URL","url":"https://www.google.com","attempt":3,"backoff":1}
{"level":"info","ts":1692076150.793304,"msg":"failed to fetch URL","url":"https://www.google.com","attempt":3,"backoff":1}
本文由 Sue211213 原创,发表于 2023 年 8 月 15 日,转载请注明出处和作者,谢谢!