package middleware import ( "bytes" "io" "io/ioutil" "net/http" "runtime" "runtime/debug" "strings" "time" "gitee.com/xuyiping_admin/pkg/logger/zaplog" "github.com/gin-gonic/gin" "go.uber.org/zap" ) type responseBodyWriter struct { gin.ResponseWriter body *bytes.Buffer } type stackErr struct { Err error Stack string } func (s *stackErr) Error() string { return s.Err.Error() } func (r responseBodyWriter) Write(b []byte) (int, error) { r.body.Write(b) return r.ResponseWriter.Write(b) } // GinLogger 接管gin框架默认的日志 func GinLogger() gin.HandlerFunc { return func(c *gin.Context) { // 获取 response 内容 w := &responseBodyWriter{body: &bytes.Buffer{}, ResponseWriter: c.Writer} c.Writer = w var requestBody []byte if c.Request.Body != nil { requestBody, _ = ioutil.ReadAll(c.Request.Body) c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(requestBody)) } start := time.Now() c.Next() cost := time.Since(start) logFields := []zap.Field{ zap.Int("status", c.Writer.Status()), zap.String("request", c.Request.Method+" "+c.Request.URL.String()), zap.String("query", c.Request.URL.RawQuery), zap.String("ip", c.ClientIP()), zap.String("user-agent", c.Request.UserAgent()), zap.String("time", cost.String()), zap.String("Request body", string(requestBody)), zap.String("Response body", w.body.String()), } if len(c.Errors) > 0 { logFields = append(logFields, zap.Any("stack", string(debug.Stack()))) zaplog.Error("Http-Access-Error", logFields...) c.Abort() } else { zaplog.Info("Http-Access-Log", logFields...) } } } // GinRecovery recover掉我的项目可能呈现的panic func GinRecovery(stack bool) gin.HandlerFunc { return func(c *gin.Context) { defer func() { if err := recover(); err != nil { /*// Check for a broken connection, as it is not really a // condition that warrants a panic stack trace. 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 { zaplog.Error(c.Request.URL.Path, zap.Any("error", err), zap.String("request", string(httpRequest)), ) // If the connection is dead, we can't write a status to it. c.Error(err.(error)) // nolint: errcheck c.Abort() return } if stack { zaplog.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), zap.String("stack", string(debug.Stack())), ) } else { zaplog.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), ) }*/ defer func() { if err = recover(); err != nil { body, _ := ioutil.ReadAll(c.Request.Body) // 获取 panic 发生的位置 pc, file, line, ok := runtime.Caller(2) funcName := "" if ok { fn := runtime.FuncForPC(pc).Name() // 去除包路径,只保留函数名 /*funcName = filepath.Base(fn) file = filepath.Base(file)*/ parts := strings.Split(fn, "/") if len(parts) > 0 { lastPart := parts[len(parts)-1] parts = strings.Split(lastPart, ".") if len(parts) > 0 { funcName = parts[len(parts)-1] } } file = strings.TrimPrefix(file, c.Request.Context().Value(gin.ContextKey).(string)+"/") // 尝试去除项目路径前缀(可选) } zaplog.Error("cors", zap.Any("recover", err), zap.Any("url", c.Request.URL), zap.Any("file", file), zap.Any("line", line), zap.Any("func", funcName), zap.Any("request", string(body)), ) c.Request.Body = io.NopCloser(bytes.NewBuffer(body)) } }() c.AbortWithStatus(http.StatusInternalServerError) } }() c.Next() } }