如何解决如何使用由 Gin 上下文创建的请求 ID 记录 HTTP 客户端的请求
想法:我想用唯一的请求 ID 将传入和传出请求记录到我的 Gin 服务器。此外,我想使用与路由相同的请求 ID 将所有 HTTP 客户端的请求记录到我的 Gin 路由中。
所有这些都应该使用中间件在幕后工作。
将请求记录到我的服务器(和响应)
为了将每个请求记录到我的服务器,我编写了这个中间件:
import (
"bytes"
"context"
"github.com/gin-contrib/requestid"
"github.com/gin-gonic/gin"
"github.com/rs/zerolog/log"
"io/ioutil"
"net/http"
"time"
)
type responseBodyWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (r responseBodyWriter) Write(b []byte) (int,error) {
r.body.Write(b)
return r.ResponseWriter.Write(b)
}
func LoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
w := &responseBodyWriter{body: &bytes.Buffer{},ResponseWriter: c.Writer}
c.Writer = w
msg := "Input:"
path := c.Request.URL.Path
raw := c.Request.URL.RawQuery
if raw != "" {
path = path + "?" + raw
}
// Read from body and write here again.
var bodyBytes []byte
if c.Request.Body != nil {
bodyBytes,_ = ioutil.ReadAll(c.Request.Body)
}
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
inputLogger := log.With().
Str("method",c.Request.Method).
Str("path",path).
Str("requestId",requestid.Get(c)).
Logger()
if len(bodyBytes) > 0 {
inputLogger.Info().RawJSON("body",bodyBytes).Msg(msg)
} else {
inputLogger.Info().Msg(msg)
}
c.Next()
end := time.Now()
latency := end.Sub(start)
msg = "Output:"
outputLogger := log.With().
Str("method",requestid.Get(c)).
RawJSON("body",w.body.Bytes()).
Int("status",c.Writer.Status()).
Dur("latency",latency).
Logger()
switch {
case c.Writer.Status() >= http.StatusBadRequest && c.Writer.Status() < http.StatusInternalServerError:
{
outputLogger.Warn().Msg(msg)
}
case c.Writer.Status() >= http.StatusInternalServerError:
{
outputLogger.Error().Msg(msg)
}
default:
outputLogger.Info().Msg(msg)
}
}
}
在我的服务器路由中记录请求
问题在于:我不知道如何将请求 ID(或 Gin 的上下文)(由 Gin 的中间件创建)传递给 RoundTrip
函数:
type Transport struct {
Transport http.RoundTripper
}
var defaultTransport = Transport{
Transport: http.DefaultTransport,}
func init() {
http.DefaultTransport = &defaultTransport
}
func (t *Transport) RoundTrip(req *http.Request) (*http.Response,error) {
ctx := context.WithValue(req.Context(),ContextKeyRequestStart,time.Now())
req = req.WithContext(ctx)
t.logRequest(req)
resp,err := t.transport().RoundTrip(req)
if err != nil {
return resp,err
}
t.logResponse(resp)
return resp,err
}
func (t *Transport) logRequest(req *http.Request) {
log.Info().
Str("method",req.Method).
Str("path",req.URL.String()).
Str("requestId","how can I get request id here???").
Msg("Api request: ")
}
func (t *Transport) logResponse(resp *http.Response) {
var bodyBytes []byte
if resp.Body != nil {
bodyBytes,_ = ioutil.ReadAll(resp.Body)
}
resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
ctx := resp.Request.Context()
log.Info().
Str("method",resp.Request.Method).
Str("path",resp.Request.URL.String()).
Str("requestId","how can I get request id here???").
RawJSON("body",bodyBytes).
Int("status",resp.StatusCode).
Dur("latency",time.Now().Sub(ctx.Value(ContextKeyRequestStart).(time.Time))).
Msg("API response: ")
}
func (t *Transport) transport() http.RoundTripper {
if t.Transport != nil {
return t.Transport
}
return http.DefaultTransport
}
解决方法
Transport.RoundTrip
函数采用 *http.Request
参数,因此您应该能够通过在处理程序中使用它创建请求来传递 Gin 上下文:
func MyHandler(c *gin.Context) {
// passing context to the request
req := http.NewRequestWithContext(c,"GET","http://localhost:8080",nil)
resp,err := http.DefaultClient.Do(req)
}
请注意,为了能够在不进行额外初始化的情况下使用您覆盖的默认 RoundTripper
,您应该使用 http.DefaultClient
。
你可以使用这个:mv
r = re.compile(r'^[0-9]*[.,]{0,1}[0-9]+/')
输出:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
requestid "github.com/sumit-tembe/gin-requestid"
)
func main() {
// without any middlewares
router := gin.New()
// Middlewares
{
//recovery middleware
router.Use(gin.Recovery())
//middleware which injects a 'RequestID' into the context and header of each request.
router.Use(requestid.RequestID(nil))
//middleware which enhance Gin request logger to include 'RequestID'
router.Use(gin.LoggerWithConfig(requestid.GetLoggerConfig(nil,nil,nil)))
}
router.GET("/",func(c *gin.Context) {
c.String(http.StatusOK,"Hello world!")
})
router.Run(":8080")
}
它还支持自定义请求id生成器,您可以根据需要进行设计。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。