303 lines
9.6 KiB
PHP
303 lines
9.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\ApiGateway\Metrics;
|
|
|
|
use App\Framework\Metrics\MetricsCollection;
|
|
|
|
/**
|
|
* Collects and tracks metrics for API Gateway operations
|
|
*/
|
|
final class ApiMetrics
|
|
{
|
|
/** @var array<string, array{requests: int, successes: int, failures: int, total_duration_ms: float, avg_duration_ms: float}> */
|
|
private array $statsByService = [];
|
|
|
|
/** @var array<string, array{requests: int, successes: int, failures: int, total_duration_ms: float, avg_duration_ms: float}> */
|
|
private array $statsByRequestName = [];
|
|
|
|
private int $totalRequests = 0;
|
|
private int $totalSuccesses = 0;
|
|
private int $totalFailures = 0;
|
|
private float $totalDurationMs = 0.0;
|
|
|
|
/** @var array<string, int> */
|
|
private array $retryCountByService = [];
|
|
|
|
/** @var array<string, int> */
|
|
private array $circuitBreakerOpenCountByService = [];
|
|
|
|
public function recordRequest(
|
|
string $service,
|
|
string $requestName,
|
|
float $durationMs,
|
|
bool $success,
|
|
int $retryAttempts = 0,
|
|
bool $circuitBreakerTriggered = false
|
|
): void {
|
|
// Total stats
|
|
$this->totalRequests++;
|
|
if ($success) {
|
|
$this->totalSuccesses++;
|
|
} else {
|
|
$this->totalFailures++;
|
|
}
|
|
$this->totalDurationMs += $durationMs;
|
|
|
|
// Per-service stats
|
|
if (!isset($this->statsByService[$service])) {
|
|
$this->statsByService[$service] = [
|
|
'requests' => 0,
|
|
'successes' => 0,
|
|
'failures' => 0,
|
|
'total_duration_ms' => 0.0,
|
|
'avg_duration_ms' => 0.0,
|
|
];
|
|
}
|
|
|
|
$this->statsByService[$service]['requests']++;
|
|
if ($success) {
|
|
$this->statsByService[$service]['successes']++;
|
|
} else {
|
|
$this->statsByService[$service]['failures']++;
|
|
}
|
|
$this->statsByService[$service]['total_duration_ms'] += $durationMs;
|
|
$this->statsByService[$service]['avg_duration_ms'] =
|
|
$this->statsByService[$service]['total_duration_ms'] / $this->statsByService[$service]['requests'];
|
|
|
|
// Per-request-name stats
|
|
if (!isset($this->statsByRequestName[$requestName])) {
|
|
$this->statsByRequestName[$requestName] = [
|
|
'requests' => 0,
|
|
'successes' => 0,
|
|
'failures' => 0,
|
|
'total_duration_ms' => 0.0,
|
|
'avg_duration_ms' => 0.0,
|
|
];
|
|
}
|
|
|
|
$this->statsByRequestName[$requestName]['requests']++;
|
|
if ($success) {
|
|
$this->statsByRequestName[$requestName]['successes']++;
|
|
} else {
|
|
$this->statsByRequestName[$requestName]['failures']++;
|
|
}
|
|
$this->statsByRequestName[$requestName]['total_duration_ms'] += $durationMs;
|
|
$this->statsByRequestName[$requestName]['avg_duration_ms'] =
|
|
$this->statsByRequestName[$requestName]['total_duration_ms'] / $this->statsByRequestName[$requestName]['requests'];
|
|
|
|
// Retry tracking
|
|
if ($retryAttempts > 0) {
|
|
if (!isset($this->retryCountByService[$service])) {
|
|
$this->retryCountByService[$service] = 0;
|
|
}
|
|
$this->retryCountByService[$service] += $retryAttempts;
|
|
}
|
|
|
|
// Circuit breaker tracking
|
|
if ($circuitBreakerTriggered) {
|
|
if (!isset($this->circuitBreakerOpenCountByService[$service])) {
|
|
$this->circuitBreakerOpenCountByService[$service] = 0;
|
|
}
|
|
$this->circuitBreakerOpenCountByService[$service]++;
|
|
}
|
|
}
|
|
|
|
public function getStats(): array
|
|
{
|
|
return [
|
|
'total' => [
|
|
'requests' => $this->totalRequests,
|
|
'successes' => $this->totalSuccesses,
|
|
'failures' => $this->totalFailures,
|
|
'success_rate' => $this->getSuccessRate(),
|
|
'avg_duration_ms' => $this->getAverageDuration(),
|
|
],
|
|
'by_service' => $this->statsByService,
|
|
'by_request_name' => $this->statsByRequestName,
|
|
'retry_counts' => $this->retryCountByService,
|
|
'circuit_breaker_opens' => $this->circuitBreakerOpenCountByService,
|
|
];
|
|
}
|
|
|
|
public function getSuccessRate(): float
|
|
{
|
|
if ($this->totalRequests === 0) {
|
|
return 0.0;
|
|
}
|
|
|
|
return $this->totalSuccesses / $this->totalRequests;
|
|
}
|
|
|
|
public function getAverageDuration(): float
|
|
{
|
|
if ($this->totalRequests === 0) {
|
|
return 0.0;
|
|
}
|
|
|
|
return $this->totalDurationMs / $this->totalRequests;
|
|
}
|
|
|
|
public function getServiceStats(string $service): ?array
|
|
{
|
|
return $this->statsByService[$service] ?? null;
|
|
}
|
|
|
|
public function getRequestNameStats(string $requestName): ?array
|
|
{
|
|
return $this->statsByRequestName[$requestName] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Collect metrics for Prometheus export
|
|
*/
|
|
public function collectMetrics(MetricsCollection $collection): void
|
|
{
|
|
// Total request counter
|
|
$collection->counter(
|
|
'api_gateway_requests_total',
|
|
(float) $this->totalRequests,
|
|
'Total number of API gateway requests'
|
|
);
|
|
|
|
// Success counter
|
|
$collection->counter(
|
|
'api_gateway_requests_success_total',
|
|
(float) $this->totalSuccesses,
|
|
'Total number of successful API gateway requests'
|
|
);
|
|
|
|
// Failure counter
|
|
$collection->counter(
|
|
'api_gateway_requests_failure_total',
|
|
(float) $this->totalFailures,
|
|
'Total number of failed API gateway requests'
|
|
);
|
|
|
|
// Success rate gauge
|
|
$collection->gauge(
|
|
'api_gateway_success_rate',
|
|
$this->getSuccessRate(),
|
|
'Success rate of API gateway requests (0.0 to 1.0)'
|
|
);
|
|
|
|
// Average duration gauge
|
|
$collection->gauge(
|
|
'api_gateway_request_duration_ms',
|
|
$this->getAverageDuration(),
|
|
'Average duration of API gateway requests in milliseconds'
|
|
);
|
|
|
|
// Per-service metrics
|
|
foreach ($this->statsByService as $service => $stats) {
|
|
$labels = ['service' => $service];
|
|
|
|
$collection->counter(
|
|
'api_gateway_requests_total',
|
|
(float) $stats['requests'],
|
|
'Total requests per service',
|
|
$labels
|
|
);
|
|
|
|
$collection->counter(
|
|
'api_gateway_requests_success_total',
|
|
(float) $stats['successes'],
|
|
'Successful requests per service',
|
|
$labels
|
|
);
|
|
|
|
$collection->counter(
|
|
'api_gateway_requests_failure_total',
|
|
(float) $stats['failures'],
|
|
'Failed requests per service',
|
|
$labels
|
|
);
|
|
|
|
$collection->gauge(
|
|
'api_gateway_request_duration_ms',
|
|
$stats['avg_duration_ms'],
|
|
'Average request duration per service in milliseconds',
|
|
$labels
|
|
);
|
|
|
|
// Success rate per service
|
|
$successRate = $stats['requests'] > 0
|
|
? $stats['successes'] / $stats['requests']
|
|
: 0.0;
|
|
|
|
$collection->gauge(
|
|
'api_gateway_success_rate',
|
|
$successRate,
|
|
'Success rate per service',
|
|
$labels
|
|
);
|
|
}
|
|
|
|
// Per-request-name metrics
|
|
foreach ($this->statsByRequestName as $requestName => $stats) {
|
|
$labels = ['request_name' => $requestName];
|
|
|
|
$collection->counter(
|
|
'api_gateway_requests_total',
|
|
(float) $stats['requests'],
|
|
'Total requests per request name',
|
|
$labels
|
|
);
|
|
|
|
$collection->counter(
|
|
'api_gateway_requests_success_total',
|
|
(float) $stats['successes'],
|
|
'Successful requests per request name',
|
|
$labels
|
|
);
|
|
|
|
$collection->counter(
|
|
'api_gateway_requests_failure_total',
|
|
(float) $stats['failures'],
|
|
'Failed requests per request name',
|
|
$labels
|
|
);
|
|
|
|
$collection->gauge(
|
|
'api_gateway_request_duration_ms',
|
|
$stats['avg_duration_ms'],
|
|
'Average request duration per request name in milliseconds',
|
|
$labels
|
|
);
|
|
}
|
|
|
|
// Retry metrics per service
|
|
foreach ($this->retryCountByService as $service => $retryCount) {
|
|
$collection->counter(
|
|
'api_gateway_retries_total',
|
|
(float) $retryCount,
|
|
'Total number of retry attempts per service',
|
|
['service' => $service]
|
|
);
|
|
}
|
|
|
|
// Circuit breaker metrics per service
|
|
foreach ($this->circuitBreakerOpenCountByService as $service => $openCount) {
|
|
$collection->counter(
|
|
'api_gateway_circuit_breaker_open_total',
|
|
(float) $openCount,
|
|
'Total number of circuit breaker opens per service',
|
|
['service' => $service]
|
|
);
|
|
}
|
|
}
|
|
|
|
public function reset(): void
|
|
{
|
|
$this->statsByService = [];
|
|
$this->statsByRequestName = [];
|
|
$this->totalRequests = 0;
|
|
$this->totalSuccesses = 0;
|
|
$this->totalFailures = 0;
|
|
$this->totalDurationMs = 0.0;
|
|
$this->retryCountByService = [];
|
|
$this->circuitBreakerOpenCountByService = [];
|
|
}
|
|
}
|