package middlewares import ( "errors" "net" "net/http/httputil" "os" "strings" "time" "github.com/gin-gonic/gin" "go.uber.org/zap" "github.com/runningwater/gohub/pkg/logger" "github.com/runningwater/gohub/pkg/response" ) // Recovery 使用 zap.Error() 来记录 panic 和 call stack func Recovery() gin.HandlerFunc { return func(c *gin.Context) { defer func() { if err := recover(); err != nil { // 获取用户的请求信息 request, _ := httputil.DumpRequest(c.Request, true) // 链接中断,客户端中断连接为正常行为,不需要记录堆栈信息 var brokenPipe bool if ne, ok := err.(*net.OpError); ok { var se *os.SyscallError if errors.As(ne.Err, &se) { errStr := strings.ToLower(se.Error()) if strings.Contains(errStr, "broken pipe") || strings.Contains(errStr, "connection reset by peer") { brokenPipe = true } } } // 链接中断的情况 if brokenPipe { logger.Error(c.Request.URL.Path, zap.Time("time", time.Now()), zap.Any("error", err), zap.String("request", string(request)), ) _ = c.Error(err.(error)) //nolint:errcheck c.Abort() // 链接已断开,无法写状态 return } // 如果不是链接中断的情况,记录堆栈信息 logger.Error("[Recovery from panic]", zap.Time("time", time.Now()), // 记录当前时间 zap.Any("error", err), // 记录错误信息 zap.String("request", string(request)), // 记录请求信息 zap.Stack("stack"), // 记录堆栈信息 ) // c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ // "message": "服务器内部错误,请稍后再试", // }) response.Abort500(c) } }() // 继续处理请求 c.Next() } }