mirror of https://github.com/jetkvm/kvm.git
146 lines
4.2 KiB
Go
146 lines
4.2 KiB
Go
package audio
|
|
|
|
import (
|
|
"runtime"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/jetkvm/kvm/internal/logging"
|
|
)
|
|
|
|
// GoroutineMonitor tracks goroutine count and provides cleanup mechanisms
|
|
type GoroutineMonitor struct {
|
|
baselineCount int
|
|
peakCount int
|
|
lastCount int
|
|
monitorInterval time.Duration
|
|
lastCheck time.Time
|
|
enabled int32
|
|
}
|
|
|
|
// Global goroutine monitor instance
|
|
var globalGoroutineMonitor *GoroutineMonitor
|
|
|
|
// NewGoroutineMonitor creates a new goroutine monitor
|
|
func NewGoroutineMonitor(monitorInterval time.Duration) *GoroutineMonitor {
|
|
if monitorInterval <= 0 {
|
|
monitorInterval = 30 * time.Second
|
|
}
|
|
|
|
// Get current goroutine count as baseline
|
|
baselineCount := runtime.NumGoroutine()
|
|
|
|
return &GoroutineMonitor{
|
|
baselineCount: baselineCount,
|
|
peakCount: baselineCount,
|
|
lastCount: baselineCount,
|
|
monitorInterval: monitorInterval,
|
|
lastCheck: time.Now(),
|
|
}
|
|
}
|
|
|
|
// Start begins goroutine monitoring
|
|
func (gm *GoroutineMonitor) Start() {
|
|
if !atomic.CompareAndSwapInt32(&gm.enabled, 0, 1) {
|
|
return // Already running
|
|
}
|
|
|
|
go gm.monitorLoop()
|
|
}
|
|
|
|
// Stop stops goroutine monitoring
|
|
func (gm *GoroutineMonitor) Stop() {
|
|
atomic.StoreInt32(&gm.enabled, 0)
|
|
}
|
|
|
|
// monitorLoop periodically checks goroutine count
|
|
func (gm *GoroutineMonitor) monitorLoop() {
|
|
logger := logging.GetDefaultLogger().With().Str("component", "goroutine-monitor").Logger()
|
|
logger.Info().Int("baseline", gm.baselineCount).Msg("goroutine monitor started")
|
|
|
|
for atomic.LoadInt32(&gm.enabled) == 1 {
|
|
time.Sleep(gm.monitorInterval)
|
|
gm.checkGoroutineCount()
|
|
}
|
|
|
|
logger.Info().Msg("goroutine monitor stopped")
|
|
}
|
|
|
|
// checkGoroutineCount checks current goroutine count and logs if it exceeds thresholds
|
|
func (gm *GoroutineMonitor) checkGoroutineCount() {
|
|
currentCount := runtime.NumGoroutine()
|
|
gm.lastCount = currentCount
|
|
|
|
// Update peak count if needed
|
|
if currentCount > gm.peakCount {
|
|
gm.peakCount = currentCount
|
|
}
|
|
|
|
// Calculate growth since baseline
|
|
growth := currentCount - gm.baselineCount
|
|
growthPercent := float64(growth) / float64(gm.baselineCount) * 100
|
|
|
|
// Log warning if growth exceeds thresholds
|
|
logger := logging.GetDefaultLogger().With().Str("component", "goroutine-monitor").Logger()
|
|
|
|
// Different log levels based on growth severity
|
|
if growthPercent > 30 {
|
|
// Severe growth - trigger cleanup
|
|
logger.Warn().Int("current", currentCount).Int("baseline", gm.baselineCount).
|
|
Int("growth", growth).Float64("growth_percent", growthPercent).
|
|
Msg("excessive goroutine growth detected - triggering cleanup")
|
|
|
|
// Force garbage collection to clean up unused resources
|
|
runtime.GC()
|
|
|
|
// Force cleanup of goroutine buffer cache
|
|
cleanupGoroutineCache()
|
|
} else if growthPercent > 20 {
|
|
// Moderate growth - just log warning
|
|
logger.Warn().Int("current", currentCount).Int("baseline", gm.baselineCount).
|
|
Int("growth", growth).Float64("growth_percent", growthPercent).
|
|
Msg("significant goroutine growth detected")
|
|
} else if growthPercent > 10 {
|
|
// Minor growth - log info
|
|
logger.Info().Int("current", currentCount).Int("baseline", gm.baselineCount).
|
|
Int("growth", growth).Float64("growth_percent", growthPercent).
|
|
Msg("goroutine growth detected")
|
|
}
|
|
|
|
// Update last check time
|
|
gm.lastCheck = time.Now()
|
|
}
|
|
|
|
// GetGoroutineStats returns current goroutine statistics
|
|
func (gm *GoroutineMonitor) GetGoroutineStats() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"current_count": gm.lastCount,
|
|
"baseline_count": gm.baselineCount,
|
|
"peak_count": gm.peakCount,
|
|
"growth": gm.lastCount - gm.baselineCount,
|
|
"growth_percent": float64(gm.lastCount-gm.baselineCount) / float64(gm.baselineCount) * 100,
|
|
"last_check": gm.lastCheck,
|
|
}
|
|
}
|
|
|
|
// GetGoroutineMonitor returns the global goroutine monitor instance
|
|
func GetGoroutineMonitor() *GoroutineMonitor {
|
|
if globalGoroutineMonitor == nil {
|
|
globalGoroutineMonitor = NewGoroutineMonitor(GetConfig().GoroutineMonitorInterval)
|
|
}
|
|
return globalGoroutineMonitor
|
|
}
|
|
|
|
// StartGoroutineMonitoring starts the global goroutine monitor
|
|
func StartGoroutineMonitoring() {
|
|
monitor := GetGoroutineMonitor()
|
|
monitor.Start()
|
|
}
|
|
|
|
// StopGoroutineMonitoring stops the global goroutine monitor
|
|
func StopGoroutineMonitoring() {
|
|
if globalGoroutineMonitor != nil {
|
|
globalGoroutineMonitor.Stop()
|
|
}
|
|
}
|