mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-05-08 02:52:36 +02:00
Release prep: 54 engines, self-hosted signatures, i18n, dashboard updates
This commit is contained in:
parent
694e32be26
commit
41cbfd6e0a
178 changed files with 36008 additions and 399 deletions
165
internal/application/resilience/behavioral.go
Normal file
165
internal/application/resilience/behavioral.go
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
package resilience
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BehaviorProfile captures the runtime behavior of a component.
|
||||
type BehaviorProfile struct {
|
||||
Goroutines int `json:"goroutines"`
|
||||
HeapAllocMB float64 `json:"heap_alloc_mb"`
|
||||
HeapObjectsK float64 `json:"heap_objects_k"`
|
||||
GCPauseMs float64 `json:"gc_pause_ms"`
|
||||
NumGC uint32 `json:"num_gc"`
|
||||
FileDescriptors int `json:"file_descriptors,omitempty"`
|
||||
CustomMetrics map[string]float64 `json:"custom_metrics,omitempty"`
|
||||
}
|
||||
|
||||
// BehavioralAlert is emitted when a behavioral anomaly is detected.
|
||||
type BehavioralAlert struct {
|
||||
Component string `json:"component"`
|
||||
AnomalyType string `json:"anomaly_type"` // goroutine_leak, memory_leak, gc_pressure, etc.
|
||||
Metric string `json:"metric"`
|
||||
Current float64 `json:"current"`
|
||||
Baseline float64 `json:"baseline"`
|
||||
ZScore float64 `json:"z_score"`
|
||||
Severity string `json:"severity"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
// BehavioralAnalyzer provides Go-side runtime behavioral analysis.
|
||||
// It profiles the current process and compares against learned baselines.
|
||||
// On Linux, eBPF hooks (immune/resilience_hooks.c) extend this to kernel level.
|
||||
type BehavioralAnalyzer struct {
|
||||
mu sync.RWMutex
|
||||
metricsDB *MetricsDB
|
||||
alertBus chan BehavioralAlert
|
||||
interval time.Duration
|
||||
component string // self component name
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// NewBehavioralAnalyzer creates a new behavioral analyzer.
|
||||
func NewBehavioralAnalyzer(component string, alertBufSize int) *BehavioralAnalyzer {
|
||||
if alertBufSize <= 0 {
|
||||
alertBufSize = 50
|
||||
}
|
||||
return &BehavioralAnalyzer{
|
||||
metricsDB: NewMetricsDB(DefaultMetricsWindow, DefaultMetricsMaxSize),
|
||||
alertBus: make(chan BehavioralAlert, alertBufSize),
|
||||
interval: 1 * time.Minute,
|
||||
component: component,
|
||||
logger: slog.Default().With("component", "sarl-behavioral"),
|
||||
}
|
||||
}
|
||||
|
||||
// AlertBus returns the channel for consuming behavioral alerts.
|
||||
func (ba *BehavioralAnalyzer) AlertBus() <-chan BehavioralAlert {
|
||||
return ba.alertBus
|
||||
}
|
||||
|
||||
// Start begins continuous behavioral monitoring. Blocks until ctx cancelled.
|
||||
func (ba *BehavioralAnalyzer) Start(ctx context.Context) {
|
||||
ba.logger.Info("behavioral analyzer started", "interval", ba.interval)
|
||||
|
||||
ticker := time.NewTicker(ba.interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
ba.logger.Info("behavioral analyzer stopped")
|
||||
return
|
||||
case <-ticker.C:
|
||||
ba.collectAndAnalyze()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collectAndAnalyze profiles runtime and checks for anomalies.
|
||||
func (ba *BehavioralAnalyzer) collectAndAnalyze() {
|
||||
profile := ba.collectProfile()
|
||||
ba.storeMetrics(profile)
|
||||
ba.detectAnomalies(profile)
|
||||
}
|
||||
|
||||
// collectProfile gathers current Go runtime stats.
|
||||
func (ba *BehavioralAnalyzer) collectProfile() BehaviorProfile {
|
||||
var mem runtime.MemStats
|
||||
runtime.ReadMemStats(&mem)
|
||||
|
||||
return BehaviorProfile{
|
||||
Goroutines: runtime.NumGoroutine(),
|
||||
HeapAllocMB: float64(mem.HeapAlloc) / (1024 * 1024),
|
||||
HeapObjectsK: float64(mem.HeapObjects) / 1000,
|
||||
GCPauseMs: float64(mem.PauseNs[(mem.NumGC+255)%256]) / 1e6,
|
||||
NumGC: mem.NumGC,
|
||||
}
|
||||
}
|
||||
|
||||
// storeMetrics records profile data in the time-series DB.
|
||||
func (ba *BehavioralAnalyzer) storeMetrics(p BehaviorProfile) {
|
||||
ba.metricsDB.AddDataPoint(ba.component, "goroutines", float64(p.Goroutines))
|
||||
ba.metricsDB.AddDataPoint(ba.component, "heap_alloc_mb", p.HeapAllocMB)
|
||||
ba.metricsDB.AddDataPoint(ba.component, "heap_objects_k", p.HeapObjectsK)
|
||||
ba.metricsDB.AddDataPoint(ba.component, "gc_pause_ms", p.GCPauseMs)
|
||||
}
|
||||
|
||||
// detectAnomalies checks each metric against its baseline via Z-score.
|
||||
func (ba *BehavioralAnalyzer) detectAnomalies(p BehaviorProfile) {
|
||||
checks := []struct {
|
||||
metric string
|
||||
value float64
|
||||
anomalyType string
|
||||
severity string
|
||||
}{
|
||||
{"goroutines", float64(p.Goroutines), "goroutine_leak", "WARNING"},
|
||||
{"heap_alloc_mb", p.HeapAllocMB, "memory_leak", "CRITICAL"},
|
||||
{"heap_objects_k", p.HeapObjectsK, "object_leak", "WARNING"},
|
||||
{"gc_pause_ms", p.GCPauseMs, "gc_pressure", "WARNING"},
|
||||
}
|
||||
|
||||
for _, c := range checks {
|
||||
baseline := ba.metricsDB.GetBaseline(ba.component, c.metric, DefaultMetricsWindow)
|
||||
if !IsAnomaly(c.value, baseline, AnomalyZScoreThreshold) {
|
||||
continue
|
||||
}
|
||||
|
||||
zscore := CalculateZScore(c.value, baseline)
|
||||
alert := BehavioralAlert{
|
||||
Component: ba.component,
|
||||
AnomalyType: c.anomalyType,
|
||||
Metric: c.metric,
|
||||
Current: c.value,
|
||||
Baseline: baseline.Mean,
|
||||
ZScore: zscore,
|
||||
Severity: c.severity,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
select {
|
||||
case ba.alertBus <- alert:
|
||||
ba.logger.Warn("behavioral anomaly detected",
|
||||
"type", c.anomalyType,
|
||||
"metric", c.metric,
|
||||
"z_score", zscore,
|
||||
)
|
||||
default:
|
||||
ba.logger.Error("behavioral alert bus full")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InjectMetric allows manually injecting a metric for testing.
|
||||
func (ba *BehavioralAnalyzer) InjectMetric(metric string, value float64) {
|
||||
ba.metricsDB.AddDataPoint(ba.component, metric, value)
|
||||
}
|
||||
|
||||
// CurrentProfile returns a snapshot of the current runtime profile.
|
||||
func (ba *BehavioralAnalyzer) CurrentProfile() BehaviorProfile {
|
||||
return ba.collectProfile()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue