blendle/zapdriver
GitHub: blendle/zapdriver
一个基于 Zap 的 Go 日志库,专门为 Google Cloud Stackdriver 提供结构化日志记录和错误上报支持。
Stars: 178 | Forks: 45
# :zap: Zapdriver
极速、基于 [Zap][zap] 的 [Stackdriver][stackdriver] 日志记录。
## 用法
本包提供了三个基础组件,以支持 Stackdriver 的全系列结构化日志记录功能:
* [特殊用途日志字段](#special-purpose-logging-fields)
* [预配置的 Stackdriver 优化编码器](#pre-configured-stackdriver-optimized-encoder)
* [自定义 Stackdriver Zap core](#custom-stackdriver-zap-core)
* [使用 Error Reporting](#using-error-reporting)
上述组件可以单独使用,但作为起步,你可以创建一个包含上述所有功能的新 Zap logger:
```
logger, err := zapdriver.NewProduction() // with sampling
logger, err := zapdriver.NewDevelopment() // with `development` set to `true`
```
上述函数返回一个指向 `zap.Logger` 对象的指针,因此你可以像往常一样使用 [Zap][zap],只不过它现在会以正确的 [Stackdriver][stackdriver] 格式进行记录。
你也可以创建一个配置结构体(struct),并据此构建你的 logger:
```
config := zapdriver.NewProductionConfig()
config := zapdriver.NewDevelopmentConfig()
```
或者,获取 Zapdriver 编码器,并以此构建你自己的配置结构体:
```
encoder := zapdriver.NewProductionEncoderConfig()
encoder := zapdriver.NewDevelopmentEncoderConfig()
```
请继续阅读,以详细了解可用的 Stackdriver 特定日志字段,以及如何使用上述组件。
### 特殊用途日志字段
你可以使用以下字段在日志条目中添加额外信息。Stackdriver 会解析这些字段,从而让你更轻松地查询日志,或在 Stackdriver 监控界面中使用这些日志详情。
* [`HTTP`](#http)
* [`Label`](#label)
* [`SourceLocation`](#sourcelocation)
* [`Operation`](#operation)
* [`TraceContext`](#tracecontext)
#### HTTP
你可以使用以下字段记录 HTTP 请求/响应周期:
```
HTTP(req *HTTPPayload) zap.Field
```
你可以手动构建请求 payload:
```
req := &HTTPPayload{
RequestMethod: "GET",
RequestURL: "/",
Status: 200,
}
```
或者,你可以根据可用的请求和响应对象自动生成该结构体:
```
NewHTTP(req *http.Request, res *http.Response) *HTTPPayload
```
如果在记录日志时请求或响应对象其中有不可用项,你可以随意为其传入 `nil`。如果传入了 `nil`,任何依赖于其中某一个的字段都将被省略。
请注意,有些字段不会被请求或响应对象填充,需要手动设置:
* `ServerIP string`
* `Latency string`
* `CacheLookup bool`
* `CacheHit bool`
* `CacheValidatedWithOriginServer bool`
* `CacheFillBytes string`
如果你不需要这些字段,最快捷的起步方式如下:
```
logger.Info("Request Received.", zapdriver.HTTP(zapdriver.NewHTTP(req, res)))
```
#### Label
你可以按如下方式向你的 payload 中添加“label”:
```
Label(key, value string) zap.Field
```
请注意,在底层实现中,这会将键名设置为 `labels.`。你需要使用 `zapdriver.Core` core,才能将其转换为 Stackdriver 能够识别这些 label 的正确格式。
有关更多详细信息,请参阅“自定义 Stackdriver Zap core”。
如果你有理由不使用所提供的 Core,你仍然可以使用以下可用函数将 label 包装在正确的 `labels` 命名空间中:
```
Labels(fields ...zap.Field) zap.Field
```
如下所示:
```
logger.Info(
"Did something.",
zapdriver.Labels(
zapdriver.Label("hello", "world"),
zapdriver.Label("hi", "universe"),
),
)
```
同样,如果你使用了提供的 Zap Core,则不需要在 `Labels` 中包装 `Label` 调用。
#### SourceLocation
你可以在日志行中添加源代码位置,以便 Stackdriver 捕获。
请注意,你可以手动进行设置,或者使用 `zapdriver.Core` 自动添加此项。如果你手动进行了设置,_并且_ 使用了 `zapdriver.Core`,那么手动的调用堆栈将优先于自动生成的堆栈而被保留。
```
SourceLocation(pc uintptr, file string, line int, ok bool) zap.Field
```
请注意,该函数签名等同于 `runtime.Caller()` 的返回值。这允许你在一个位置捕获堆栈帧,而在另一个位置进行记录,如下所示:
```
pc, file, line, ok := runtime.Caller(0)
// do other stuff...
logger.Error("Something happened!", zapdriver.SourceLocation(pc, file, line, ok))
```
如果你使用了 `zapdriver.Core`,上述用例是你唯一需要手动设置源代码位置的用例。在所有其他情况下,你可以直接省略此字段,它将在触发日志行的位置使用堆栈帧自动添加。
如果你没有使用 `zapdriver.Core`,但仍然想在触发的日志行的帧处添加源代码位置,你可以这样做:
```
logger.Error("Something happened!", zapdriver.SourceLocation(runtime.Caller(0)))
```
#### Operation
`Operation` 日志字段允许你将日志行分组为应用程序执行的单个“操作”:
```
Operation(id, producer string, first, last bool) zap.Field
```
对于属于同一操作的一对日志,你应在它们之间使用相同的 `id`。`producer` 是一个任意标识符,在你所有应用程序的所有日志中应当是全局唯一的(这意味着它可能应该是当前应用程序的唯一名称)。你应该将该操作的第一条日志的 `first` 设为 true,并将最后一条日志的 `last` 设为 true。
```
logger.Info("Started.", zapdriver.Operation("3g4d3g", "my-app", true, false))
logger.Debug("Progressing.", zapdriver.Operation("3g4d3g", "my-app", false, false))
logger.Info("Done.", zapdriver.Operation("3g4d3g", "my-app", false, true))
```
除了定义“开始”和“结束”布尔值之外,你还可以使用这三个便捷函数:
```
OperationStart(id, producer string) zap.Field
OperationCont(id, producer string) zap.Field
OperationEnd(id, producer string) zap.Field
```
#### TraceContext
你可以在日志行中添加 trace context 信息,以便 Stackdriver 捕获。
```
TraceContext(trace string, spanId string, sampled bool, projectName string) []zap.Field
```
如下所示:
```
logger.Error("Something happened!", zapdriver.TraceContext("105445aa7843bc8bf206b120001000", "0", true, "my-project-name")...)
```
### 预配置的 Stackdriver 优化编码器
Stackdriver 编码器将所有 Zap 日志级别映射为相应的 [Stackdriver 支持的级别][levels]:
它还设置了一些默认键以使用[正确的名称][names],例如 `timestamp`、`severity` 和 `message`。
如果你想手动构建 Zap logger 配置,可以使用此编码器:
```
zapdriver.NewProductionEncoderConfig()
```
为了保持一致性,还提供了 `zapdriver.NewDevelopmentEncoderConfig()`,不过它目前返回的是完全相同的编码器。
### 自定义 Stackdriver Zap core
本包中包含一个自定义 Zap core,以支持一些特殊的用例。
首先,如果你使用 `zapdriver.NewProduction()`(或 `NewDevelopment`),你已经启用了此 core,因此一切_直接可用_ ™。
有两种用例需要使用此 core:
1. 如果你使用 `zapdriver.Label("hello", "world")`,它最初会以键名 `labels.hello` 和值 `world` 出现在你的日志中。此时如果你有两个 label,你可能还会有值为 `universe` 的 `labels.hi`。这在原样状态下就能工作,但要让 Stackdriver 将其正确解析为真正的“label”,你需要使用 Zapdriver core,以便将这两个字段都重写为使用 `labels` 命名空间,并在该命名空间内使用 `hello` 和 `hi` 键。这是自动完成的。
2. 如果你不想在每次日志调用时都使用 `zapdriver.SourceLocation()`,你可以使用此 core 将源代码位置自动添加到每个日志条目中。
在构建 logger 时,你可以按如下方式注入 Zapdriver core:
```
config := &zap.Config{}
logger, err := config.Build(zapdriver.WrapCore())
```
### 使用 Error Reporting
要使用 StackDriver 的 Error Reporting 工具报告错误,日志行需要遵循 [Error Reporting][errorreporting] 文档中描述的独立日志格式。
最简单的方法是使用 `NewProductionWithCore`:
```
logger, err := zapdriver.NewProductionWithCore(zapdriver.WrapCore(
zapdriver.ReportAllErrors(true),
zapdriver.ServiceName("my service"),
))
```
为了保持一致性,还提供了 `zapdriver.NewDevelopmentWithCore()`
如果你正在构建自定义 logger,你可以使用 `WrapCore()` 来配置 driver core:
```
config := &zap.Config{}
logger, err := config.Build(zapdriver.WrapCore(
zapdriver.ReportAllErrors(true),
zapdriver.ServiceName("my service"),
))
```
通过这种方式配置,每个错误日志条目都将被报告给 Stackdriver 的 Error Reporting 工具。
#### 手动报告错误
如果你不希望每个错误都被报告,你可以手动将 `ErrorReport()` 附加到日志调用中:
```
logger.Error("An error to be reported!", zapdriver.ErrorReport(runtime.Caller(0)))
// Or get Caller details
pc, file, line, ok := runtime.Caller(0)
// do other stuff... and log elsewhere
logger.Error("Another error to be reported!", zapdriver.ErrorReport(pc, file, line, ok))
```
请记住,ErrorReport 需要在日志条目中附加一个 ServiceContext。如果你没有使用 `WrapCore` 进行配置,错误报告将使用服务名称 `unknown` 进行附加。为防止这种情况发生,请在使用 logger 之前(或之时)配置你的 core 或附加 service context:
```
logger.Error(
"An error to be reported!",
zapdriver.ErrorReport(runtime.Caller(0)),
zapdriver.ServiceContext("my service"),
)
// Or permanently attach it to your logger
logger = logger.With(zapdriver.ServiceContext("my service"))
// and then use it
logger.Error("An error to be reported!", zapdriver.ErrorReport(runtime.Caller(0)))
```
标签:ETW劫持, EVTX分析, Go, Ruby工具, Stackdriver, Zap, 日志审计, 日志记录, 运维监控