Files
michaelschiemer/src/Framework/ErrorBoundaries/Metrics/BoundaryMetrics.php
Michael Schiemer 55a330b223 Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
2025-08-11 20:13:26 +02:00

309 lines
12 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\ErrorBoundaries\Metrics;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Core\ValueObjects\Timestamp;
/**
* Metrics for error boundary operations
*/
final readonly class BoundaryMetrics
{
public function __construct(
public string $boundaryName,
public int $totalExecutions = 0,
public int $successfulExecutions = 0,
public int $failedExecutions = 0,
public int $fallbackExecutions = 0,
public int $timeoutExecutions = 0,
public int $circuitBreakerTrips = 0,
public Duration $totalExecutionTime = new Duration(0.0),
public Duration $averageExecutionTime = new Duration(0.0),
public Duration $maxExecutionTime = new Duration(0.0),
public Duration $minExecutionTime = new Duration(0.0),
public ?Timestamp $lastSuccessTime = null,
public ?Timestamp $lastFailureTime = null,
public ?Timestamp $lastFallbackTime = null,
public ?Timestamp $createdAt = null,
public ?Timestamp $updatedAt = null,
) {
}
/**
* Calculate success rate
*/
public function getSuccessRate(): Percentage
{
if ($this->totalExecutions === 0) {
return Percentage::fromFloat(0.0);
}
return Percentage::fromFloat($this->successfulExecutions / $this->totalExecutions);
}
/**
* Calculate failure rate
*/
public function getFailureRate(): Percentage
{
if ($this->totalExecutions === 0) {
return Percentage::fromFloat(0.0);
}
return Percentage::fromFloat($this->failedExecutions / $this->totalExecutions);
}
/**
* Calculate fallback rate
*/
public function getFallbackRate(): Percentage
{
if ($this->totalExecutions === 0) {
return Percentage::fromFloat(0.0);
}
return Percentage::fromFloat($this->fallbackExecutions / $this->totalExecutions);
}
/**
* Calculate timeout rate
*/
public function getTimeoutRate(): Percentage
{
if ($this->totalExecutions === 0) {
return Percentage::fromFloat(0.0);
}
return Percentage::fromFloat($this->timeoutExecutions / $this->totalExecutions);
}
/**
* Check if boundary is healthy (success rate > 90%)
*/
public function isHealthy(): bool
{
return $this->getSuccessRate()->toFloat() > 0.9;
}
/**
* Check if circuit breaker is frequently tripping
*/
public function hasFrequentCircuitBreakerTrips(): bool
{
if ($this->totalExecutions === 0) {
return false;
}
$tripRate = $this->circuitBreakerTrips / $this->totalExecutions;
return $tripRate > 0.1; // More than 10% of executions result in circuit breaker trips
}
/**
* Get health status
*/
public function getHealthStatus(): BoundaryHealthStatus
{
if ($this->isHealthy() && ! $this->hasFrequentCircuitBreakerTrips()) {
return BoundaryHealthStatus::HEALTHY;
}
if ($this->getSuccessRate()->toFloat() > 0.7) {
return BoundaryHealthStatus::WARNING;
}
return BoundaryHealthStatus::CRITICAL;
}
/**
* Record successful execution
*/
public function recordSuccess(Duration $executionTime): self
{
$newTotalTime = $this->totalExecutionTime->add($executionTime);
$newTotalExecutions = $this->totalExecutions + 1;
$newAverageTime = Duration::fromMilliseconds(
$newTotalTime->toMilliseconds() / $newTotalExecutions
);
return new self(
boundaryName: $this->boundaryName,
totalExecutions: $newTotalExecutions,
successfulExecutions: $this->successfulExecutions + 1,
failedExecutions: $this->failedExecutions,
fallbackExecutions: $this->fallbackExecutions,
timeoutExecutions: $this->timeoutExecutions,
circuitBreakerTrips: $this->circuitBreakerTrips,
totalExecutionTime: $newTotalTime,
averageExecutionTime: $newAverageTime,
maxExecutionTime: $executionTime->isGreaterThan($this->maxExecutionTime)
? $executionTime
: $this->maxExecutionTime,
minExecutionTime: $this->minExecutionTime->isZero() || $executionTime->isLessThan($this->minExecutionTime)
? $executionTime
: $this->minExecutionTime,
lastSuccessTime: Timestamp::now(),
lastFailureTime: $this->lastFailureTime,
lastFallbackTime: $this->lastFallbackTime,
createdAt: $this->createdAt ?? Timestamp::now(),
updatedAt: Timestamp::now(),
);
}
/**
* Record failed execution
*/
public function recordFailure(Duration $executionTime): self
{
$newTotalTime = $this->totalExecutionTime->add($executionTime);
$newTotalExecutions = $this->totalExecutions + 1;
$newAverageTime = Duration::fromMilliseconds(
$newTotalTime->toMilliseconds() / $newTotalExecutions
);
return new self(
boundaryName: $this->boundaryName,
totalExecutions: $newTotalExecutions,
successfulExecutions: $this->successfulExecutions,
failedExecutions: $this->failedExecutions + 1,
fallbackExecutions: $this->fallbackExecutions,
timeoutExecutions: $this->timeoutExecutions,
circuitBreakerTrips: $this->circuitBreakerTrips,
totalExecutionTime: $newTotalTime,
averageExecutionTime: $newAverageTime,
maxExecutionTime: $executionTime->isGreaterThan($this->maxExecutionTime)
? $executionTime
: $this->maxExecutionTime,
minExecutionTime: $this->minExecutionTime->isZero() || $executionTime->isLessThan($this->minExecutionTime)
? $executionTime
: $this->minExecutionTime,
lastSuccessTime: $this->lastSuccessTime,
lastFailureTime: Timestamp::now(),
lastFallbackTime: $this->lastFallbackTime,
createdAt: $this->createdAt ?? Timestamp::now(),
updatedAt: Timestamp::now(),
);
}
/**
* Record fallback execution
*/
public function recordFallback(): self
{
return new self(
boundaryName: $this->boundaryName,
totalExecutions: $this->totalExecutions + 1,
successfulExecutions: $this->successfulExecutions,
failedExecutions: $this->failedExecutions,
fallbackExecutions: $this->fallbackExecutions + 1,
timeoutExecutions: $this->timeoutExecutions,
circuitBreakerTrips: $this->circuitBreakerTrips,
totalExecutionTime: $this->totalExecutionTime,
averageExecutionTime: $this->averageExecutionTime,
maxExecutionTime: $this->maxExecutionTime,
minExecutionTime: $this->minExecutionTime,
lastSuccessTime: $this->lastSuccessTime,
lastFailureTime: $this->lastFailureTime,
lastFallbackTime: Timestamp::now(),
createdAt: $this->createdAt ?? Timestamp::now(),
updatedAt: Timestamp::now(),
);
}
/**
* Record timeout execution
*/
public function recordTimeout(Duration $executionTime): self
{
$newTotalTime = $this->totalExecutionTime->add($executionTime);
$newTotalExecutions = $this->totalExecutions + 1;
$newAverageTime = Duration::fromMilliseconds(
$newTotalTime->toMilliseconds() / $newTotalExecutions
);
return new self(
boundaryName: $this->boundaryName,
totalExecutions: $newTotalExecutions,
successfulExecutions: $this->successfulExecutions,
failedExecutions: $this->failedExecutions,
fallbackExecutions: $this->fallbackExecutions,
timeoutExecutions: $this->timeoutExecutions + 1,
circuitBreakerTrips: $this->circuitBreakerTrips,
totalExecutionTime: $newTotalTime,
averageExecutionTime: $newAverageTime,
maxExecutionTime: $executionTime->isGreaterThan($this->maxExecutionTime)
? $executionTime
: $this->maxExecutionTime,
minExecutionTime: $this->minExecutionTime->isZero() || $executionTime->isLessThan($this->minExecutionTime)
? $executionTime
: $this->minExecutionTime,
lastSuccessTime: $this->lastSuccessTime,
lastFailureTime: $this->lastFailureTime,
lastFallbackTime: $this->lastFallbackTime,
createdAt: $this->createdAt ?? Timestamp::now(),
updatedAt: Timestamp::now(),
);
}
/**
* Record circuit breaker trip
*/
public function recordCircuitBreakerTrip(): self
{
return new self(
boundaryName: $this->boundaryName,
totalExecutions: $this->totalExecutions,
successfulExecutions: $this->successfulExecutions,
failedExecutions: $this->failedExecutions,
fallbackExecutions: $this->fallbackExecutions,
timeoutExecutions: $this->timeoutExecutions,
circuitBreakerTrips: $this->circuitBreakerTrips + 1,
totalExecutionTime: $this->totalExecutionTime,
averageExecutionTime: $this->averageExecutionTime,
maxExecutionTime: $this->maxExecutionTime,
minExecutionTime: $this->minExecutionTime,
lastSuccessTime: $this->lastSuccessTime,
lastFailureTime: $this->lastFailureTime,
lastFallbackTime: $this->lastFallbackTime,
createdAt: $this->createdAt ?? Timestamp::now(),
updatedAt: Timestamp::now(),
);
}
/**
* Convert to array for serialization
*/
public function toArray(): array
{
return [
'boundary_name' => $this->boundaryName,
'total_executions' => $this->totalExecutions,
'successful_executions' => $this->successfulExecutions,
'failed_executions' => $this->failedExecutions,
'fallback_executions' => $this->fallbackExecutions,
'timeout_executions' => $this->timeoutExecutions,
'circuit_breaker_trips' => $this->circuitBreakerTrips,
'success_rate' => $this->getSuccessRate()->toFloat(),
'failure_rate' => $this->getFailureRate()->toFloat(),
'fallback_rate' => $this->getFallbackRate()->toFloat(),
'timeout_rate' => $this->getTimeoutRate()->toFloat(),
'total_execution_time_ms' => $this->totalExecutionTime->toMilliseconds(),
'average_execution_time_ms' => $this->averageExecutionTime->toMilliseconds(),
'max_execution_time_ms' => $this->maxExecutionTime->toMilliseconds(),
'min_execution_time_ms' => $this->minExecutionTime->toMilliseconds(),
'last_success_time' => $this->lastSuccessTime?->toIsoString(),
'last_failure_time' => $this->lastFailureTime?->toIsoString(),
'last_fallback_time' => $this->lastFallbackTime?->toIsoString(),
'health_status' => $this->getHealthStatus()->value,
'is_healthy' => $this->isHealthy(),
'has_frequent_circuit_trips' => $this->hasFrequentCircuitBreakerTrips(),
'created_at' => $this->createdAt?->toIsoString(),
'updated_at' => $this->updatedAt?->toIsoString(),
];
}
}