felixge/httpsnoop

GitHub: felixge/httpsnoop

Go 语言库,通过安全地包装 http.ResponseWriter 来捕获 HTTP 请求的响应时间、字节数和状态码等监控指标。

Stars: 1162 | Forks: 48

# httpsnoop httpsnoop 包提供了一种简便的方法,用于从您应用程序的 http.Handlers 中捕获与 http 相关的指标(例如 响应时间、写入字节数和 http 状态码)。 实现此功能需要对 http.ResponseWriter 接口进行非同寻常的包装, 该接口也面向对更底层 API 感兴趣的用户开放。 [![Go Reference](https://pkg.go.dev/badge/github.com/felixge/httpsnoop.svg)](https://pkg.go.dev/github.com/felixge/httpsnoop) [![Build Status](https://static.pigsec.cn/wp-content/uploads/repos/cas/d9/d9e65752d8447236a5b8597cc4c3c312cde238bc6d227d29513f49e9e1695fd0.svg)](https://github.com/felixge/httpsnoop/actions/workflows/main.yaml) ## 用法示例 ``` // myH is your app's http handler, perhaps a http.ServeMux or similar. var myH http.Handler // wrappedH wraps myH in order to log every request. wrappedH := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetrics(myH, w, r) log.Printf( "%s %s (code=%d dt=%s written=%d)", r.Method, r.URL, m.Code, m.Duration, m.Written, ) }) http.ListenAndServe(":8080", wrappedH) ``` ## 为什么会有这个包 对应用程序的 http.Handler 进行监测(埋点)出乎意料地困难。 然而,如果您在搜索引擎中搜索例如“capture ResponseWriter status code”,您会发现 大量的建议和代码示例表明这是一项相当简单的任务。不幸的是, 到目前为止我所看到的一切都极有可能 导致您的应用程序出现问题。 主要问题在于,`http.ResponseWriter` 通常实现了额外的 接口,例如 `http.Flusher`、`http.CloseNotifier`、`http.Hijacker`、`http.Pusher` 和 `io.ReaderFrom`。因此,仅仅将 `http.ResponseWriter` 包装在同样实现了 `http.ResponseWriter` 接口的自定义结构体中的这种简单做法, 会隐藏上述提到的额外接口。这极有可能 在任何复杂的应用程序中引入难以察觉的 bug。 我见过的另一种方法是返回一个实现了 上述所有接口的结构体。然而,这也是有问题的,因为当底层的 `http.ResponseWriter` 没有实现这些接口时,很难模拟其中某些接口的行为。这同样也很危险, 因为应用程序可能仅仅由于 检测到了这些额外接口的存在,就选择改变其运行方式。 这个包通过检查 `http.ResponseWriter` 实现了哪些额外的 接口来解决这个问题,并返回一个实现了 完全相同接口集合的包装版本。 此外,这个包还能妥善处理一些边缘情况,例如未调用 `WriteHeader`,或者多次调用它, 以及对 `http.ResponseWriter` 方法的并发调用, 甚至是在被包装的 `ServeHTTP` 已经返回之后发生的调用。 不幸的是,这个包本身也并非完美无缺。它可能 依然缺少 Go 核心库提供的一些接口(如果您发现了,请告诉我), 并且对于将自身额外接口混入其中的应用程序,它无法 正常工作。不过,您可以使用 `httpsnoop.Unwrap(w)` 来访问底层的 `http.ResponseWriter`,并将结果类型断言为其它的接口。 然而,希望上面的解释已经足以打消您自己动手解决 这个问题的念头。httpsnoop 仍然有可能导致您的应用程序出问题, 但至少它会尽可能地避免这种情况。 不管怎样,这里真正的问题在于,将额外的接口隐藏在 `http.ResponseWriter` 中是一个有争议的设计选择,但这可能要深究到 Go 语言规范本身。不过没关系,相比其他选择, 我还是更喜欢 Go ;). ## 性能 ``` BenchmarkBaseline-8 20000 94912 ns/op BenchmarkCaptureMetrics-8 20000 95461 ns/op ``` 正如您所看到的,在我的机器上,对普通的 http.Handler 使用 `CaptureMetrics` 会为每个 http 请求引入 大约 ~500 ns 的开销。然而,误差范围 似乎比这还要大,因此可以合理地 认为 `CaptureMetrics` 引入的开销绝对是 微不足道的。 ## 许可证 MIT
标签:API集成, EVTX分析, Go, Ruby工具, Syscall, Web开发, 中间件, 可观测性, 性能监控, 日志审计