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, 日志审计, 日志记录, 运维监控