Go gorilla 记录每个请求持续时间和状态码
Go gorilla log each request duration and status code
我有一个 REST API 使用 gorilla/mux v1.7.3 和 gorilla/context v1.1.1 在 Go1.13 中编写。
我想记录每个请求持续时间和状态代码。我已经有一个基本的工作版本,但我想知道是否有更简洁的选项。
我有大约 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")
当 log.HandleRequestWithLog
只是计算函数执行的时间,并使用 returned 状态代码记录它(使用 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 方法来 return 状态代码。所以我把签名改为:
func Handle(w http.ResponseWriter, r *http.Request) int { .. }
我想知道是否有比 return 为每个请求输入 HTTP 状态代码更好的选择。另外,如果我想从请求中获得更多信息,会发生什么情况?我也要return这个值?
您可以定义实现 http.ResponseWriter
的 LogResponseWriter
来跟踪响应数据(状态、body 等),而不是更改 HTTP 处理程序签名。
- 让我们定义
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)
}
- 定义
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())
})
}
}
- 在
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"}
- 跟进:如果我想从请求中获取更多信息,会发生什么情况?
在您的日志中间件中,您可以读取请求 header 和 body(来自 r *http.Request
),然后使用持续时间和响应数据记录它们。请注意,您应该 reassign the request body 以便您的实际处理程序可以读取请求 body.
我有一个 REST API 使用 gorilla/mux v1.7.3 和 gorilla/context v1.1.1 在 Go1.13 中编写。
我想记录每个请求持续时间和状态代码。我已经有一个基本的工作版本,但我想知道是否有更简洁的选项。
我有大约 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")
当 log.HandleRequestWithLog
只是计算函数执行的时间,并使用 returned 状态代码记录它(使用 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 方法来 return 状态代码。所以我把签名改为:
func Handle(w http.ResponseWriter, r *http.Request) int { .. }
我想知道是否有比 return 为每个请求输入 HTTP 状态代码更好的选择。另外,如果我想从请求中获得更多信息,会发生什么情况?我也要return这个值?
您可以定义实现 http.ResponseWriter
的 LogResponseWriter
来跟踪响应数据(状态、body 等),而不是更改 HTTP 处理程序签名。
- 让我们定义
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)
}
- 定义
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())
})
}
}
- 在
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"}
- 跟进:如果我想从请求中获取更多信息,会发生什么情况?
在您的日志中间件中,您可以读取请求 header 和 body(来自 r *http.Request
),然后使用持续时间和响应数据记录它们。请注意,您应该 reassign the request body 以便您的实际处理程序可以读取请求 body.