felixge/httpsnoop
GitHub: felixge/httpsnoop
Go 语言库,通过安全地包装 http.ResponseWriter 来捕获 HTTP 请求的响应时间、字节数和状态码等监控指标。
Stars: 1162 | Forks: 48
# httpsnoop
httpsnoop 包提供了一种简便的方法,用于从您应用程序的
http.Handlers 中捕获与 http 相关的指标(例如
响应时间、写入字节数和 http 状态码)。
实现此功能需要对 http.ResponseWriter 接口进行非同寻常的包装,
该接口也面向对更底层 API 感兴趣的用户开放。
[](https://pkg.go.dev/github.com/felixge/httpsnoop)
[](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开发, 中间件, 可观测性, 性能监控, 日志审计