Files
michaelschiemer/src/Framework/Monitoring/ErrorBoundaryMetrics.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

209 lines
6.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Monitoring;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Core\ValueObjects\Timestamp;
/**
* Metrics value object for error boundary monitoring
*/
final readonly class ErrorBoundaryMetrics
{
public function __construct(
public string $boundaryName,
public int $totalExecutions,
public int $totalSuccesses,
public int $totalFallbacks,
public int $totalErrors,
public Duration $averageExecutionTime,
public Duration $peakExecutionTime,
public Timestamp $lastExecution,
public int $consecutiveFailures,
public array $errorTypes = [], // Array of error class names with counts
) {
}
/**
* Get success rate as percentage
*/
public function getSuccessRate(): Percentage
{
if ($this->totalExecutions === 0) {
return Percentage::fromFloat(0.0);
}
return Percentage::fromFloat($this->totalSuccesses / $this->totalExecutions);
}
/**
* Get fallback rate as percentage
*/
public function getFallbackRate(): Percentage
{
if ($this->totalExecutions === 0) {
return Percentage::fromFloat(0.0);
}
return Percentage::fromFloat($this->totalFallbacks / $this->totalExecutions);
}
/**
* Get error rate as percentage
*/
public function getErrorRate(): Percentage
{
if ($this->totalExecutions === 0) {
return Percentage::fromFloat(0.0);
}
return Percentage::fromFloat($this->totalErrors / $this->totalExecutions);
}
/**
* Check if boundary is healthy
*/
public function isHealthy(): bool
{
return $this->getFallbackRate()->toFloat() < 0.1 && // Less than 10% fallback rate
$this->getErrorRate()->toFloat() < 0.05; // Less than 5% error rate
}
/**
* Check if boundary is degraded
*/
public function isDegraded(): bool
{
return ($this->getFallbackRate()->toFloat() >= 0.1 && $this->getFallbackRate()->toFloat() < 0.25) ||
($this->getErrorRate()->toFloat() >= 0.05 && $this->getErrorRate()->toFloat() < 0.15);
}
/**
* Check if boundary is failed
*/
public function isFailed(): bool
{
return $this->getFallbackRate()->toFloat() >= 0.25 || // More than 25% fallback rate
$this->getErrorRate()->toFloat() >= 0.15; // More than 15% error rate
}
/**
* Get health status string
*/
public function getHealthStatus(): string
{
if ($this->isFailed()) {
return 'failed';
}
if ($this->isDegraded()) {
return 'degraded';
}
return 'healthy';
}
/**
* Get time since last execution
*/
public function getTimeSinceLastExecution(Timestamp $currentTime): Duration
{
return $this->lastExecution->diff($currentTime);
}
/**
* Get most common error type
*/
public function getMostCommonErrorType(): ?string
{
if (empty($this->errorTypes)) {
return null;
}
$maxCount = 0;
$mostCommon = null;
foreach ($this->errorTypes as $errorType => $count) {
if ($count > $maxCount) {
$maxCount = $count;
$mostCommon = $errorType;
}
}
return $mostCommon;
}
/**
* Get error type distribution
*/
public function getErrorTypeDistribution(): array
{
if (empty($this->errorTypes) || $this->totalErrors === 0) {
return [];
}
$distribution = [];
foreach ($this->errorTypes as $errorType => $count) {
$distribution[$errorType] = [
'count' => $count,
'percentage' => ($count / $this->totalErrors) * 100,
];
}
// Sort by count descending
uasort($distribution, fn ($a, $b) => $b['count'] <=> $a['count']);
return $distribution;
}
/**
* Convert to array for serialization
*/
public function toArray(): array
{
return [
'boundary_name' => $this->boundaryName,
'total_executions' => $this->totalExecutions,
'total_successes' => $this->totalSuccesses,
'total_fallbacks' => $this->totalFallbacks,
'total_errors' => $this->totalErrors,
'success_rate' => $this->getSuccessRate()->toFloat(),
'fallback_rate' => $this->getFallbackRate()->toFloat(),
'error_rate' => $this->getErrorRate()->toFloat(),
'average_execution_time_ms' => $this->averageExecutionTime->toMilliseconds(),
'peak_execution_time_ms' => $this->peakExecutionTime->toMilliseconds(),
'last_execution' => $this->lastExecution->toIsoString(),
'consecutive_failures' => $this->consecutiveFailures,
'error_types' => $this->errorTypes,
'most_common_error_type' => $this->getMostCommonErrorType(),
'error_type_distribution' => $this->getErrorTypeDistribution(),
'health_status' => $this->getHealthStatus(),
'is_healthy' => $this->isHealthy(),
'is_degraded' => $this->isDegraded(),
'is_failed' => $this->isFailed(),
];
}
/**
* Create from array
*/
public static function fromArray(array $data): self
{
return new self(
boundaryName: $data['boundary_name'],
totalExecutions: $data['total_executions'],
totalSuccesses: $data['total_successes'],
totalFallbacks: $data['total_fallbacks'],
totalErrors: $data['total_errors'],
averageExecutionTime: Duration::fromMilliseconds($data['average_execution_time_ms']),
peakExecutionTime: Duration::fromMilliseconds($data['peak_execution_time_ms']),
lastExecution: Timestamp::fromFloat(strtotime($data['last_execution'])),
consecutiveFailures: $data['consecutive_failures'],
errorTypes: $data['error_types'] ?? [],
);
}
}