web服务除了错误日志、业务日志还有一个常用且重要的日志类别——访问日志。每一条访问日志记录应包含但不限于请求方法、请求路径、请求参数、方法开始时间、方法调用结束时间、响应结果状态码、响应结果

1. 访问日志中间件

  1. func AccessLog() gin.HandlerFunc {
  2. reutrn func(c *gin.Context) {
  3. // 开始时间
  4. startTime := time.Now()
  5. // 请求方法
  6. method := c.Request.Method
  7. // 请求路径
  8. path := c.Request.URL.Path
  9. // 请求参数
  10. request := c.Request.PostForm.Encode()
  11. // 处理请求
  12. c.Next()
  13. // 结束时间
  14. endTime := time.Now()
  15. // 响应结果状态码
  16. statusCode := c.Writer.Status()
  17. log.Printf("%d %s %s %s %s %s", statusCode, startTime, method, path, request, endTime)
  18. }
  19. }

2. 获取响应结果

在示例中实现了请求方法等要素的记录,但无法获取到响应结果,对于访问日志显然是不满足要求的,那么如何获取?想一想响应内容会存到哪里?没错,在Writer中,那么我们是不是可以在Writer.write方法中做一些处理,把结果存储起来?

新增代码:

  1. // 自定义一个Writer,实现http.ResponseWriter
  2. type AccessLogWriter struct {
  3. gin.ResponseWriter
  4. body *bytes.Buffer
  5. }
  6. func (w *AccessLogWriter) Write(p []byte) (int, error) {
  7. // 响应结果写入缓冲区
  8. if n, err := w.body.Write(p); err != nil {
  9. return n, err
  10. }
  11. return w.ResponseWriter.Write(p)
  12. }
  13. func AccessLog() gin.HandlerFunc {
  14. reutrn func(c *gin.Context) {
  15. bodyWriter := &AccessLogWriter{
  16. body: bytes.NewBufferString(""),
  17. ResponseWriter: c.Writer
  18. }
  19. // 替换原有输出流
  20. c.Writer = bodyWriter
  21. // 开始时间
  22. startTime := time.Now()
  23. // 请求方法
  24. method := c.Request.Method
  25. // 请求路径
  26. path := c.Request.URL.Path
  27. // 请求参数
  28. request := c.Request.PostForm.Encode()
  29. // 处理请求
  30. c.Next()
  31. // 结束时间
  32. endTime := time.Now()
  33. // 响应结果状态码
  34. statusCode := bodyWriter.Status()
  35. // 响应结果
  36. response := bodyWriter.body.String()
  37. log.Printf("%d %s %s %s %s %s %s", statusCode, startTime, method, path, request, endTime, response)
  38. }
  39. }

3. 总结

对于访问日志,大多数要素都能在Reqeust和Writer中直接获取到。无法直接获取的需要思考一下如何通过自定义Writer获取,因为要素都在Request和Writer两个流中