package metrics import ( "sync" "time" "dbbackup/internal/logger" ) // OperationMetrics holds performance metrics for database operations type OperationMetrics struct { Operation string `json:"operation"` Database string `json:"database"` StartTime time.Time `json:"start_time"` Duration time.Duration `json:"duration"` SizeBytes int64 `json:"size_bytes"` CompressionRatio float64 `json:"compression_ratio,omitempty"` ThroughputMBps float64 `json:"throughput_mbps"` ErrorCount int `json:"error_count"` Success bool `json:"success"` } // MetricsCollector collects and reports operation metrics type MetricsCollector struct { metrics []OperationMetrics mu sync.RWMutex logger logger.Logger } // NewMetricsCollector creates a new metrics collector func NewMetricsCollector(log logger.Logger) *MetricsCollector { return &MetricsCollector{ metrics: make([]OperationMetrics, 0), logger: log, } } // RecordOperation records metrics for a completed operation func (mc *MetricsCollector) RecordOperation(operation, database string, start time.Time, sizeBytes int64, success bool, errorCount int) { duration := time.Since(start) throughput := calculateThroughput(sizeBytes, duration) metric := OperationMetrics{ Operation: operation, Database: database, StartTime: start, Duration: duration, SizeBytes: sizeBytes, ThroughputMBps: throughput, ErrorCount: errorCount, Success: success, } mc.mu.Lock() mc.metrics = append(mc.metrics, metric) mc.mu.Unlock() // Log structured metrics if mc.logger != nil { fields := map[string]interface{}{ "metric_type": "operation_complete", "operation": operation, "database": database, "duration_ms": duration.Milliseconds(), "size_bytes": sizeBytes, "throughput_mbps": throughput, "error_count": errorCount, "success": success, } if success { mc.logger.WithFields(fields).Info("Operation completed successfully") } else { mc.logger.WithFields(fields).Error("Operation failed") } } } // RecordCompressionRatio updates compression ratio for a recorded operation func (mc *MetricsCollector) RecordCompressionRatio(operation, database string, ratio float64) { mc.mu.Lock() defer mc.mu.Unlock() // Find and update the most recent matching operation for i := len(mc.metrics) - 1; i >= 0; i-- { if mc.metrics[i].Operation == operation && mc.metrics[i].Database == database { mc.metrics[i].CompressionRatio = ratio break } } } // GetMetrics returns a copy of all collected metrics func (mc *MetricsCollector) GetMetrics() []OperationMetrics { mc.mu.RLock() defer mc.mu.RUnlock() result := make([]OperationMetrics, len(mc.metrics)) copy(result, mc.metrics) return result } // GetAverages calculates average performance metrics func (mc *MetricsCollector) GetAverages() map[string]interface{} { mc.mu.RLock() defer mc.mu.RUnlock() if len(mc.metrics) == 0 { return map[string]interface{}{} } var totalDuration time.Duration var totalSize, totalThroughput float64 var successCount, errorCount int for _, m := range mc.metrics { totalDuration += m.Duration totalSize += float64(m.SizeBytes) totalThroughput += m.ThroughputMBps if m.Success { successCount++ } errorCount += m.ErrorCount } count := len(mc.metrics) return map[string]interface{}{ "total_operations": count, "success_rate": float64(successCount) / float64(count) * 100, "avg_duration_ms": totalDuration.Milliseconds() / int64(count), "avg_size_mb": totalSize / float64(count) / 1024 / 1024, "avg_throughput_mbps": totalThroughput / float64(count), "total_errors": errorCount, } } // Clear removes all collected metrics func (mc *MetricsCollector) Clear() { mc.mu.Lock() defer mc.mu.Unlock() mc.metrics = make([]OperationMetrics, 0) } // calculateThroughput calculates MB/s throughput func calculateThroughput(bytes int64, duration time.Duration) float64 { if duration == 0 { return 0 } seconds := duration.Seconds() if seconds == 0 { return 0 } return float64(bytes) / seconds / 1024 / 1024 } // Global metrics collector instance var GlobalMetrics *MetricsCollector // InitGlobalMetrics initializes the global metrics collector func InitGlobalMetrics(log logger.Logger) { GlobalMetrics = NewMetricsCollector(log) }