Files
websocket-qris/internal/services/websocket/monitor.go
2025-10-24 12:33:10 +00:00

212 lines
6.1 KiB
Go

package websocket
import (
"fmt"
"time"
)
// DetailedStats menyimpan statistik detail WebSocket
type DetailedStats struct {
ConnectedClients int `json:"connected_clients"`
UniqueIPs int `json:"unique_ips"`
StaticClients int `json:"static_clients"`
ActiveRooms int `json:"active_rooms"`
IPDistribution map[string]int `json:"ip_distribution"`
RoomDistribution map[string]int `json:"room_distribution"`
MessageQueueSize int `json:"message_queue_size"`
QueueWorkers int `json:"queue_workers"`
Uptime time.Duration `json:"uptime_seconds"`
Timestamp int64 `json:"timestamp"`
}
// ClientInfo menyimpan informasi klien
type ClientInfo struct {
ID string `json:"id"`
StaticID string `json:"static_id"`
IPAddress string `json:"ip_address"`
UserID string `json:"user_id"`
Room string `json:"room"`
ConnectedAt time.Time `json:"connected_at"`
LastPing time.Time `json:"last_ping"`
LastPong time.Time `json:"last_pong"`
IsActive bool `json:"is_active"`
}
// MonitoringData menyimpan data monitoring lengkap
type MonitoringData struct {
Stats DetailedStats `json:"stats"`
RecentActivity []ActivityLog `json:"recent_activity"`
SystemHealth map[string]interface{} `json:"system_health"`
Performance PerformanceMetrics `json:"performance"`
}
// PerformanceMetrics menyimpan metrik performa
type PerformanceMetrics struct {
MessagesPerSecond float64 `json:"messages_per_second"`
AverageLatency float64 `json:"average_latency_ms"`
ErrorRate float64 `json:"error_rate_percent"`
MemoryUsage int64 `json:"memory_usage_bytes"`
}
// MonitoringManager mengelola monitoring WebSocket
type MonitoringManager struct {
hub *Hub
}
// NewMonitoringManager membuat manajer monitoring baru
func NewMonitoringManager(hub *Hub) *MonitoringManager {
return &MonitoringManager{hub: hub}
}
// GetDetailedStats mengembalikan statistik detail
func (m *MonitoringManager) GetDetailedStats() DetailedStats {
m.hub.mu.RLock()
defer m.hub.mu.RUnlock()
// Hitung distribusi IP
ipDistribution := make(map[string]int)
for ip, clients := range m.hub.clientsByIP {
ipDistribution[ip] = len(clients)
}
// Hitung distribusi ruangan
roomDistribution := make(map[string]int)
for room, clients := range m.hub.rooms {
roomDistribution[room] = len(clients)
}
return DetailedStats{
ConnectedClients: len(m.hub.clients),
UniqueIPs: len(m.hub.clientsByIP),
StaticClients: len(m.hub.clientsByStatic),
ActiveRooms: len(m.hub.rooms),
IPDistribution: ipDistribution,
RoomDistribution: roomDistribution,
MessageQueueSize: len(m.hub.messageQueue),
QueueWorkers: m.hub.config.QueueWorkers,
Uptime: time.Since(m.hub.startTime),
Timestamp: time.Now().Unix(),
}
}
// GetAllClients mengembalikan semua klien yang terhubung
func (m *MonitoringManager) GetAllClients() []ClientInfo {
m.hub.mu.RLock()
defer m.hub.mu.RUnlock()
var clients []ClientInfo
for client := range m.hub.clients {
clientInfo := ClientInfo{
ID: client.ID,
StaticID: client.StaticID,
IPAddress: client.IPAddress,
UserID: client.UserID,
Room: client.Room,
ConnectedAt: client.connectedAt,
LastPing: client.lastPing,
LastPong: client.lastPong,
IsActive: client.isClientActive(),
}
clients = append(clients, clientInfo)
}
return clients
}
// GetMonitoringData mengembalikan data monitoring lengkap
func (m *MonitoringManager) GetMonitoringData() MonitoringData {
stats := m.GetDetailedStats()
m.hub.activityMu.RLock()
recentActivity := make([]ActivityLog, 0)
// Dapatkan 100 aktivitas terakhir
start := len(m.hub.activityLog) - 100
if start < 0 {
start = 0
}
for i := start; i < len(m.hub.activityLog); i++ {
recentActivity = append(recentActivity, m.hub.activityLog[i])
}
m.hub.activityMu.RUnlock()
// Dapatkan kesehatan sistem dari layanan database
systemHealth := make(map[string]interface{})
if m.hub.dbService != nil {
systemHealth["databases"] = m.hub.dbService.Health()
systemHealth["available_dbs"] = m.hub.dbService.ListDBs()
}
systemHealth["websocket_status"] = "healthy"
systemHealth["uptime_seconds"] = time.Since(m.hub.startTime).Seconds()
// Hitung metrik performa
uptime := time.Since(m.hub.startTime)
var messagesPerSecond float64
var errorRate float64
if uptime.Seconds() > 0 {
messagesPerSecond = float64(m.hub.messageCount) / uptime.Seconds()
}
if m.hub.messageCount > 0 {
errorRate = (float64(m.hub.errorCount) / float64(m.hub.messageCount)) * 100
}
performance := PerformanceMetrics{
MessagesPerSecond: messagesPerSecond,
AverageLatency: 2.5, // Nilai mock - implementasi pelacakan latensi aktual
ErrorRate: errorRate,
MemoryUsage: 0, // Nilai mock - implementasi pelacakan memori aktual
}
return MonitoringData{
Stats: stats,
RecentActivity: recentActivity,
SystemHealth: systemHealth,
Performance: performance,
}
}
// CleanupInactiveClients membersihkan klien tidak aktif
func (m *MonitoringManager) CleanupInactiveClients(inactiveTimeout time.Duration) int {
m.hub.mu.RLock()
var inactiveClients []*Client
cutoff := time.Now().Add(-inactiveTimeout)
for client := range m.hub.clients {
if client.lastPing.Before(cutoff) {
inactiveClients = append(inactiveClients, client)
}
}
m.hub.mu.RUnlock()
// Putuskan koneksi klien tidak aktif
for _, client := range inactiveClients {
m.hub.logActivity("cleanup_disconnect", client.ID,
fmt.Sprintf("Inactive for %v", time.Since(client.lastPing)))
client.cancel()
client.Conn.Close()
}
return len(inactiveClients)
}
// DisconnectClient memutuskan koneksi klien tertentu
func (m *MonitoringManager) DisconnectClient(clientID string) bool {
m.hub.mu.RLock()
client, exists := m.hub.clientsByID[clientID]
m.hub.mu.RUnlock()
if !exists {
return false
}
// Log aktivitas
m.hub.logActivity("force_disconnect", clientID, "Client disconnected by admin")
// Batalkan context dan tutup koneksi
client.cancel()
client.Conn.Close()
return true
}