mirror of
https://github.com/EZ-Api/ez-api.git
synced 2026-01-13 17:47:51 +00:00
feat(api): add log_request_body_enabled feature flag support
Add runtime feature flag to control whether request bodies are stored in logs. The Handler now accepts a Redis client to check the log_request_body_enabled feature flag before persisting log records. - Add logRequestBodyFeatureKey constant for feature flag - Inject Redis client into Handler for feature flag lookups - Strip request body from log records when feature is disabled - Update tests to pass Redis client to NewHandler
This commit is contained in:
@@ -119,7 +119,7 @@ func main() {
|
|||||||
masterService := service.NewMasterService(db)
|
masterService := service.NewMasterService(db)
|
||||||
healthService := service.NewHealthCheckService(db, rdb)
|
healthService := service.NewHealthCheckService(db, rdb)
|
||||||
|
|
||||||
handler := api.NewHandler(db, syncService, logWriter)
|
handler := api.NewHandler(db, syncService, logWriter, rdb)
|
||||||
adminHandler := api.NewAdminHandler(db, masterService, syncService)
|
adminHandler := api.NewAdminHandler(db, masterService, syncService)
|
||||||
masterHandler := api.NewMasterHandler(db, masterService, syncService)
|
masterHandler := api.NewMasterHandler(db, masterService, syncService)
|
||||||
internalHandler := api.NewInternalHandler(db)
|
internalHandler := api.NewInternalHandler(db)
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ import (
|
|||||||
|
|
||||||
const featuresKey = "meta:features"
|
const featuresKey = "meta:features"
|
||||||
const (
|
const (
|
||||||
logRetentionFeatureKey = "log_retention_days"
|
logRetentionFeatureKey = "log_retention_days"
|
||||||
logMaxRecordsFeatureKey = "log_max_records"
|
logMaxRecordsFeatureKey = "log_max_records"
|
||||||
logRetentionRedisKey = "meta:log:retention_days"
|
logRequestBodyFeatureKey = "log_request_body_enabled"
|
||||||
logMaxRecordsRedisKey = "meta:log:max_records"
|
logRetentionRedisKey = "meta:log:retention_days"
|
||||||
|
logMaxRecordsRedisKey = "meta:log:max_records"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FeatureHandler manages lightweight feature flags stored in Redis for DP/CP runtime toggles.
|
// FeatureHandler manages lightweight feature flags stored in Redis for DP/CP runtime toggles.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -11,6 +12,7 @@ import (
|
|||||||
groupx "github.com/ez-api/foundation/group"
|
groupx "github.com/ez-api/foundation/group"
|
||||||
"github.com/ez-api/foundation/provider"
|
"github.com/ez-api/foundation/provider"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,10 +20,11 @@ type Handler struct {
|
|||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
sync *service.SyncService
|
sync *service.SyncService
|
||||||
logger *service.LogWriter
|
logger *service.LogWriter
|
||||||
|
rdb *redis.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandler(db *gorm.DB, sync *service.SyncService, logger *service.LogWriter) *Handler {
|
func NewHandler(db *gorm.DB, sync *service.SyncService, logger *service.LogWriter, rdb *redis.Client) *Handler {
|
||||||
return &Handler{db: db, sync: sync, logger: logger}
|
return &Handler{db: db, sync: sync, logger: logger, rdb: rdb}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateKey is now handled by MasterHandler
|
// CreateKey is now handled by MasterHandler
|
||||||
@@ -463,7 +466,25 @@ func (h *Handler) IngestLog(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !h.logRequestBodyEnabled(c.Request.Context()) {
|
||||||
|
rec.RequestBody = ""
|
||||||
|
}
|
||||||
|
|
||||||
// By default, only metadata is expected; payload fields may be empty.
|
// By default, only metadata is expected; payload fields may be empty.
|
||||||
h.logger.Write(rec)
|
h.logger.Write(rec)
|
||||||
c.JSON(http.StatusAccepted, gin.H{"status": "queued"})
|
c.JSON(http.StatusAccepted, gin.H{"status": "queued"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) logRequestBodyEnabled(ctx context.Context) bool {
|
||||||
|
if h == nil || h.rdb == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
raw, err := h.rdb.HGet(ctx, featuresKey, logRequestBodyFeatureKey).Result()
|
||||||
|
if err == redis.Nil || strings.TrimSpace(raw) == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return strings.EqualFold(raw, "true") || strings.EqualFold(raw, "1")
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func newTestHandlerWithRedis(t *testing.T) (*Handler, *gorm.DB, *miniredis.Minir
|
|||||||
mr := miniredis.RunT(t)
|
mr := miniredis.RunT(t)
|
||||||
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
||||||
sync := service.NewSyncService(rdb)
|
sync := service.NewSyncService(rdb)
|
||||||
return NewHandler(db, sync, nil), db, mr
|
return NewHandler(db, sync, nil, rdb), db, mr
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateModel_DefaultsKindChat_AndWritesModelsMeta(t *testing.T) {
|
func TestCreateModel_DefaultsKindChat_AndWritesModelsMeta(t *testing.T) {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func newTestHandler(t *testing.T) (*Handler, *gorm.DB) {
|
|||||||
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
||||||
sync := service.NewSyncService(rdb)
|
sync := service.NewSyncService(rdb)
|
||||||
|
|
||||||
return NewHandler(db, sync, nil), db
|
return NewHandler(db, sync, nil, rdb), db
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateProvider_DefaultsVertexLocationGlobal(t *testing.T) {
|
func TestCreateProvider_DefaultsVertexLocationGlobal(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user