Files
lijiaoqiao/supply-api/internal/audit/service/audit_sampling.go

172 lines
3.9 KiB
Go
Raw Normal View History

package service
import (
"math/rand"
"strings"
"sync"
)
// ==================== P1-04 审计事件采样策略 ====================
// AuditSamplingConfig 审计采样配置
type AuditSamplingConfig struct {
// 成功事件采样率 (0.0 - 1.0)
// 0.01 = 1% 采样
SuccessSampleRate float64
// 是否启用采样
Enabled bool
// 强制全量记录的事件类型(不受采样影响)
ForceRecordEventTypes []string
}
// DefaultAuditSamplingConfig 默认审计采样配置
func DefaultAuditSamplingConfig() *AuditSamplingConfig {
return &AuditSamplingConfig{
Enabled: true,
SuccessSampleRate: 0.01, // 1% 采样
ForceRecordEventTypes: []string{
"token.authn.fail",
"token.authz.fail",
"token.revoked",
"account.created",
"account.deleted",
"settlement.completed",
"payment.processed",
"admin.action",
},
}
}
// AuditSampler 审计事件采样器
type AuditSampler struct {
config *AuditSamplingConfig
sampled *rand.Rand
mu sync.Mutex
}
// NewAuditSampler 创建审计采样器
func NewAuditSampler(config *AuditSamplingConfig) *AuditSampler {
if config == nil {
config = DefaultAuditSamplingConfig()
}
return &AuditSampler{
config: config,
sampled: rand.New(rand.NewSource(42)), // 确定性采样
}
}
// ShouldRecord 判断事件是否应该记录
// 返回 true 表示记录false 表示采样丢弃
func (s *AuditSampler) ShouldRecord(eventName string, success bool) bool {
if !s.config.Enabled {
return true
}
// 失败事件总是记录
if !success {
return true
}
// 强制记录类型总是记录(支持前缀匹配)
for _, forcedType := range s.config.ForceRecordEventTypes {
if eventName == forcedType || strings.HasPrefix(eventName, forcedType) {
return true
}
}
// 成功事件按采样率决定
return s.ShouldSample()
}
// ShouldSample 判断成功事件是否应该采样
func (s *AuditSampler) ShouldSample() bool {
s.mu.Lock()
defer s.mu.Unlock()
r := s.sampled.Float64()
return r < s.config.SuccessSampleRate
}
// RecordRate 返回当前采样率
func (s *AuditSampler) RecordRate() float64 {
return 1.0 - s.config.SuccessSampleRate
}
// GetConfig 返回采样配置
func (s *AuditSampler) GetConfig() *AuditSamplingConfig {
return s.config
}
// AuditEventClassifier 审计事件分类器
type AuditEventClassifier struct{}
// NewAuditEventClassifier 创建事件分类器
func NewAuditEventClassifier() *AuditEventClassifier {
return &AuditEventClassifier{}
}
// IsHighPriorityEvent 判断是否为高优先级事件(失败事件)
func (c *AuditEventClassifier) IsHighPriorityEvent(eventName string, success bool) bool {
if !success {
return true
}
highPriorityPrefixes := []string{
"token.authn.fail",
"token.authz.fail",
"token.revoked",
"account.",
"settlement.",
"payment.",
"admin.",
}
lowPriorityPrefixes := []string{
"token.authn.success",
"token.access",
"api.request",
}
// 检查是否低优先级
for _, prefix := range lowPriorityPrefixes {
if strings.HasPrefix(eventName, prefix) {
return false
}
}
// 检查是否高优先级
for _, prefix := range highPriorityPrefixes {
if strings.HasPrefix(eventName, prefix) {
return true
}
}
return false
}
// GetSamplingRecommendation 获取采样建议
func (c *AuditEventClassifier) GetSamplingRecommendation(eventName string) string {
if strings.HasPrefix(eventName, "token.authn.success") {
return "1% sampling recommended"
}
if strings.HasPrefix(eventName, "token.authn.fail") {
return "100% record required"
}
if strings.HasPrefix(eventName, "account.") {
return "100% record required"
}
if strings.HasPrefix(eventName, "settlement.") {
return "100% record required"
}
if strings.HasPrefix(eventName, "payment.") {
return "100% record required"
}
if strings.HasPrefix(eventName, "api.request") {
return "1% sampling recommended"
}
return "10% sampling recommended"
}