137 lines
3.0 KiB
Go
137 lines
3.0 KiB
Go
package middlewares
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"go.uber.org/zap"
|
|
|
|
"enaklo-pos-be/internal/common/logger"
|
|
)
|
|
|
|
func RequestMiddleware(c ConfigLogger) (handler gin.HandlerFunc) {
|
|
if !c.IsLoggerEnabled() {
|
|
return func(ctx *gin.Context) {
|
|
ctx.Next()
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|