Zap中文文档
本文最后更新于 148 天前,其中的信息可能已经有所发展或是发生改变。如有疑问或错误请反馈至邮箱super.lucky.qu@gmail.com

前言

目前没有找到Go-Uber/Zap的一个合适的中文文档,所以自己借助GPT和翻译软件自译了英文文档,不能保证完全准确,建议对照英语文档阅读:官方英语文档

README

安装

go get -u go.uber.org/zap

请注意,zap 仅支持 Go 的最新的两个版本。

快速入门

在性能要求较高但并非关键的场景中,建议使用 SugaredLogger。它比其他结构化日志记录包快 4 到 10 倍,并支持结构化和 printf 风格的 API。

logger, _ := zap.NewProduction()
defer logger.Sync() // 刷新缓冲区(如果有的话)
sugar := logger.Sugar()
sugar.Infow("failed to fetch URL",
  // 使用松散类型的键值对形式提供结构化上下文信息
  "url", url,
  "attempt", 3,
  "backoff", time.Second,
)
sugar.Infof("Failed to fetch URL: %s", url) // 格式化字符串输出日志

当性能和类型安全至关重要时,请使用 Logger。它比 SugaredLogger 更快,占用的内存更少,但仅支持结构化日志记录。

logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("failed to fetch URL",
  // 使用强类型字段值提供结构化上下文信息
  zap.String("url", url),
  zap.Int("attempt", 3),
  zap.Duration("backoff", time.Second),
)

有关更多详细信息,请参阅文档和常见问题解答。

性能

对于在关键路径中进行日志记录的应用程序,基于反射的序列化和字符串格式化是极其昂贵的——它们占用大量 CPU 资源并进行许多小的内存分配。换句话说,使用 encoding/json 和 fmt.Fprintf 来记录大量的 interface{} 类型,会使应用程序变得缓慢。

Zap 采用了不同的方法。它包括一个无反射、零分配的 JSON 编码器,且基础的 Logger 尽可能避免序列化开销和内存分配。通过在这个基础上构建高层的 SugaredLogger,zap 允许用户根据需要选择何时需要计算每次分配的成本,何时又希望使用一个更熟悉、松散类型的 API。

根据其自有的基准测试套件的测量结果,zap 不仅在性能上超过了类似的结构化日志记录包——它还比标准库更快。像所有基准测试一样,请保持适当的怀疑态度。

开发状态:稳定

所有 API 已经最终确定,1.x 系列版本中不会做破坏性更改。使用符合语义化版本管理的依赖系统的用户应将 zap 锁定为 ^1。

贡献

我们鼓励并支持一个活跃、健康的贡献者社区——包括你!详情请参见贡献指南和行为准则。zap 的维护者会关注问题和拉取请求,但你也可以将任何不当行为报告给 oss-conduct@uber.com。这个邮件列表是一个私密、安全的空间;即使是 zap 的维护者也无法访问,因此请毫不犹豫地要求我们保持高标准。

根据 MIT 许可证发布

1特别需要注意的是,我们可能是在对比略旧版本的其他包进行基准测试。版本已经在 benchmarks/go.mod 文件中锁定。

文档正文

概览

zap 包提供了快速、结构化、分级的日志记录功能。

对于在关键路径中进行日志记录的应用程序,基于反射的序列化和字符串格式化是极其昂贵的——它们占用大量 CPU 资源并进行许多小的内存分配。换句话说,使用 json.Marshal 和 fmt.Fprintf 来记录大量的 interface{} 类型,会使应用程序变得缓慢。

Zap 采用了不同的方法。它包括一个无反射、零分配的 JSON 编码器,且基础的 Logger 尽可能避免序列化开销和内存分配。通过在这个基础上构建高层的 SugaredLogger,zap 允许用户根据需要选择何时需要计算每次分配的成本,何时又希望使用一个更熟悉、松散类型的 API。

选择一个日志记录器

在性能重要,但不至于关键的场合,使用 SugaredLogger。它比其他结构化日志记录包快 4-10 倍,并支持结构化日志记录和 printf 风格的日志记录。像 log15 和 go-kit 一样,SugaredLogger 的结构化日志记录 API 是松散类型的,并接受变长的键值对参数。(对于更复杂的用例,它们也支持强类型字段——有关详细信息,请参阅 SugaredLogger.With 文档。)

sugar := zap.NewExample().Sugar()
defer sugar.Sync()
sugar.Infow("failed to fetch URL",
  "url", "http://example.com",
  "attempt", 3,
  "backoff", time.Second,
)
sugar.Infof("failed to fetch URL: %s", "http://example.com")

默认情况下,日志记录器是无缓冲的。然而,由于 zap 的低级 API 允许缓冲,因此在让进程退出之前调用 Sync 是一个好习惯。

在那些每微秒和每次分配都至关重要的特殊场景中,使用 Logger。它比 SugaredLogger 更快,且分配的内存更少,但它只支持强类型的结构化日志记录。

logger := zap.NewExample()
defer logger.Sync()
logger.Info("failed to fetch URL",
  zap.String("url", "http://example.com"),
  zap.Int("attempt", 3),
  zap.Duration("backoff", time.Second),
)


在 Logger 和 SugaredLogger 之间进行选择不需要在整个应用程序中做出决定:在两者之间转换是简单且低成本的。

logger := zap.NewExample()
defer logger.Sync()
sugar := logger.Sugar()
plain := sugar.Desugar()

配置 Zap

构建 Logger 的最简单方法是使用 zap 提供的预设函数:NewExample、NewProduction 和 NewDevelopment。这些预设通过一次函数调用构建一个日志记录器:

logger, err := zap.NewProduction()
if err != nil {
  log.Fatalf("无法初始化 zap 日志记录器: %v", err)
}
defer logger.Sync()

对于小型项目来说,预设足够了,但对于大型项目和组织来说,自然需要更多的定制。对于大多数用户来说,zap 的 Config 结构体在灵活性和便利性之间达到了一个合适的平衡。有关示例代码,请参阅包级别的 BasicConfiguration 示例。

一些不常见的配置(如将输出分配到文件、将日志发送到消息队列等)是可行的,但需要直接使用 go.uber.org/zap/zapcore。有关示例代码,请参阅包级别的 AdvancedConfiguration 示例。

扩展 Zap

zap 包本身是对 go.uber.org/zap/zapcore 中接口的相对简洁的封装。扩展 zap 以支持新的编码格式(例如 BSON)、新的日志输出目标(例如 Kafka)或其他更复杂的功能(例如异常聚合服务,如 Sentry 或 Rollbar),通常需要实现 zapcore.Encoder、zapcore.WriteSyncer 或 zapcore.Core 接口。有关详细信息,请参阅 zapcore 的文档。

同样,包的作者可以使用 zapcore 包中的高性能 Encoder 和 Core 实现来构建自己的日志记录器。

常见问题解答

涵盖从安装错误到设计决策的常见问题解答,可以在以下链接找到:https://github.com/uber-go/zap/blob/master/FAQ.md

示例(高级配置)

// 绑定的 Config 结构体仅支持最常见的配置选项。
// 更复杂的需求,比如将日志分割到多个文件或写入非文件输出,
// 需要使用 zapcore 包。
//
// 在这个例子中,假设我们同时将日志发送到 Kafka,并写入控制台。
// 我们希望对控制台输出和 Kafka 主题进行不同的编码,
// 并且希望对高优先级日志进行特殊处理。

// 首先,定义我们的级别处理逻辑。
highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
    return lvl >= zapcore.ErrorLevel
})
lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
    return lvl < zapcore.ErrorLevel
})

// 假设我们有两个 Kafka 主题的客户端。客户端实现了
// zapcore.WriteSyncer,且支持并发使用。(如果它们只
// 实现了 io.Writer,我们可以使用 zapcore.AddSync 来添加一个不操作的 Sync
// 方法。如果它们不支持并发使用,我们可以通过 zapcore.Lock 添加一个保护
// 的互斥锁。)
topicDebugging := zapcore.AddSync(io.Discard)
topicErrors := zapcore.AddSync(io.Discard)

// 高优先级输出也应该发送到标准错误,低优先级输出应该发送到标准输出。
consoleDebugging := zapcore.Lock(os.Stdout)
consoleErrors := zapcore.Lock(os.Stderr)

// 优化 Kafka 输出以便机器消费,控制台输出则优化为人类操作员使用。
kafkaEncoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())

// 将输出、编码器和级别处理函数组合成 zapcore.Cores,然后将四个核心合并。
core := zapcore.NewTee(
    zapcore.NewCore(kafkaEncoder, topicErrors, highPriority),
    zapcore.NewCore(consoleEncoder, consoleErrors, highPriority),
    zapcore.NewCore(kafkaEncoder, topicDebugging, lowPriority),
    zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority),
)

// 从 zapcore.Core 创建 Logger 非常简单。
logger := zap.New(core)
defer logger.Sync()
logger.Info("构造了一个日志记录器")

示例(基本配置)

// 对于一些用户,NewProduction、NewDevelopment 和 NewExample 构造函数提供的预设值可能不适用。
// 对于大多数这些用户,捆绑的 Config 结构体提供了合适的灵活性和便利性平衡。
// (对于更复杂的需求,参见 AdvancedConfiguration 示例。)
//
// 请参阅 Config 和 zapcore.EncoderConfig 的文档,以了解所有可用的选项。
rawJSON := []byte(`{
  "level": "debug",
  "encoding": "json",
  "outputPaths": ["stdout", "/tmp/logs"],
  "errorOutputPaths": ["stderr"],
  "initialFields": {"foo": "bar"},
  "encoderConfig": {
    "messageKey": "message",
    "levelKey": "level",
    "levelEncoder": "lowercase"
  }
}`)

var cfg zap.Config
if err := json.Unmarshal(rawJSON, &cfg); err != nil {
    panic(err)
}
logger := zap.Must(cfg.Build())
defer logger.Sync()

logger.Info("logger construction succeeded")

示例(预设)

// 使用 zap 的预设构造函数是最简单的方式来熟悉这个包,
// 但它们不允许太多的自定义。
logger := zap.NewExample() // 或者 NewProduction,或 NewDevelopment
defer logger.Sync()

const url = "http://example.com"

// 在大多数情况下,使用 SugaredLogger。它比大多数其他结构化日志包快 4-10 倍,并且有一个熟悉的、松散类型的 API。
sugar := logger.Sugar()
sugar.Infow("无法获取 URL。",
    // 以松散类型的键值对形式传递结构化上下文。
    "url", url,
    "attempt", 3,
    "backoff", time.Second,
)
sugar.Infof("无法获取 URL: %s", url)

// 在极少数每微秒都很重要的情况下,使用 Logger。
// 它比 SugaredLogger 更快,但只支持结构化日志记录。
logger.Info("无法获取 URL。",
    // 以强类型字段形式传递结构化上下文。
    zap.String("url", url),
    zap.Int("attempt", 3),
    zap.Duration("backoff", time.Second),
)

索引

常数

变量

函数

类型定义

上一篇
下一篇