【问题标题】:Go log thread id in Gorilla Handler在 Gorilla Handler 中记录线程 id
【发布时间】:2016-03-01 01:48:07
【问题描述】:

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

【问题讨论】:

    标签: logging go gorilla


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      gorilla/handlers 库默认情况下不提供执行此操作的方法:那里的日志记录函数以 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 ""
      }
      

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

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

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

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

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

      【讨论】:

      • 有了这个,我们需要记录的每个地方,我们都需要传递请求实例来检索ID。这很痛苦。从处理程序中,我们可以调用许多函数,这些函数可能是实用函数/数据库方法等。所有这些方法都可以修改为接受请求实例。
      • 不再使用 gorilla/context,它早在 context.Context 存在之前就已经诞生了,不能很好地与 http.Request.WithContext 的请求的浅拷贝(添加到 net/http从 1.7 开始)执行。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多