我们如何在 Gorilla Handlers
的日志记录中获取线程 ID
或由处理程序处理的 http 请求的任何其他唯一 ID?
在 Java 中,当 Tomcat 或其他容器处理多个 http 请求时,线程 id 有助于跟踪各个 http 请求处理的所有日志消息。
Go 中的等价物是什么?给定使用 Gorilla
库开发的 Rest API,如何跟踪处理程序处理中特定 http 请求的所有日志语句?
最佳答案
gorilla/handlers默认情况下,库不提供执行此操作的方法:那里的日志记录函数以 Apache 格式记录,而 Apache 格式不提供此功能。
另请记住,“线程 ID”在这里没有意义 - 您需要一个与 *http.Request
关联的请求 ID。
您可以编写自己的 RequestID 中间件,该中间件创建一个 ID 并将其存储在请求上下文中,以便其他中间件/处理程序根据需要进行检索:
package main
import (
"crypto/rand"
"encoding/base64"
"net/http"
"github.com/gorilla/context"
)
const ReqID string = "gorilla.RequestID"
// RequestID wraps handlers and makes a unique (32-byte) request ID available in
// the request context.
// Example:
// http.Handle("/", RequestID(LoggingHandler(YourHandler)))
//
// func LoggingHandler(h http.Handler) http.Handler {
// fn := func(w http.ResponseWriter, r *http.Request) {
// h.ServeHTTP(w, r)
//
// id := GetRequestID(r)
// log.Printf("%s | %s", id, r.RemoteAddr)
// }
//
// return http.HandlerFunc(fn)
// }
func RequestID(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
b := make([]byte, 8)
_, err = rand.Read(&b)
if err != nil {
http.Error(w, http.StatusText(500), 500)
return
}
base64ID := base64.URLEncoding.EncodeToString(b)
context.Set(r, ReqID, base64ID)
h.ServeHTTP(w, r)
// Clear the context at the end of the request lifetime
context.Clear(r)
}
return http.HandlerFunc(fn)
}
func GetRequestID(r *http.Request) string {
if v, ok := context.GetOK(r, ReqID); ok {
if id, ok := v.(string); ok {
return id
}
}
return ""
}
请记住,上面的代码未经测试。这是我在 Playground 中凭空写出来的,所以如果有错误请告诉我。
除了这个基本示例之外,您还可以考虑进行改进:
- 在 ID 中添加主机名前缀 - 如果您要聚合来自多个进程/计算机的日志,这会很有帮助)
- 提供时间戳或递增整数作为最终 ID 的一部分,以帮助跟踪一段时间内的请求
- 对其进行基准测试。
请注意,在极高的负载下(例如数万个请求/秒 - 每天数千万次点击),这可能性能不佳,但不太可能成为超过 99% 的用户的瓶颈。
PS:我可能会考虑在某个时候在 gorilla/handlers 库中提供 handlers.RequestID
实现 - 如果您想看到它,请在存储库上提出问题,我会'我会看看我是否能找到时间对上述内容进行更完整的实现。
关于logging - 在 Gorilla Handler 中记录线程 ID,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33947545/