Go gorilla 记录每个请求持续时间和状态代码

问题描述 投票:0回答:3

我有一个用 Go1.13 编写的 REST API,使用 gorilla/mux v1.7.3 和 gorilla/context v1.1.1.

我想记录每个请求持续时间和状态代码。我已经有一个基本的工作版本,但我想知道是否有更清洁的选项。

我有大约 20 个 HTTP 调用。让我们以其中一个为例:

client.HandleFunc("/register", register.Handle).Methods("POST") 

以及

register.Handle
的签名是:

func Handle(w http.ResponseWriter, r *http.Request) { ... }

所以为了记录每个请求持续时间和状态代码,我简单地用“日志处理程序”包装了方法处理程序

register.Handle

client.HandleFunc("/register", log.HandleRequestWithLog(register.Handle)).Methods("POST")

when

log.HandleRequestWithLog
简单地计算函数执行的时间,并使用返回的状态代码记录它(使用 Azure 指标记录):

func HandleRequestWithLog(h func(http.ResponseWriter, *http.Request) int) http.HandlerFunc {
    return func(writer http.ResponseWriter, request *http.Request) {
        startTime := time.Now()
        statusCode := h(writer, request)
        duration := time.Now().Sub(startTime)
        trace := appinsights.NewRequestTelemetry(request.Method, request.URL.Path, duration, string(rune(statusCode)))
        trace.Timestamp = time.Now()
        client.Track(trace)
    }
}

所以为了提取状态码,我需要每个 HTTP 方法都返回状态码。所以我把签名改成了:

func Handle(w http.ResponseWriter, r *http.Request) int { .. }

我想知道是否有比为每个请求返回 HTTP 状态代码更好的选择。另外,如果我想从请求中获得更多信息,会发生什么情况?我也会返回这个值吗?

rest go logging gorilla
3个回答
5
投票

无需更改 HTTP 处理程序签名,您可以定义一个实现

LogResponseWriter
http.ResponseWriter
来跟踪响应数据(状态、正文等)。

  1. 让我们定义
    LogResponseWriter
type LogResponseWriter struct {
    http.ResponseWriter
    statusCode int
    buf        bytes.Buffer
}

func NewLogResponseWriter(w http.ResponseWriter) *LogResponseWriter {
    return &LogResponseWriter{ResponseWriter: w}
}

func (w *LogResponseWriter) WriteHeader(code int) {
    w.statusCode = code
    w.ResponseWriter.WriteHeader(code)
}

func (w *LogResponseWriter) Write(body []byte) (int, error) {
    w.buf.Write(body)
    return w.ResponseWriter.Write(body)
}
  1. 定义
    LogMiddleware
    (这里,我使用std log和
    mux.MiddlewareFunc
type LogMiddleware struct {
    logger *log.Logger
}

func NewLogMiddleware(logger *log.Logger) *LogMiddleware {
    return &LogMiddleware{logger: logger}
}

func (m *LogMiddleware) Func() mux.MiddlewareFunc {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            startTime := time.Now()

            logRespWriter := NewLogResponseWriter(w)
            next.ServeHTTP(logRespWriter, r)

            m.logger.Printf(
                "duration=%s status=%d body=%s",
                time.Since(startTime).String(),
                logRespWriter.statusCode,
                logRespWriter.buf.String())
        })
    }
}
  1. main.go
    中启动服务器。在这里,我们可以在我们的路由器中使用
    LogMiddleware
func main() {
    logger := log.New(os.Stdout, "", log.LstdFlags)

    router := mux.NewRouter()
    router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        time.Sleep(1 * time.Second)

        w.WriteHeader(http.StatusOK)
        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte(`{"msg": "hello world"}`))
    })

    logMiddleware := NewLogMiddleware(logger)
    router.Use(logMiddleware.Func())

    logger.Fatalln(http.ListenAndServe(":8080", router))
}

我访问时的日志结果

http://localhost:8080

2020/10/08 00:51:38 duration=1.003927394s status=200 body={"msg": "hello world"}
  1. 跟进:如果我想从请求中获得更多信息会怎样?

在您的日志中间件中,您可以读取请求标头和正文(来自

r *http.Request
),然后将它们与持续时间和响应数据一起记录下来。请注意,您应该重新分配请求正文,以便您的实际处理程序可以读取请求正文。


0
投票

这是对 https://stackoverflow.com/a/64249969/1630083(从下面)的回答。

我服务的 websockets 有这个问题:

http: panic serving 172.21.0.10:44614: interface conversion: *apiserver.LogResponseWriter is not http.Hijacker: missing method Hijack

但是代码来自:http://www.inanzzz.com/index.php/post/q7bs/access-log-middleware-in-golang

// Overrides http.Flusher type.
func (w *LogResponseWriter) Flush() {
    if fl, ok := w.ResponseWriter.(http.Flusher); ok {
        if w.statusCode == 0 {
            w.WriteHeader(http.StatusOK)
        }
        fl.Flush()
    }
}

// Overrides http.Hijacker type.
func (w *LogResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
    hj, ok := w.ResponseWriter.(http.Hijacker)
    if !ok {
        return nil, nil, fmt.Errorf("the hijacker interface is not supported")
    }
    return hj.Hijack()
}

我现在开始工作了,谢谢!


0
投票

OP 可能对这个库感兴趣:

https://github.com/felixge/httpsnoop

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.