package middlewares import ( "bytes" "encoding/json" "fmt" "io" "net/http" "strconv" "strings" "time" "github.com/gin-gonic/gin" "go.uber.org/zap" "furtuna-be/internal/common/logger" ) func RequestMiddleware() (handler gin.HandlerFunc) { return func(ctx *gin.Context) { start := time.Now() body, _ := readRequestBody(ctx.Request) reqData := getRequestParam(ctx.Request, body) // Check if the request contains a file isFileUpload := false contentType := ctx.Request.Header.Get("Content-Type") if strings.HasPrefix(contentType, "multipart/form-data") { isFileUpload = true } // Log the request if it's not a file upload if !isFileUpload { logger.ContextLogger(ctx).With(reqData...).Info("Request") } rbw := &ResponseBodyWriter{body: bytes.NewBufferString(""), ResponseWriter: ctx.Writer} ctx.Writer = rbw stop := time.Now() latency := stop.Sub(start).Milliseconds() resData := reqData resData = append(resData, getResponseParam(rbw, latency)...) if !isFileUpload { logger.ContextLogger(ctx).With(resData...).Info("Response") } } } func readRequestBody(req *http.Request) ([]byte, error) { body, err := io.ReadAll(req.Body) if err != nil { logger.ContextLogger(req.Context()).Error(fmt.Sprintf("Error reading body: %v", err)) return nil, err } req.Body = io.NopCloser(bytes.NewBuffer(body)) return body, nil } type ResponseBodyWriter struct { gin.ResponseWriter body *bytes.Buffer } func excludeSensitiveFields(data []interface{}) []interface{} { var result []interface{} for _, item := range data { if param, ok := item.(gin.Param); ok { // Exclude Authorization and Password fields if param.Key != "Authorization" && param.Key != "Password" { result = append(result, item) } } else { result = append(result, item) } } return result } func getRequestParam(req *http.Request, body []byte) []zap.Field { var reqData []zap.Field reqData = append(reqData, zap.Any("host", req.Host), zap.Any("uri", req.RequestURI), zap.Any("method", req.Method), zap.Any("path", func() interface{} { p := req.URL.Path if p == "" { p = "/" } return p }()), zap.Any("protocol", req.Proto), zap.Any("referer", req.Referer()), zap.Any("user_agent", req.UserAgent()), zap.Any("headers", req.Header), zap.Any("remote_ip", req.RemoteAddr), zap.Any("body", excludeSensitiveFieldsFromBody(body)), ) return reqData } func getResponseParam(rbw *ResponseBodyWriter, latency int64) []zap.Field { var resData []zap.Field resData = append(resData, zap.Any("httpStatus", rbw.Status()), zap.Any("body", rbw.body.String()), zap.Any("latency_human", strconv.FormatInt(latency, 10)), zap.Any("headers", rbw.Header()), ) return resData } func excludeSensitiveFieldsFromBody(body []byte) string { var data map[string]interface{} if err := json.Unmarshal(body, &data); err != nil { return string(body) } delete(data, "password") result, _ := json.Marshal(data) return string(result) }