gin recovery 日志收集
这段代码实现了一个 Gin 中间件 GinRecovery,用于捕获和处理可能在请求处理中出现的 panic,并使用 zap 记录相关日志。这个中间件可以确保在发生 panic 时,服务器不会崩溃,并且可以记录详细的错误信息以供后续排查。
代码详解
引入的包
import (
"net"
"net/http"
"net/http/httputil"
"os"
"runtime/debug"
"strings"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
GinRecovery 函数
func GinRecovery(stack bool) gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
// 检查是否是连接断开的错误,这种错误不需要记录 panic 堆栈信息。
var brokenPipe bool
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
}
}
}
// 获取请求信息
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)) // nolint: errcheck
c.Abort()
return
}
// 记录 panic 错误日志
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()
}
}
核心逻辑
-
defer recover:
- 使用
defer和recover捕获可能在请求处理过程中发生的 panic。
- 使用
-
检查连接错误:
- 检查是否是连接断开(如 "broken pipe" 或 "connection reset by peer")导致的错误。这类错误通常不需要记录 panic 堆栈信息。
-
记录请求信息:
- 使用
httputil.DumpRequest获取请求的详细信息,以便记录日志。
- 使用
-
处理连接断开错误:
- 如果是连接断开错误,记录错误日志并中止请求处理。
-
记录 panic 错误日志:
- 如果
stack参数为true,记录完整的 panic 错误堆栈信息。 - 否则,只记录错误信息和请求信息。
- 如果
-
返回 HTTP 500 状态码:
- 在捕获到 panic 后,返回 HTTP 500 状态码表示服务器内部错误。
使用示例
要在 Gin 应用中使用这个中间件,可以在路由设置时添加:
r := gin.Default()
r.Use(GinRecovery(true)) // 传入 true 表示记录堆栈信息
总结
这个中间件通过捕获和处理请求处理中可能出现的 panic,确保服务器不会因未捕获的异常而崩溃。同时,通过记录详细的错误和请求信息,帮助开发者快速定位和解决问题。