recovery.go 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. package middlewares
  2. import (
  3. "errors"
  4. "net"
  5. "net/http/httputil"
  6. "os"
  7. "strings"
  8. "time"
  9. "github.com/gin-gonic/gin"
  10. "go.uber.org/zap"
  11. "github.com/runningwater/gohub/pkg/logger"
  12. "github.com/runningwater/gohub/pkg/response"
  13. )
  14. // Recovery 使用 zap.Error() 来记录 panic 和 call stack
  15. func Recovery() gin.HandlerFunc {
  16. return func(c *gin.Context) {
  17. defer func() {
  18. if err := recover(); err != nil {
  19. // 获取用户的请求信息
  20. request, _ := httputil.DumpRequest(c.Request, true)
  21. // 链接中断,客户端中断连接为正常行为,不需要记录堆栈信息
  22. var brokenPipe bool
  23. if ne, ok := err.(*net.OpError); ok {
  24. var se *os.SyscallError
  25. if errors.As(ne.Err, &se) {
  26. errStr := strings.ToLower(se.Error())
  27. if strings.Contains(errStr, "broken pipe") || strings.Contains(errStr, "connection reset by peer") {
  28. brokenPipe = true
  29. }
  30. }
  31. }
  32. // 链接中断的情况
  33. if brokenPipe {
  34. logger.Error(c.Request.URL.Path,
  35. zap.Time("time", time.Now()),
  36. zap.Any("error", err),
  37. zap.String("request", string(request)),
  38. )
  39. _ = c.Error(err.(error)) //nolint:errcheck
  40. c.Abort()
  41. // 链接已断开,无法写状态
  42. return
  43. }
  44. // 如果不是链接中断的情况,记录堆栈信息
  45. logger.Error("[Recovery from panic]",
  46. zap.Time("time", time.Now()), // 记录当前时间
  47. zap.Any("error", err), // 记录错误信息
  48. zap.String("request", string(request)), // 记录请求信息
  49. zap.Stack("stack"), // 记录堆栈信息
  50. )
  51. // c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
  52. // "message": "服务器内部错误,请稍后再试",
  53. // })
  54. response.Abort500(c)
  55. }
  56. }()
  57. // 继续处理请求
  58. c.Next()
  59. }
  60. }