golang gin日志中间件
我们有时候需要一些日志来判断做一些错误处理,虽然gin已经默认使用了一个很不错的中间件,但可能我们需要的信息并不在其中
下面我们自定义一个日志中间件,首先应该明确我们在日志中应该记录什么?
一般的日志中间件就会记录这些信息:请求头、响应体、响应时间、请求方法、请求IP等
首先实现一个方法来请求体:
func getRequestBody(ctx *gin.Context) interface {} {
switch ctx.Request.Method {
case http.MethodGet:
return ctx.Request.URL.Query()
case http.MethodPost:
fallthrough
case http.MethodPut:
fallthrough
case http.MethodPatch:
var bodyBytes []byte
bodyBytes, err := ioutil.ReadAll(ctx.Request.Body)
if err != nil {
return nil
}
ctx.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
return string(bodyBytes)
}
return nil
}
我们需要定义一个结构体和方法
// bodyLogWriter 定义一个存储响应内容的结构体
type bodyLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
// Write 读取响应数据
func (w bodyLogWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
在bodyLogWriter结构体中封装了gin的responseWriter,然后在重写的Write方法中,首先向bytes.Buffer中写数据,然后响应
这保证了我们可以正确的获取到响应内容
最后就是中间件的实现,其中最重要的一点就是用我们自定义的bodyLogWriter来代替ctx.Writer,保证响应会保留一份在bytes.Buffer中
// RequestLog gin请求日志中间件
func RequestLog(ctx *gin.Context) {
t := time.Now()
// 初始化bodyLogWriter
blw := &bodyLogWriter{
body: bytes.NewBufferString(""),
ResponseWriter: ctx.Writer,
}
ctx.Writer = blw
// 获取请求信息
requestBody := getRequestBody(ctx)
ctx.Next()
// 记录响应信息
// 请求时间
costTime := time.Since(t)
// 响应内容
responseBody := blw.body.String()
// 日志格式
logContext := make(map[string]interface{})
logContext["request_uri"] = ctx.Request.RequestURI
logContext["request_method"] = ctx.Request.Method
logContext["refer_service_name"] = ctx.Request.Referer()
logContext["refer_request_host"] = ctx.ClientIP()
logContext["request_body"] = requestBody
logContext["request_time"] = t.String()
logContext["response_body"] = responseBody
logContext["time_used"] = fmt.Sprintf("%v", costTime)
logContext["header"] = ctx.Request.Header
log.Println(logContext)
}
当然最后日志在控制台打印了,如果有持久化的需求可以异步持久化到本地或者远程的数据库
3. 跨域中间件
func CORSMiddleWare(ctx *gin.Context) {
method := ctx.Request.Method
// set response header
ctx.Header( "Access-Control-Allow-Origin" , ctx.Request.Header.Get( "Origin" ))
ctx.Header( "Access-Control-Allow-Credentials" , "true" )
ctx.Header( "Access-Control-Allow-Headers" ,
"Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With" )
ctx.Header( "Access-Control-Allow-Methods" , "GET,POST,PUT,PATCH,DELETE,OPTIONS" )
// 默认过滤options和head这两个请求,使用204返回
if method == http.MethodOptions || method == http.MethodHead {
ctx.AbortWithStatus(http.StatusNoContent)
return
}
ctx.Next()
}

