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.ResponseWritertype AccessLogWriter struct {gin.ResponseWriterbody *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两个流中
