web服务除了错误日志、业务日志还有一个常用且重要的日志类别——访问日志。每一条访问日志记录应包含但不限于请求方法、请求路径、请求参数、方法开始时间、方法调用结束时间、响应结果状态码、响应结果
1. 访问日志中间件
func AccessLog() gin.HandlerFunc {
reutrn func(c *gin.Context) {
// 开始时间
startTime := time.Now()
// 请求方法
method := c.Request.Method
// 请求路径
path := c.Request.URL.Path
// 请求参数
request := c.Request.PostForm.Encode()
// 处理请求
c.Next()
// 结束时间
endTime := time.Now()
// 响应结果状态码
statusCode := c.Writer.Status()
log.Printf("%d %s %s %s %s %s", statusCode, startTime, method, path, request, endTime)
}
}
2. 获取响应结果
在示例中实现了请求方法等要素的记录,但无法获取到响应结果,对于访问日志显然是不满足要求的,那么如何获取?想一想响应内容会存到哪里?没错,在Writer中,那么我们是不是可以在Writer.write方法中做一些处理,把结果存储起来?
新增代码:
// 自定义一个Writer,实现http.ResponseWriter
type AccessLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w *AccessLogWriter) Write(p []byte) (int, error) {
// 响应结果写入缓冲区
if n, err := w.body.Write(p); err != nil {
return n, err
}
return w.ResponseWriter.Write(p)
}
func AccessLog() gin.HandlerFunc {
reutrn func(c *gin.Context) {
bodyWriter := &AccessLogWriter{
body: bytes.NewBufferString(""),
ResponseWriter: c.Writer
}
// 替换原有输出流
c.Writer = bodyWriter
// 开始时间
startTime := time.Now()
// 请求方法
method := c.Request.Method
// 请求路径
path := c.Request.URL.Path
// 请求参数
request := c.Request.PostForm.Encode()
// 处理请求
c.Next()
// 结束时间
endTime := time.Now()
// 响应结果状态码
statusCode := bodyWriter.Status()
// 响应结果
response := bodyWriter.body.String()
log.Printf("%d %s %s %s %s %s %s", statusCode, startTime, method, path, request, endTime, response)
}
}
3. 总结
对于访问日志,大多数要素都能在Reqeust和Writer中直接获取到。无法直接获取的需要思考一下如何通过自定义Writer获取,因为要素都在Request和Writer两个流中