You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							233 lines
						
					
					
						
							6.6 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							233 lines
						
					
					
						
							6.6 KiB
						
					
					
				| package protocol | |
| 
 | |
| import ( | |
| 	"sync" | |
| 	"sync/atomic" | |
| 	"time" | |
| ) | |
| 
 | |
| // Metrics tracks basic request/error/latency statistics for Kafka protocol operations | |
| type Metrics struct { | |
| 	// Request counters by API key | |
| 	requestCounts map[uint16]*int64 | |
| 	errorCounts   map[uint16]*int64 | |
| 
 | |
| 	// Latency tracking | |
| 	latencySum   map[uint16]*int64 // Total latency in microseconds | |
| 	latencyCount map[uint16]*int64 // Number of requests for average calculation | |
|  | |
| 	// Connection metrics | |
| 	activeConnections int64 | |
| 	totalConnections  int64 | |
| 
 | |
| 	// Mutex for map operations | |
| 	mu sync.RWMutex | |
| 
 | |
| 	// Start time for uptime calculation | |
| 	startTime time.Time | |
| } | |
| 
 | |
| // APIMetrics represents metrics for a specific API | |
| type APIMetrics struct { | |
| 	APIKey       uint16  `json:"api_key"` | |
| 	APIName      string  `json:"api_name"` | |
| 	RequestCount int64   `json:"request_count"` | |
| 	ErrorCount   int64   `json:"error_count"` | |
| 	AvgLatencyMs float64 `json:"avg_latency_ms"` | |
| } | |
| 
 | |
| // ConnectionMetrics represents connection-related metrics | |
| type ConnectionMetrics struct { | |
| 	ActiveConnections int64     `json:"active_connections"` | |
| 	TotalConnections  int64     `json:"total_connections"` | |
| 	UptimeSeconds     int64     `json:"uptime_seconds"` | |
| 	StartTime         time.Time `json:"start_time"` | |
| } | |
| 
 | |
| // MetricsSnapshot represents a complete metrics snapshot | |
| type MetricsSnapshot struct { | |
| 	APIs        []APIMetrics      `json:"apis"` | |
| 	Connections ConnectionMetrics `json:"connections"` | |
| 	Timestamp   time.Time         `json:"timestamp"` | |
| } | |
| 
 | |
| // NewMetrics creates a new metrics tracker | |
| func NewMetrics() *Metrics { | |
| 	return &Metrics{ | |
| 		requestCounts: make(map[uint16]*int64), | |
| 		errorCounts:   make(map[uint16]*int64), | |
| 		latencySum:    make(map[uint16]*int64), | |
| 		latencyCount:  make(map[uint16]*int64), | |
| 		startTime:     time.Now(), | |
| 	} | |
| } | |
| 
 | |
| // RecordRequest records a successful request with latency | |
| func (m *Metrics) RecordRequest(apiKey uint16, latency time.Duration) { | |
| 	m.ensureCounters(apiKey) | |
| 
 | |
| 	atomic.AddInt64(m.requestCounts[apiKey], 1) | |
| 	atomic.AddInt64(m.latencySum[apiKey], latency.Microseconds()) | |
| 	atomic.AddInt64(m.latencyCount[apiKey], 1) | |
| } | |
| 
 | |
| // RecordError records an error for a specific API | |
| func (m *Metrics) RecordError(apiKey uint16, latency time.Duration) { | |
| 	m.ensureCounters(apiKey) | |
| 
 | |
| 	atomic.AddInt64(m.requestCounts[apiKey], 1) | |
| 	atomic.AddInt64(m.errorCounts[apiKey], 1) | |
| 	atomic.AddInt64(m.latencySum[apiKey], latency.Microseconds()) | |
| 	atomic.AddInt64(m.latencyCount[apiKey], 1) | |
| } | |
| 
 | |
| // RecordConnection records a new connection | |
| func (m *Metrics) RecordConnection() { | |
| 	atomic.AddInt64(&m.activeConnections, 1) | |
| 	atomic.AddInt64(&m.totalConnections, 1) | |
| } | |
| 
 | |
| // RecordDisconnection records a connection closure | |
| func (m *Metrics) RecordDisconnection() { | |
| 	atomic.AddInt64(&m.activeConnections, -1) | |
| } | |
| 
 | |
| // GetSnapshot returns a complete metrics snapshot | |
| func (m *Metrics) GetSnapshot() MetricsSnapshot { | |
| 	m.mu.RLock() | |
| 	defer m.mu.RUnlock() | |
| 
 | |
| 	apis := make([]APIMetrics, 0, len(m.requestCounts)) | |
| 
 | |
| 	for apiKey, requestCount := range m.requestCounts { | |
| 		requests := atomic.LoadInt64(requestCount) | |
| 		errors := atomic.LoadInt64(m.errorCounts[apiKey]) | |
| 		latencySum := atomic.LoadInt64(m.latencySum[apiKey]) | |
| 		latencyCount := atomic.LoadInt64(m.latencyCount[apiKey]) | |
| 
 | |
| 		var avgLatencyMs float64 | |
| 		if latencyCount > 0 { | |
| 			avgLatencyMs = float64(latencySum) / float64(latencyCount) / 1000.0 // Convert to milliseconds | |
| 		} | |
| 
 | |
| 		apis = append(apis, APIMetrics{ | |
| 			APIKey:       apiKey, | |
| 			APIName:      getAPIName(APIKey(apiKey)), | |
| 			RequestCount: requests, | |
| 			ErrorCount:   errors, | |
| 			AvgLatencyMs: avgLatencyMs, | |
| 		}) | |
| 	} | |
| 
 | |
| 	return MetricsSnapshot{ | |
| 		APIs: apis, | |
| 		Connections: ConnectionMetrics{ | |
| 			ActiveConnections: atomic.LoadInt64(&m.activeConnections), | |
| 			TotalConnections:  atomic.LoadInt64(&m.totalConnections), | |
| 			UptimeSeconds:     int64(time.Since(m.startTime).Seconds()), | |
| 			StartTime:         m.startTime, | |
| 		}, | |
| 		Timestamp: time.Now(), | |
| 	} | |
| } | |
| 
 | |
| // GetAPIMetrics returns metrics for a specific API | |
| func (m *Metrics) GetAPIMetrics(apiKey uint16) APIMetrics { | |
| 	m.ensureCounters(apiKey) | |
| 
 | |
| 	requests := atomic.LoadInt64(m.requestCounts[apiKey]) | |
| 	errors := atomic.LoadInt64(m.errorCounts[apiKey]) | |
| 	latencySum := atomic.LoadInt64(m.latencySum[apiKey]) | |
| 	latencyCount := atomic.LoadInt64(m.latencyCount[apiKey]) | |
| 
 | |
| 	var avgLatencyMs float64 | |
| 	if latencyCount > 0 { | |
| 		avgLatencyMs = float64(latencySum) / float64(latencyCount) / 1000.0 | |
| 	} | |
| 
 | |
| 	return APIMetrics{ | |
| 		APIKey:       apiKey, | |
| 		APIName:      getAPIName(APIKey(apiKey)), | |
| 		RequestCount: requests, | |
| 		ErrorCount:   errors, | |
| 		AvgLatencyMs: avgLatencyMs, | |
| 	} | |
| } | |
| 
 | |
| // GetConnectionMetrics returns connection-related metrics | |
| func (m *Metrics) GetConnectionMetrics() ConnectionMetrics { | |
| 	return ConnectionMetrics{ | |
| 		ActiveConnections: atomic.LoadInt64(&m.activeConnections), | |
| 		TotalConnections:  atomic.LoadInt64(&m.totalConnections), | |
| 		UptimeSeconds:     int64(time.Since(m.startTime).Seconds()), | |
| 		StartTime:         m.startTime, | |
| 	} | |
| } | |
| 
 | |
| // Reset resets all metrics (useful for testing) | |
| func (m *Metrics) Reset() { | |
| 	m.mu.Lock() | |
| 	defer m.mu.Unlock() | |
| 
 | |
| 	for apiKey := range m.requestCounts { | |
| 		atomic.StoreInt64(m.requestCounts[apiKey], 0) | |
| 		atomic.StoreInt64(m.errorCounts[apiKey], 0) | |
| 		atomic.StoreInt64(m.latencySum[apiKey], 0) | |
| 		atomic.StoreInt64(m.latencyCount[apiKey], 0) | |
| 	} | |
| 
 | |
| 	atomic.StoreInt64(&m.activeConnections, 0) | |
| 	atomic.StoreInt64(&m.totalConnections, 0) | |
| 	m.startTime = time.Now() | |
| } | |
| 
 | |
| // ensureCounters ensures that counters exist for the given API key | |
| func (m *Metrics) ensureCounters(apiKey uint16) { | |
| 	m.mu.RLock() | |
| 	if _, exists := m.requestCounts[apiKey]; exists { | |
| 		m.mu.RUnlock() | |
| 		return | |
| 	} | |
| 	m.mu.RUnlock() | |
| 
 | |
| 	m.mu.Lock() | |
| 	defer m.mu.Unlock() | |
| 
 | |
| 	// Double-check after acquiring write lock | |
| 	if _, exists := m.requestCounts[apiKey]; exists { | |
| 		return | |
| 	} | |
| 
 | |
| 	m.requestCounts[apiKey] = new(int64) | |
| 	m.errorCounts[apiKey] = new(int64) | |
| 	m.latencySum[apiKey] = new(int64) | |
| 	m.latencyCount[apiKey] = new(int64) | |
| } | |
| 
 | |
| // Global metrics instance | |
| var globalMetrics = NewMetrics() | |
| 
 | |
| // GetGlobalMetrics returns the global metrics instance | |
| func GetGlobalMetrics() *Metrics { | |
| 	return globalMetrics | |
| } | |
| 
 | |
| // RecordRequestMetrics is a convenience function to record request metrics globally | |
| func RecordRequestMetrics(apiKey uint16, latency time.Duration) { | |
| 	globalMetrics.RecordRequest(apiKey, latency) | |
| } | |
| 
 | |
| // RecordErrorMetrics is a convenience function to record error metrics globally | |
| func RecordErrorMetrics(apiKey uint16, latency time.Duration) { | |
| 	globalMetrics.RecordError(apiKey, latency) | |
| } | |
| 
 | |
| // RecordConnectionMetrics is a convenience function to record connection metrics globally | |
| func RecordConnectionMetrics() { | |
| 	globalMetrics.RecordConnection() | |
| } | |
| 
 | |
| // RecordDisconnectionMetrics is a convenience function to record disconnection metrics globally | |
| func RecordDisconnectionMetrics() { | |
| 	globalMetrics.RecordDisconnection() | |
| }
 |