|
@@ -0,0 +1,107 @@
|
|
|
+package middleware
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "io/ioutil"
|
|
|
+
|
|
|
+ "net"
|
|
|
+ "net/http"
|
|
|
+ "net/http/httputil"
|
|
|
+ "os"
|
|
|
+ "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
|
|
|
+}
|
|
|
+
|
|
|
+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("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
|
|
|
+ zap.String("time", cost.String()),
|
|
|
+ zap.String("x-request-id", c.Request.Header.Get("X-Request-Id")),
|
|
|
+ }
|
|
|
+ logFields = append(logFields, zap.String("Request body", string(requestBody)))
|
|
|
+ logFields = append(logFields, zap.String("Response body", w.body.String()))
|
|
|
+ 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)),
|
|
|
+ )
|
|
|
+ }
|
|
|
+ c.AbortWithStatus(http.StatusInternalServerError)
|
|
|
+ }
|
|
|
+ }()
|
|
|
+ c.Next()
|
|
|
+ }
|
|
|
+}
|