在 Gorilla Handler 中记录线程 ID

Go log thread id in Gorilla Handler

我们如何在 Gorilla Handlers 中获取处理程序正在处理的 http 请求的 thread id 或任何其他唯一 ID?
在 Java 中,当 Tomcat 或其他容器处理多个 http 请求时,线程 id 有助于跟踪各个 http 请求处理的所有日志消息。
Go 中的等价物是什么?给定一个使用 Gorilla 库开发的 Rest API,我如何跟踪处理程序处理中特定 http 请求的所有日志语句?

gorilla/handlers 库默认不提供执行此操作的方法:那里的日志记录功能以 Apache 格式记录,不提供此功能。

另请记住,"thread ID" 在这里没有意义 - 您需要一个与 *http.Request 关联的 request ID。

您可以编写自己的 RequestID 中间件,创建 ID 并存储在请求上下文中,供其他 middleware/handlers 根据需要检索:

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 ""
}

请记住,上面的代码未经测试。在操场上写下了我的头顶,所以如果有错误请告诉我。

除了这个基本示例之外,您还可以考虑改进:

  • 在 ID 前加上主机名 - 如果您要从多个 processes/machines)
  • 聚合日志,这很有用
  • 提供时间戳或递​​增整数作为最终 ID 的一部分,以帮助跟踪随时间变化的请求
  • 对其进行基准测试。

请注意,在极高负载下(例如每天数万次 req/s - 数千万次点击),这可能性能不佳,但不太可能成为 > 99% 的瓶颈用户。

PS:我可能会在某个时候考虑在 gorilla/handlers 库中提供一个 handlers.RequestID 实现——如果你想看到它,请在 repo 上提出问题我会看看是否能抽出时间对上述内容进行更完整的处理。

基于 https://groups.google.com/forum/#!searchin/golang-nuts/Logging$20http$20thread/golang-nuts/vDNEH3_vMXQ/uyqGEwdchzgJ,ThreadLocal 概念在 Go 中是不可能的。

每个需要日志记录的地方,都需要传入 http Request 实例,以便可以检索与请求关联的上下文,并且可以从该上下文中获取唯一 ID要求。 但是将 Request 实例传递给所有 layers/methods.

是不切实际的