记录 http 响应(除了请求)
Logging http responses (in addition to requests)
我正在使用 Go 和 Gorilla 网络工具包的 mux 和处理程序包来构建一个复杂的应用程序,其中一部分需要一个 http 服务器。 Gorilla 的 mux 和处理程序包工作得非常好,我能够成功启动 http 服务器 运行 并且记录请求非常简单。
但是,我无法确定如何记录回复。理想情况下,我想要一种类似于 Gorilla 的 LoggingHandler 的机制,它可以轻松地“包装”日志机制。
是否有可以轻松包装/记录响应的 Go 包?有没有办法以我没有考虑过的方式使用 Go 或 Gorilla 的功能?
编辑 抱歉,我没有注意到你提到的 gorilla-mux,我只用 gin 试过这个,但如果它使用中间件,这应该仍然有效。
诀窍是,c.Next()
在一个中间件中阻塞,直到所有后续中间件 return。这是一个 logrus 解决方案。把它作为你的第一个中间件:
func Logrus(logger *logrus.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now().UTC()
path := c.Request.URL.Path
c.Next()
end := time.Now().UTC()
latency := end.Sub(start)
logger.WithFields(logrus.Fields{
"status": c.Writer.Status(),
"method": c.Request.Method,
"path": path,
"ip": c.ClientIP(),
"duration": latency,
"user_agent": c.Request.UserAgent(),
}).Info()
}
}
GinEngine.Use(Logrus(logrus.StandardLogger()))
感谢您的宝贵建议。我尝试了一些建议并找到了一个使用极简包装器的相当简单的解决方案。这是对我有用的解决方案(请随时提供评论,或者更好的是,其他解决方案):
import (
"fmt"
"log"
"net/http"
"net/http/httptest"
"net/http/httputil"
"github.com/gorilla/mux"
)
:
func logHandler(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
x, err := httputil.DumpRequest(r, true)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
return
}
log.Println(fmt.Sprintf("%q", x))
rec := httptest.NewRecorder()
fn(rec, r)
log.Println(fmt.Sprintf("%q", rec.Body))
}
}
func MessageHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "A message was received")
}
下面的代码将使用上述处理程序:
:
router := mux.NewRouter()
router.HandleFunc("/", logHandler(MessageHandler))
:
以上代码的输出类似于:
:
2016/07/20 14:44:29 "GET ... HTTP/1.1\r\nHost: localhost:8088\r\nAccept: */*\r\nUser-Agent: curl/7.43.0\r\n\r\n"
2016/07/20 14:44:29 ...[response body]
:
如果您想实际将回复发送给客户,Eric Broda 接受的答案将无济于事。我对实际有效的代码进行了修改:
func logHandler(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
x, err := httputil.DumpRequest(r, true)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
return
}
log.Println(fmt.Sprintf("%q", x))
rec := httptest.NewRecorder()
fn(rec, r)
log.Println(fmt.Sprintf("%q", rec.Body))
// this copies the recorded response to the response writer
for k, v := range rec.HeaderMap {
w.Header()[k] = v
}
w.WriteHeader(rec.Code)
rec.Body.WriteTo(w)
}
}
我正在使用 Go 和 Gorilla 网络工具包的 mux 和处理程序包来构建一个复杂的应用程序,其中一部分需要一个 http 服务器。 Gorilla 的 mux 和处理程序包工作得非常好,我能够成功启动 http 服务器 运行 并且记录请求非常简单。
但是,我无法确定如何记录回复。理想情况下,我想要一种类似于 Gorilla 的 LoggingHandler 的机制,它可以轻松地“包装”日志机制。
是否有可以轻松包装/记录响应的 Go 包?有没有办法以我没有考虑过的方式使用 Go 或 Gorilla 的功能?
编辑 抱歉,我没有注意到你提到的 gorilla-mux,我只用 gin 试过这个,但如果它使用中间件,这应该仍然有效。
诀窍是,c.Next()
在一个中间件中阻塞,直到所有后续中间件 return。这是一个 logrus 解决方案。把它作为你的第一个中间件:
func Logrus(logger *logrus.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now().UTC()
path := c.Request.URL.Path
c.Next()
end := time.Now().UTC()
latency := end.Sub(start)
logger.WithFields(logrus.Fields{
"status": c.Writer.Status(),
"method": c.Request.Method,
"path": path,
"ip": c.ClientIP(),
"duration": latency,
"user_agent": c.Request.UserAgent(),
}).Info()
}
}
GinEngine.Use(Logrus(logrus.StandardLogger()))
感谢您的宝贵建议。我尝试了一些建议并找到了一个使用极简包装器的相当简单的解决方案。这是对我有用的解决方案(请随时提供评论,或者更好的是,其他解决方案):
import (
"fmt"
"log"
"net/http"
"net/http/httptest"
"net/http/httputil"
"github.com/gorilla/mux"
)
:
func logHandler(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
x, err := httputil.DumpRequest(r, true)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
return
}
log.Println(fmt.Sprintf("%q", x))
rec := httptest.NewRecorder()
fn(rec, r)
log.Println(fmt.Sprintf("%q", rec.Body))
}
}
func MessageHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "A message was received")
}
下面的代码将使用上述处理程序:
:
router := mux.NewRouter()
router.HandleFunc("/", logHandler(MessageHandler))
:
以上代码的输出类似于:
:
2016/07/20 14:44:29 "GET ... HTTP/1.1\r\nHost: localhost:8088\r\nAccept: */*\r\nUser-Agent: curl/7.43.0\r\n\r\n"
2016/07/20 14:44:29 ...[response body]
:
如果您想实际将回复发送给客户,Eric Broda 接受的答案将无济于事。我对实际有效的代码进行了修改:
func logHandler(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
x, err := httputil.DumpRequest(r, true)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
return
}
log.Println(fmt.Sprintf("%q", x))
rec := httptest.NewRecorder()
fn(rec, r)
log.Println(fmt.Sprintf("%q", rec.Body))
// this copies the recorded response to the response writer
for k, v := range rec.HeaderMap {
w.Header()[k] = v
}
w.WriteHeader(rec.Code)
rec.Body.WriteTo(w)
}
}