using EonaCat.DoxaApi.Models; namespace EonaCat.DoxaApi.Generation { // This file is part of the EonaCat project(s) which is released under the Apache License. // See the LICENSE file or go to https://EonaCat.com/license for full license details. /// /// Collects and manages API metrics and analytics. /// public class MetricsCollector { private readonly Dictionary _metrics = new(); private readonly List _requestHistory = new(); private readonly object _lock = new(); private readonly DateTime _startTime = DateTime.UtcNow; /// /// Records a request to an endpoint. /// public void RecordRequest(string method, string path, int statusCode, long responseTimeMs, long responseSize, string? userAgent = null, string? ipAddress = null) { lock (_lock) { var key = $"{method}:{path}"; if (!_metrics.ContainsKey(key)) { _metrics[key] = new ApiMetrics { EndpointId = key, Method = method, Path = path, CreatedAt = DateTime.UtcNow }; } var metric = _metrics[key]; metric.TotalRequests++; metric.LastRequestAt = DateTime.UtcNow; if (statusCode >= 200 && statusCode < 300) { metric.SuccessfulRequests++; } else if (statusCode >= 400) { metric.FailedRequests++; } // Track status codes if (!metric.StatusCodes.ContainsKey(statusCode)) { metric.StatusCodes[statusCode] = 0; } metric.StatusCodes[statusCode]++; // Calculate running average response time metric.AverageResponseTimeMs = (metric.AverageResponseTimeMs * (metric.TotalRequests - 1) + responseTimeMs) / metric.TotalRequests; // Calculate running average response size metric.AverageResponseSize = (metric.AverageResponseSize * (metric.TotalRequests - 1) + responseSize) / metric.TotalRequests; // Add to request history (keep last 1000) _requestHistory.Add(new RequestMetadata { Method = method, Path = path, StatusCode = statusCode, ResponseTimeMs = responseTimeMs, ResponseSize = responseSize, Timestamp = DateTime.UtcNow, UserAgent = userAgent, IpAddress = ipAddress }); if (_requestHistory.Count > 1000) { _requestHistory.RemoveAt(0); } } } /// /// Records an error for an endpoint. /// public void RecordError(string method, string path, string errorMessage, string? userAgent = null, string? ipAddress = null) { lock (_lock) { var key = $"{method}:{path}"; if (!_metrics.ContainsKey(key)) { _metrics[key] = new ApiMetrics { EndpointId = key, Method = method, Path = path, CreatedAt = DateTime.UtcNow }; } _metrics[key].FailedRequests++; _requestHistory.Add(new RequestMetadata { Method = method, Path = path, StatusCode = 500, ResponseTimeMs = 0, Timestamp = DateTime.UtcNow, UserAgent = userAgent, IpAddress = ipAddress, Error = errorMessage }); if (_requestHistory.Count > 1000) { _requestHistory.RemoveAt(0); } } } /// /// Gets metrics for a specific endpoint. /// public ApiMetrics? GetMetrics(string method, string path) { lock (_lock) { var key = $"{method}:{path}"; return _metrics.ContainsKey(key) ? _metrics[key] : null; } } /// /// Gets all collected metrics. /// public IEnumerable GetAllMetrics() { lock (_lock) { return _metrics.Values.ToList(); } } /// /// Gets the analytics dashboard data. /// public AnalyticsDashboard GetDashboard() { lock (_lock) { var allMetrics = _metrics.Values.ToList(); var totalRequests = allMetrics.Sum(m => m.TotalRequests); var totalErrors = allMetrics.Sum(m => m.FailedRequests); var topEndpoints = allMetrics .OrderByDescending(m => m.TotalRequests) .Take(10) .Select(m => new EndpointUsageSummary { Method = m.Method, Path = m.Path, RequestCount = m.TotalRequests, ErrorCount = m.FailedRequests, AverageResponseTimeMs = m.AverageResponseTimeMs, LastCalled = m.LastRequestAt }) .ToList(); var recentRequests = _requestHistory .OrderByDescending(r => r.Timestamp) .Take(20) .ToList(); var uptime = DateTime.UtcNow - _startTime; var requestsPerSecond = totalRequests > 0 ? totalRequests / uptime.TotalSeconds : 0; return new AnalyticsDashboard { TotalEndpoints = _metrics.Count, TotalRequests = totalRequests, TotalErrors = totalErrors, AverageResponseTime = allMetrics.Any() ? allMetrics.Average(m => m.AverageResponseTimeMs) : 0, PeakRequestsPerSecond = requestsPerSecond, Uptime = uptime, TopEndpoints = topEndpoints, RecentRequests = recentRequests, ErrorRate = totalRequests > 0 ? (double)totalErrors / totalRequests : 0, GeneratedAt = DateTime.UtcNow }; } } /// /// Clears all collected metrics and history. /// public void Clear() { lock (_lock) { _metrics.Clear(); _requestHistory.Clear(); } } /// /// Gets recent requests (last N). /// public IEnumerable GetRecentRequests(int count = 50) { lock (_lock) { return _requestHistory .OrderByDescending(r => r.Timestamp) .Take(count) .ToList(); } } } }