recovery.go 1.8 KB

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