hi-server/internal/middleware/loggerMiddleware.go
shanshanzhong a01570b59d
Some checks failed
Build docker and publish / build (20.15.1) (push) Failing after 8m21s
fix gitea workflow path and runner label
2026-03-04 06:33:14 -08:00

139 lines
4.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package middleware
import (
"bytes"
"encoding/json"
"io"
"time"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/svc"
)
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)
}
func LoggerMiddleware(svc *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
sensitiveFields := []string{"password", "old_password", "new_password"}
// get response body
w := &responseBodyWriter{body: &bytes.Buffer{}, ResponseWriter: c.Writer}
c.Writer = w
// get request body
var requestBody []byte
if c.Request.Body != nil {
// c.Request.Body It can only be read once, and after reading, it needs to be reassigned to c.Request Body
requestBody, _ = io.ReadAll(c.Request.Body)
// After reading, reassign c.Request Body For subsequent operations
c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
}
// start time
start := time.Now()
c.Next()
// Start recording logs
cost := time.Since(start)
responseStatus := c.Writer.Status()
host := c.Request.Host
logs := []logger.LogField{
{
Key: "status",
Value: responseStatus,
},
{
Key: "request",
Value: c.Request.Method + " " + host + c.Request.URL.String(),
},
{
Key: "query",
Value: c.Request.URL.RawQuery,
},
{
Key: "ip",
Value: c.ClientIP(),
},
{
Key: "user-agent",
Value: c.Request.UserAgent(),
},
}
if apiHeader, ok := c.Get("api_header"); ok {
logs = append(logs, logger.Field("api_header", apiHeader))
}
if c.Errors.Last() != nil {
var e *xerr.CodeError
var errMessage string
if errors.As(c.Errors.Last().Err, &e) {
errMessage = e.GetErrMsg()
} else {
errMessage = c.Errors.Last().Error()
}
logs = append(logs, logger.Field("error", errMessage))
}
if status, ok := c.Get(ctxDeviceDecryptStatusKey); ok {
logs = append(logs, logger.Field("device_decrypt_status", status))
}
if reason, ok := c.Get(ctxDeviceDecryptReasonKey); ok {
logs = append(logs, logger.Field("device_decrypt_reason", reason))
}
if encryptedQuery, ok := c.Get(ctxEncryptedQueryKey); ok {
logs = append(logs, logger.Field("encrypted_query", encryptedQuery))
}
if decryptedQuery, ok := c.Get(ctxDecryptedQueryKey); ok {
logs = append(logs, logger.Field("decrypted_query", decryptedQuery))
}
if c.Request.Method == "POST" || c.Request.Method == "PUT" || c.Request.Method == "DELETE" {
// request content
logs = append(logs, logger.Field("request_body", string(maskSensitiveFields(requestBody, sensitiveFields))))
if decryptedBody, ok := c.Get(ctxDecryptedBodyKey); ok {
if bodyText, isString := decryptedBody.(string); isString {
logs = append(logs, logger.Field("decrypted_request_body", string(maskSensitiveFields([]byte(bodyText), sensitiveFields))))
}
}
// response content
logs = append(logs, logger.Field("response_body", w.body.String()))
}
logs = append(logs, logger.Field("duration", cost))
if responseStatus >= 500 && responseStatus <= 599 {
logger.WithContext(c.Request.Context()).Errorw("HTTP Error", logs...)
} else {
logger.WithContext(c.Request.Context()).Infow("HTTP Request", logs...)
}
if responseStatus == 404 {
logger.WithContext(c.Request.Context()).Debugf("404 Not Found: Host:%s Path:%s IsPanDomain:%v", host, c.Request.URL.Path, svc.Config.Subscribe.PanDomain)
}
}
}
func maskSensitiveFields(data []byte, fieldsToMask []string) []byte {
var jsonData map[string]interface{}
if err := json.Unmarshal(data, &jsonData); err != nil {
return data
}
for _, field := range fieldsToMask {
if _, exists := jsonData[field]; exists {
jsonData[field] = "***" // use *** to mask sensitive fields
}
}
maskedData, err := json.Marshal(jsonData)
if err != nil {
return data
}
return maskedData
}