feat: add API Gateway, RapidMail and Shopify integrations, update WireGuard configs, add Redis override and architecture docs

This commit is contained in:
2025-11-04 23:08:17 +01:00
parent 5d6edea3bb
commit f9b8cf9f33
23 changed files with 3621 additions and 8 deletions

View File

@@ -0,0 +1,302 @@
<?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 = [];
}
}