为了防止报错引起Gin服务挂掉以及错误日志记录,我们使用全局错误中间件进行管理。
go
package middleware
import (
"ToDoList/global"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"net"
"net/http"
"net/http/httputil"
"os"
"runtime/debug"
"strings"
)
// GinRecovery recover掉项目可能出现的panic,并使用zap记录相关日志
func GinRecovery(stack bool) gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
var brokenPipe bool
// 如果是连接错误并且错误信息中包含"broken pipe"或"connection reset by peer",则认为是客户端断开连接导致的错误。
if ne, ok := err.(*net.OpError); ok {
if se, ok := ne.Err.(*os.SyscallError); ok {
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
brokenPipe = true
}
}
}
//如果brokenPipe为true,表明客户端断开连接导致的错误,则使用zap记录相关日志,并将错误信息作为错误返回给客户端。
httpRequest, _ := httputil.DumpRequest(c.Request, false)
if brokenPipe {
global.GVA_LOG.Error(c.Request.URL.Path,
zap.Any("error", err),
zap.String("request", string(httpRequest)),
)
_ = c.Error(err.(error))
c.Abort()
return
}
//如果stack为true,则使用debug.Stack()函数获取堆栈信息,并使用zap记录相关日志。
if stack {
global.GVA_LOG.Error("[Recovery from panic]",
zap.Any("error", err),
zap.String("request", string(httpRequest)),
zap.String("stack", string(debug.Stack())),
)
} else {
global.GVA_LOG.Error("[Recovery from panic]",
zap.Any("error", err),
zap.String("request", string(httpRequest)),
)
}
//发生错误中止当前请求并返回500状态码。
c.AbortWithStatus(http.StatusInternalServerError)
}
}()
c.Next()
}
}