277 lines
8.2 KiB
Go
277 lines
8.2 KiB
Go
package stats
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// ComponentStats represents statistics for a specific component
|
|
type ComponentStats struct {
|
|
Component string `json:"component"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Queries int64 `json:"queries"`
|
|
Errors int64 `json:"errors"`
|
|
ResponseTime float64 `json:"response_time_ms"`
|
|
Connections int64 `json:"connections"`
|
|
BytesServed int64 `json:"bytes_served"`
|
|
Metadata map[string]interface{} `json:"metadata"`
|
|
}
|
|
|
|
// DHTStats represents DHT-specific statistics
|
|
type DHTStats struct {
|
|
QueriesSent int64 `json:"queries_sent"`
|
|
QueriesReceived int64 `json:"queries_received"`
|
|
NodesInTable int `json:"nodes_in_routing_table"`
|
|
StoredPeers int `json:"stored_peers"`
|
|
AnnouncesSent int64 `json:"announces_sent"`
|
|
PeersSeen int64 `json:"peers_seen"`
|
|
LastAnnounce time.Time `json:"last_announce"`
|
|
ErrorRate float64 `json:"error_rate"`
|
|
}
|
|
|
|
// TrackerStats represents tracker-specific statistics
|
|
type TrackerStats struct {
|
|
ActiveTorrents int `json:"active_torrents"`
|
|
TotalPeers int `json:"total_peers"`
|
|
AnnouncesPerMin float64 `json:"announces_per_minute"`
|
|
ScrapeRequests int64 `json:"scrape_requests"`
|
|
AverageSwarmSize float64 `json:"average_swarm_size"`
|
|
LastActivity time.Time `json:"last_activity"`
|
|
}
|
|
|
|
// GatewayStats represents gateway-specific statistics
|
|
type GatewayStats struct {
|
|
UploadsPerHour float64 `json:"uploads_per_hour"`
|
|
DownloadsPerHour float64 `json:"downloads_per_hour"`
|
|
BandwidthUsed int64 `json:"bandwidth_used_bytes"`
|
|
ActiveUploads int `json:"active_uploads"`
|
|
ActiveDownloads int `json:"active_downloads"`
|
|
CacheHitRate float64 `json:"cache_hit_rate"`
|
|
AverageFileSize int64 `json:"average_file_size"`
|
|
}
|
|
|
|
// WebSocketStats represents WebSocket tracker statistics
|
|
type WebSocketStats struct {
|
|
ActiveConnections int `json:"active_connections"`
|
|
WebRTCPeers int `json:"webrtc_peers"`
|
|
MessagesPerSec float64 `json:"messages_per_second"`
|
|
ConnectionErrors int64 `json:"connection_errors"`
|
|
PeerExchanges int64 `json:"peer_exchanges"`
|
|
AverageLatency float64 `json:"average_latency_ms"`
|
|
}
|
|
|
|
// SystemStats represents overall system performance
|
|
type SystemStats struct {
|
|
CPUUsage float64 `json:"cpu_usage_percent"`
|
|
MemoryUsage int64 `json:"memory_usage_bytes"`
|
|
GoroutineCount int `json:"goroutine_count"`
|
|
ResponseTime float64 `json:"avg_response_time_ms"`
|
|
RequestsPerSec float64 `json:"requests_per_second"`
|
|
}
|
|
|
|
// BandwidthStats represents bandwidth usage tracking
|
|
type BandwidthStats struct {
|
|
TorrentHash string `json:"torrent_hash"`
|
|
BytesServed int64 `json:"bytes_served"`
|
|
BytesFromPeers int64 `json:"bytes_from_peers"`
|
|
P2POffloadPercent float64 `json:"p2p_offload_percent"`
|
|
PeerCount int `json:"peer_count"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
}
|
|
|
|
// TimeSeriesPoint represents a single point in time-series data
|
|
type TimeSeriesPoint struct {
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Value float64 `json:"value"`
|
|
Component string `json:"component"`
|
|
Metric string `json:"metric"`
|
|
}
|
|
|
|
// StatsCollector manages collection and aggregation of statistics
|
|
type StatsCollector struct {
|
|
mutex sync.RWMutex
|
|
dhtStats *DHTStats
|
|
trackerStats *TrackerStats
|
|
gatewayStats *GatewayStats
|
|
wsStats *WebSocketStats
|
|
systemStats *SystemStats
|
|
bandwidthMap map[string]*BandwidthStats
|
|
|
|
// Rate tracking
|
|
requestCounts map[string]int64
|
|
errorCounts map[string]int64
|
|
lastReset time.Time
|
|
|
|
// Performance tracking
|
|
responseTimes map[string][]float64
|
|
activeConns int64
|
|
}
|
|
|
|
// NewStatsCollector creates a new statistics collector
|
|
func NewStatsCollector() *StatsCollector {
|
|
return &StatsCollector{
|
|
dhtStats: &DHTStats{},
|
|
trackerStats: &TrackerStats{},
|
|
gatewayStats: &GatewayStats{},
|
|
wsStats: &WebSocketStats{},
|
|
systemStats: &SystemStats{},
|
|
bandwidthMap: make(map[string]*BandwidthStats),
|
|
requestCounts: make(map[string]int64),
|
|
errorCounts: make(map[string]int64),
|
|
responseTimes: make(map[string][]float64),
|
|
lastReset: time.Now(),
|
|
}
|
|
}
|
|
|
|
// RecordRequest increments request count for a component
|
|
func (sc *StatsCollector) RecordRequest(component string) {
|
|
sc.mutex.Lock()
|
|
defer sc.mutex.Unlock()
|
|
sc.requestCounts[component]++
|
|
}
|
|
|
|
// RecordError increments error count for a component
|
|
func (sc *StatsCollector) RecordError(component string) {
|
|
sc.mutex.Lock()
|
|
defer sc.mutex.Unlock()
|
|
sc.errorCounts[component]++
|
|
}
|
|
|
|
// RecordResponseTime records response time for a component
|
|
func (sc *StatsCollector) RecordResponseTime(component string, duration time.Duration) {
|
|
sc.mutex.Lock()
|
|
defer sc.mutex.Unlock()
|
|
ms := float64(duration.Nanoseconds()) / 1e6
|
|
if sc.responseTimes[component] == nil {
|
|
sc.responseTimes[component] = make([]float64, 0, 100)
|
|
}
|
|
sc.responseTimes[component] = append(sc.responseTimes[component], ms)
|
|
|
|
// Keep only last 100 measurements
|
|
if len(sc.responseTimes[component]) > 100 {
|
|
sc.responseTimes[component] = sc.responseTimes[component][1:]
|
|
}
|
|
}
|
|
|
|
// RecordBandwidth records bandwidth usage for a torrent
|
|
func (sc *StatsCollector) RecordBandwidth(torrentHash string, bytesServed, bytesFromPeers int64, peerCount int) {
|
|
sc.mutex.Lock()
|
|
defer sc.mutex.Unlock()
|
|
|
|
var p2pOffload float64
|
|
if bytesServed > 0 {
|
|
p2pOffload = float64(bytesFromPeers) / float64(bytesServed) * 100
|
|
}
|
|
|
|
sc.bandwidthMap[torrentHash] = &BandwidthStats{
|
|
TorrentHash: torrentHash,
|
|
BytesServed: bytesServed,
|
|
BytesFromPeers: bytesFromPeers,
|
|
P2POffloadPercent: p2pOffload,
|
|
PeerCount: peerCount,
|
|
Timestamp: time.Now(),
|
|
}
|
|
}
|
|
|
|
// GetAverageResponseTime calculates average response time for a component
|
|
func (sc *StatsCollector) GetAverageResponseTime(component string) float64 {
|
|
sc.mutex.RLock()
|
|
defer sc.mutex.RUnlock()
|
|
|
|
times := sc.responseTimes[component]
|
|
if len(times) == 0 {
|
|
return 0
|
|
}
|
|
|
|
var sum float64
|
|
for _, t := range times {
|
|
sum += t
|
|
}
|
|
return sum / float64(len(times))
|
|
}
|
|
|
|
// GetRequestRate calculates requests per second for a component
|
|
func (sc *StatsCollector) GetRequestRate(component string) float64 {
|
|
sc.mutex.RLock()
|
|
defer sc.mutex.RUnlock()
|
|
|
|
duration := time.Since(sc.lastReset).Seconds()
|
|
if duration == 0 {
|
|
return 0
|
|
}
|
|
return float64(sc.requestCounts[component]) / duration
|
|
}
|
|
|
|
// GetErrorRate calculates error rate percentage for a component
|
|
func (sc *StatsCollector) GetErrorRate(component string) float64 {
|
|
sc.mutex.RLock()
|
|
defer sc.mutex.RUnlock()
|
|
|
|
requests := sc.requestCounts[component]
|
|
if requests == 0 {
|
|
return 0
|
|
}
|
|
return float64(sc.errorCounts[component]) / float64(requests) * 100
|
|
}
|
|
|
|
// ResetCounters resets rate-based counters (called periodically)
|
|
func (sc *StatsCollector) ResetCounters() {
|
|
sc.mutex.Lock()
|
|
defer sc.mutex.Unlock()
|
|
|
|
// Reset counters but keep running totals for rates
|
|
sc.requestCounts = make(map[string]int64)
|
|
sc.errorCounts = make(map[string]int64)
|
|
sc.lastReset = time.Now()
|
|
}
|
|
|
|
// UpdateDHTStats updates DHT statistics
|
|
func (sc *StatsCollector) UpdateDHTStats(stats *DHTStats) {
|
|
sc.mutex.Lock()
|
|
defer sc.mutex.Unlock()
|
|
sc.dhtStats = stats
|
|
}
|
|
|
|
// UpdateTrackerStats updates tracker statistics
|
|
func (sc *StatsCollector) UpdateTrackerStats(stats *TrackerStats) {
|
|
sc.mutex.Lock()
|
|
defer sc.mutex.Unlock()
|
|
sc.trackerStats = stats
|
|
}
|
|
|
|
// UpdateGatewayStats updates gateway statistics
|
|
func (sc *StatsCollector) UpdateGatewayStats(stats *GatewayStats) {
|
|
sc.mutex.Lock()
|
|
defer sc.mutex.Unlock()
|
|
sc.gatewayStats = stats
|
|
}
|
|
|
|
// UpdateWebSocketStats updates WebSocket statistics
|
|
func (sc *StatsCollector) UpdateWebSocketStats(stats *WebSocketStats) {
|
|
sc.mutex.Lock()
|
|
defer sc.mutex.Unlock()
|
|
sc.wsStats = stats
|
|
}
|
|
|
|
// UpdateSystemStats updates system statistics
|
|
func (sc *StatsCollector) UpdateSystemStats(stats *SystemStats) {
|
|
sc.mutex.Lock()
|
|
defer sc.mutex.Unlock()
|
|
sc.systemStats = stats
|
|
}
|
|
|
|
// GetSnapshot returns a complete snapshot of current statistics
|
|
func (sc *StatsCollector) GetSnapshot() map[string]interface{} {
|
|
sc.mutex.RLock()
|
|
defer sc.mutex.RUnlock()
|
|
|
|
return map[string]interface{}{
|
|
"timestamp": time.Now().Format(time.RFC3339),
|
|
"dht": sc.dhtStats,
|
|
"tracker": sc.trackerStats,
|
|
"gateway": sc.gatewayStats,
|
|
"websocket": sc.wsStats,
|
|
"system": sc.systemStats,
|
|
"bandwidth": sc.bandwidthMap,
|
|
}
|
|
} |