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();
}
}
}
}