/
recovery.go
61 lines (54 loc) · 1.54 KB
/
recovery.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package handler
import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/tsingsun/woocoo/pkg/conf"
"github.com/tsingsun/woocoo/pkg/log"
"go.uber.org/zap"
"net/http"
"net/http/httputil"
)
var (
ErrRecovery = errors.New("internal server error")
)
// RecoveryMiddleware is a middleware that recovers from panics anywhere in the chain
// and handles the control to the centralized HTTPErrorHandler.
type RecoveryMiddleware struct {
}
// Recovery is the recovery middleware apply function. see MiddlewareNewFunc
func Recovery() Middleware {
return &RecoveryMiddleware{}
}
func (h *RecoveryMiddleware) Name() string {
return RecoverName
}
func (h *RecoveryMiddleware) ApplyFunc(_ *conf.Configuration) gin.HandlerFunc {
return gin.CustomRecovery(func(c *gin.Context, err any) {
HandleRecoverError(c, err, 4)
})
}
func HandleRecoverError(c *gin.Context, p any, stackSkip int) {
httpRequest, _ := httputil.DumpRequest(c.Request, false)
err, ok := p.(error)
// gin private error doesn't show to user
if ok {
AbortWithError(c, http.StatusInternalServerError, err)
} else {
AbortWithError(c, http.StatusInternalServerError, ErrRecovery)
err = fmt.Errorf("%v", p)
}
fc := GetLogCarrierFromGinContext(c)
if fc != nil {
fc.Fields = append(fc.Fields,
zap.NamedError("panic", err),
zap.String("request", string(httpRequest)),
zap.StackSkip(log.StacktraceKey, stackSkip),
)
return
}
logger.Ctx(c).WithOptions(zap.AddCallerSkip(stackSkip)).Error("[Recovery from panic]",
zap.Error(err),
zap.String("request", string(httpRequest)),
)
}