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
This commit is contained in:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -0,0 +1,534 @@
<?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;
use App\Framework\DateTime\Clock;
use App\Framework\Logging\Logger;
use App\Framework\StateManagement\StateManager;
/**
* Advanced monitoring system for circuit breakers and error boundaries
*/
final readonly class AdvancedMonitoring
{
public function __construct(
private StateManager $stateManager,
private Clock $clock,
private ?Logger $logger = null,
private MonitoringConfig $config = new MonitoringConfig(),
) {
}
/**
* Record circuit breaker metrics
*/
public function recordCircuitBreakerMetrics(string $circuitName, CircuitBreakerMetrics $metrics): void
{
$timestamp = $this->clock->time();
$key = "monitoring:circuit:{$circuitName}:{$timestamp->toTimestamp()}";
$this->stateManager->setState(
$key,
$metrics->toArray(),
$this->config->metricsRetention
);
// Update aggregated metrics
$this->updateAggregatedMetrics($circuitName, $metrics, $timestamp);
// Check for alerts
$this->checkCircuitBreakerAlerts($circuitName, $metrics);
}
/**
* Record error boundary metrics
*/
public function recordErrorBoundaryMetrics(string $boundaryName, ErrorBoundaryMetrics $metrics): void
{
$timestamp = $this->clock->time();
$key = "monitoring:boundary:{$boundaryName}:{$timestamp->toTimestamp()}";
$this->stateManager->setState(
$key,
$metrics->toArray(),
$this->config->metricsRetention
);
// Update aggregated metrics
$this->updateBoundaryAggregatedMetrics($boundaryName, $metrics, $timestamp);
// Check for alerts
$this->checkErrorBoundaryAlerts($boundaryName, $metrics);
}
/**
* Get circuit breaker health dashboard
*/
public function getCircuitBreakerDashboard(): array
{
$circuits = $this->getActiveCircuits();
$dashboard = [
'timestamp' => $this->clock->time()->toIsoString(),
'total_circuits' => count($circuits),
'healthy_circuits' => 0,
'degraded_circuits' => 0,
'failed_circuits' => 0,
'circuits' => [],
];
foreach ($circuits as $circuitName) {
$health = $this->getCircuitHealth($circuitName);
$dashboard['circuits'][$circuitName] = $health;
match ($health['status']) {
'healthy' => $dashboard['healthy_circuits']++,
'degraded' => $dashboard['degraded_circuits']++,
'failed' => $dashboard['failed_circuits']++,
};
}
return $dashboard;
}
/**
* Get error boundary dashboard
*/
public function getErrorBoundaryDashboard(): array
{
$boundaries = $this->getActiveBoundaries();
$dashboard = [
'timestamp' => $this->clock->time()->toIsoString(),
'total_boundaries' => count($boundaries),
'healthy_boundaries' => 0,
'degraded_boundaries' => 0,
'failed_boundaries' => 0,
'boundaries' => [],
];
foreach ($boundaries as $boundaryName) {
$health = $this->getBoundaryHealth($boundaryName);
$dashboard['boundaries'][$boundaryName] = $health;
match ($health['status']) {
'healthy' => $dashboard['healthy_boundaries']++,
'degraded' => $dashboard['degraded_boundaries']++,
'failed' => $dashboard['failed_boundaries']++,
};
}
return $dashboard;
}
/**
* Get system-wide health summary
*/
public function getSystemHealthSummary(): SystemHealthSummary
{
$circuitMetrics = $this->getAggregatedCircuitMetrics();
$boundaryMetrics = $this->getAggregatedBoundaryMetrics();
$totalComponents = count($circuitMetrics) + count($boundaryMetrics);
$healthyComponents = 0;
$degradedComponents = 0;
$failedComponents = 0;
foreach ($circuitMetrics as $metrics) {
match ($this->calculateHealthStatus($metrics)) {
'healthy' => $healthyComponents++,
'degraded' => $degradedComponents++,
'failed' => $failedComponents++,
};
}
foreach ($boundaryMetrics as $metrics) {
match ($this->calculateBoundaryHealthStatus($metrics)) {
'healthy' => $healthyComponents++,
'degraded' => $degradedComponents++,
'failed' => $failedComponents++,
};
}
$overallHealthScore = $totalComponents > 0
? Percentage::fromFloat($healthyComponents / $totalComponents)
: Percentage::fromFloat(1.0);
return new SystemHealthSummary(
timestamp: $this->clock->time(),
totalComponents: $totalComponents,
healthyComponents: $healthyComponents,
degradedComponents: $degradedComponents,
failedComponents: $failedComponents,
overallHealthScore: $overallHealthScore,
circuitBreakerCount: count($circuitMetrics),
errorBoundaryCount: count($boundaryMetrics),
);
}
/**
* Get trend analysis for circuit breaker
*/
public function getCircuitBreakerTrends(string $circuitName, Duration $timeWindow): array
{
$endTime = $this->clock->time();
$startTime = Timestamp::fromFloat($endTime->toFloat() - $timeWindow->toSeconds());
$metrics = $this->getMetricsInTimeRange(
"monitoring:circuit:{$circuitName}",
$startTime,
$endTime
);
return $this->analyzeTrends($metrics);
}
/**
* Get performance analytics
*/
public function getPerformanceAnalytics(Duration $timeWindow): array
{
$endTime = $this->clock->time();
$startTime = Timestamp::fromFloat($endTime->toFloat() - $timeWindow->toSeconds());
$circuits = $this->getActiveCircuits();
$boundaries = $this->getActiveBoundaries();
$analytics = [
'time_range' => [
'start' => $startTime->toIsoString(),
'end' => $endTime->toIsoString(),
'duration' => $timeWindow->toHumanReadable(),
],
'circuit_breakers' => [],
'error_boundaries' => [],
'system_summary' => [
'total_requests' => 0,
'total_failures' => 0,
'average_response_time' => null,
'peak_response_time' => null,
'error_rate' => null,
],
];
$totalRequests = 0;
$totalFailures = 0;
$totalResponseTime = 0;
$peakResponseTime = Duration::zero();
// Analyze circuit breakers
foreach ($circuits as $circuitName) {
$metrics = $this->getMetricsInTimeRange(
"monitoring:circuit:{$circuitName}",
$startTime,
$endTime
);
$circuitAnalysis = $this->analyzeCircuitPerformance($metrics);
$analytics['circuit_breakers'][$circuitName] = $circuitAnalysis;
$totalRequests += $circuitAnalysis['total_requests'];
$totalFailures += $circuitAnalysis['total_failures'];
$totalResponseTime += $circuitAnalysis['total_response_time_ms'];
if ($circuitAnalysis['peak_response_time'] > $peakResponseTime->toMilliseconds()) {
$peakResponseTime = Duration::fromMilliseconds($circuitAnalysis['peak_response_time']);
}
}
// Analyze error boundaries
foreach ($boundaries as $boundaryName) {
$metrics = $this->getMetricsInTimeRange(
"monitoring:boundary:{$boundaryName}",
$startTime,
$endTime
);
$boundaryAnalysis = $this->analyzeBoundaryPerformance($metrics);
$analytics['error_boundaries'][$boundaryName] = $boundaryAnalysis;
}
// System summary
if ($totalRequests > 0) {
$analytics['system_summary'] = [
'total_requests' => $totalRequests,
'total_failures' => $totalFailures,
'average_response_time' => Duration::fromMilliseconds($totalResponseTime / $totalRequests),
'peak_response_time' => $peakResponseTime,
'error_rate' => Percentage::fromFloat($totalFailures / $totalRequests),
];
}
return $analytics;
}
/**
* Generate monitoring alerts
*/
public function generateAlerts(): array
{
$alerts = [];
// Check circuit breaker alerts
foreach ($this->getActiveCircuits() as $circuitName) {
$health = $this->getCircuitHealth($circuitName);
if ($health['status'] !== 'healthy') {
$alerts[] = new MonitoringAlert(
type: 'circuit_breaker',
severity: $health['status'] === 'failed' ? 'critical' : 'warning',
component: $circuitName,
message: "Circuit breaker '{$circuitName}' is in {$health['status']} state",
timestamp: $this->clock->time(),
metrics: $health,
);
}
}
// Check error boundary alerts
foreach ($this->getActiveBoundaries() as $boundaryName) {
$health = $this->getBoundaryHealth($boundaryName);
if ($health['status'] !== 'healthy') {
$alerts[] = new MonitoringAlert(
type: 'error_boundary',
severity: $health['status'] === 'failed' ? 'critical' : 'warning',
component: $boundaryName,
message: "Error boundary '{$boundaryName}' is in {$health['status']} state",
timestamp: $this->clock->time(),
metrics: $health,
);
}
}
return $alerts;
}
/**
* Get circuit health status
*/
private function getCircuitHealth(string $circuitName): array
{
$aggregatedKey = "monitoring:circuit:aggregated:{$circuitName}";
$metrics = $this->stateManager->getState($aggregatedKey);
if ($metrics === null) {
return [
'status' => 'unknown',
'last_updated' => null,
'metrics' => null,
];
}
$status = $this->calculateHealthStatus($metrics);
return [
'status' => $status,
'last_updated' => $metrics['last_updated'] ?? null,
'metrics' => $metrics,
];
}
/**
* Get boundary health status
*/
private function getBoundaryHealth(string $boundaryName): array
{
$aggregatedKey = "monitoring:boundary:aggregated:{$boundaryName}";
$metrics = $this->stateManager->getState($aggregatedKey);
if ($metrics === null) {
return [
'status' => 'unknown',
'last_updated' => null,
'metrics' => null,
];
}
$status = $this->calculateBoundaryHealthStatus($metrics);
return [
'status' => $status,
'last_updated' => $metrics['last_updated'] ?? null,
'metrics' => $metrics,
];
}
/**
* Calculate health status from metrics
*/
private function calculateHealthStatus(array $metrics): string
{
$errorRate = $metrics['error_rate'] ?? 0.0;
$responseTime = $metrics['average_response_time'] ?? 0.0;
if ($errorRate > $this->config->criticalErrorRateThreshold) {
return 'failed';
}
if ($errorRate > $this->config->warningErrorRateThreshold ||
$responseTime > $this->config->slowResponseThreshold->toMilliseconds()) {
return 'degraded';
}
return 'healthy';
}
/**
* Calculate boundary health status from metrics
*/
private function calculateBoundaryHealthStatus(array $metrics): string
{
$fallbackRate = $metrics['fallback_rate'] ?? 0.0;
$errorRate = $metrics['error_rate'] ?? 0.0;
if ($fallbackRate > $this->config->criticalFallbackRateThreshold) {
return 'failed';
}
if ($fallbackRate > $this->config->warningFallbackRateThreshold ||
$errorRate > $this->config->warningErrorRateThreshold) {
return 'degraded';
}
return 'healthy';
}
/**
* Update aggregated metrics for circuit breaker
*/
private function updateAggregatedMetrics(
string $circuitName,
CircuitBreakerMetrics $metrics,
Timestamp $timestamp
): void {
$aggregatedKey = "monitoring:circuit:aggregated:{$circuitName}";
$this->stateManager->updateState($aggregatedKey, function ($existing) use ($metrics, $timestamp) {
return $this->aggregateCircuitMetrics($existing, $metrics, $timestamp);
});
}
/**
* Update aggregated metrics for error boundary
*/
private function updateBoundaryAggregatedMetrics(
string $boundaryName,
ErrorBoundaryMetrics $metrics,
Timestamp $timestamp
): void {
$aggregatedKey = "monitoring:boundary:aggregated:{$boundaryName}";
$this->stateManager->updateState($aggregatedKey, function ($existing) use ($metrics, $timestamp) {
return $this->aggregateBoundaryMetrics($existing, $metrics, $timestamp);
});
}
/**
* Aggregate circuit breaker metrics
*/
private function aggregateCircuitMetrics(?array $existing, CircuitBreakerMetrics $metrics, Timestamp $timestamp): array
{
if ($existing === null) {
return array_merge($metrics->toArray(), [
'first_seen' => $timestamp->toIsoString(),
'last_updated' => $timestamp->toIsoString(),
]);
}
// Simple aggregation - in production you'd want more sophisticated algorithms
$totalRequests = ($existing['total_requests'] ?? 0) + $metrics->totalRequests;
$totalFailures = ($existing['total_failures'] ?? 0) + $metrics->totalFailures;
return [
'total_requests' => $totalRequests,
'total_failures' => $totalFailures,
'error_rate' => $totalRequests > 0 ? $totalFailures / $totalRequests : 0.0,
'average_response_time' => $metrics->averageResponseTime->toMilliseconds(),
'peak_response_time' => max(
$existing['peak_response_time'] ?? 0,
$metrics->peakResponseTime->toMilliseconds()
),
'first_seen' => $existing['first_seen'],
'last_updated' => $timestamp->toIsoString(),
];
}
/**
* Aggregate boundary metrics
*/
private function aggregateBoundaryMetrics(?array $existing, ErrorBoundaryMetrics $metrics, Timestamp $timestamp): array
{
if ($existing === null) {
return array_merge($metrics->toArray(), [
'first_seen' => $timestamp->toIsoString(),
'last_updated' => $timestamp->toIsoString(),
]);
}
$totalExecutions = ($existing['total_executions'] ?? 0) + $metrics->totalExecutions;
$totalFallbacks = ($existing['total_fallbacks'] ?? 0) + $metrics->totalFallbacks;
$totalErrors = ($existing['total_errors'] ?? 0) + $metrics->totalErrors;
return [
'total_executions' => $totalExecutions,
'total_fallbacks' => $totalFallbacks,
'total_errors' => $totalErrors,
'fallback_rate' => $totalExecutions > 0 ? $totalFallbacks / $totalExecutions : 0.0,
'error_rate' => $totalExecutions > 0 ? $totalErrors / $totalExecutions : 0.0,
'average_execution_time' => $metrics->averageExecutionTime->toMilliseconds(),
'first_seen' => $existing['first_seen'],
'last_updated' => $timestamp->toIsoString(),
];
}
// Placeholder methods - implement based on your specific needs
private function getActiveCircuits(): array
{
return [];
}
private function getActiveBoundaries(): array
{
return [];
}
private function getAggregatedCircuitMetrics(): array
{
return [];
}
private function getAggregatedBoundaryMetrics(): array
{
return [];
}
private function getMetricsInTimeRange(string $prefix, Timestamp $start, Timestamp $end): array
{
return [];
}
private function analyzeTrends(array $metrics): array
{
return [];
}
private function analyzeCircuitPerformance(array $metrics): array
{
return [];
}
private function analyzeBoundaryPerformance(array $metrics): array
{
return [];
}
private function checkCircuitBreakerAlerts(string $name, CircuitBreakerMetrics $metrics): void
{
}
private function checkErrorBoundaryAlerts(string $name, ErrorBoundaryMetrics $metrics): void
{
}
}

View File

@@ -0,0 +1,149 @@
<?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 circuit breaker monitoring
*/
final readonly class CircuitBreakerMetrics
{
public function __construct(
public string $circuitName,
public int $totalRequests,
public int $totalFailures,
public int $totalSuccesses,
public Duration $averageResponseTime,
public Duration $peakResponseTime,
public string $currentState, // OPEN, CLOSED, HALF_OPEN
public Timestamp $lastStateChange,
public int $consecutiveFailures,
public ?Duration $timeUntilRetry = null,
) {
}
/**
* Get failure rate as percentage
*/
public function getFailureRate(): Percentage
{
if ($this->totalRequests === 0) {
return Percentage::fromFloat(0.0);
}
return Percentage::fromFloat($this->totalFailures / $this->totalRequests);
}
/**
* Get success rate as percentage
*/
public function getSuccessRate(): Percentage
{
if ($this->totalRequests === 0) {
return Percentage::fromFloat(0.0);
}
return Percentage::fromFloat($this->totalSuccesses / $this->totalRequests);
}
/**
* Check if circuit is healthy
*/
public function isHealthy(): bool
{
return $this->currentState === 'CLOSED' &&
$this->getFailureRate()->toFloat() < 0.05; // Less than 5% failure rate
}
/**
* Check if circuit is degraded
*/
public function isDegraded(): bool
{
return $this->currentState === 'HALF_OPEN' ||
($this->currentState === 'CLOSED' && $this->getFailureRate()->toFloat() >= 0.05);
}
/**
* Check if circuit is failed
*/
public function isFailed(): bool
{
return $this->currentState === 'OPEN';
}
/**
* Get health status string
*/
public function getHealthStatus(): string
{
if ($this->isFailed()) {
return 'failed';
}
if ($this->isDegraded()) {
return 'degraded';
}
return 'healthy';
}
/**
* Get time since last state change
*/
public function getTimeSinceLastStateChange(Timestamp $currentTime): Duration
{
return $this->lastStateChange->diff($currentTime);
}
/**
* Convert to array for serialization
*/
public function toArray(): array
{
return [
'circuit_name' => $this->circuitName,
'total_requests' => $this->totalRequests,
'total_failures' => $this->totalFailures,
'total_successes' => $this->totalSuccesses,
'failure_rate' => $this->getFailureRate()->toFloat(),
'success_rate' => $this->getSuccessRate()->toFloat(),
'average_response_time_ms' => $this->averageResponseTime->toMilliseconds(),
'peak_response_time_ms' => $this->peakResponseTime->toMilliseconds(),
'current_state' => $this->currentState,
'last_state_change' => $this->lastStateChange->toIsoString(),
'consecutive_failures' => $this->consecutiveFailures,
'time_until_retry_ms' => $this->timeUntilRetry?->toMilliseconds(),
'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(
circuitName: $data['circuit_name'],
totalRequests: $data['total_requests'],
totalFailures: $data['total_failures'],
totalSuccesses: $data['total_successes'],
averageResponseTime: Duration::fromMilliseconds($data['average_response_time_ms']),
peakResponseTime: Duration::fromMilliseconds($data['peak_response_time_ms']),
currentState: $data['current_state'],
lastStateChange: Timestamp::fromFloat(strtotime($data['last_state_change'])),
consecutiveFailures: $data['consecutive_failures'],
timeUntilRetry: isset($data['time_until_retry_ms'])
? Duration::fromMilliseconds($data['time_until_retry_ms'])
: null,
);
}
}

View File

@@ -0,0 +1,208 @@
<?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'] ?? [],
);
}
}

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace App\Framework\Monitoring;
use App\Framework\Core\ValueObjects\Timestamp;
/**
* Monitoring alert value object
*/
final readonly class MonitoringAlert
{
public function __construct(
public string $type, // 'circuit_breaker', 'error_boundary', 'system'
public string $severity, // 'info', 'warning', 'critical'
public string $component,
public string $message,
public Timestamp $timestamp,
public array $metrics = [],
public ?string $alertId = null,
) {
}
/**
* Get alert priority (1-5, 5 being highest)
*/
public function getPriority(): int
{
return match ($this->severity) {
'info' => 1,
'warning' => 3,
'critical' => 5,
default => 2,
};
}
/**
* Check if alert requires immediate attention
*/
public function requiresImmediateAttention(): bool
{
return $this->severity === 'critical';
}
/**
* Get alert age
*/
public function getAge(Timestamp $currentTime): \App\Framework\Core\ValueObjects\Duration
{
return $this->timestamp->diff($currentTime);
}
/**
* Convert to array for serialization
*/
public function toArray(): array
{
return [
'alert_id' => $this->alertId ?? uniqid('alert_', true),
'type' => $this->type,
'severity' => $this->severity,
'priority' => $this->getPriority(),
'component' => $this->component,
'message' => $this->message,
'timestamp' => $this->timestamp->toIsoString(),
'requires_immediate_attention' => $this->requiresImmediateAttention(),
'metrics' => $this->metrics,
];
}
/**
* Create from array
*/
public static function fromArray(array $data): self
{
return new self(
type: $data['type'],
severity: $data['severity'],
component: $data['component'],
message: $data['message'],
timestamp: Timestamp::fromFloat(strtotime($data['timestamp'])),
metrics: $data['metrics'] ?? [],
alertId: $data['alert_id'] ?? null,
);
}
}

View File

@@ -0,0 +1,92 @@
<?php
declare(strict_types=1);
namespace App\Framework\Monitoring;
use App\Framework\Core\ValueObjects\Duration;
/**
* Configuration for advanced monitoring system
*/
final readonly class MonitoringConfig
{
public function __construct(
public Duration $metricsRetention = new Duration(86400.0), // 24 hours
public Duration $aggregationInterval = new Duration(300.0), // 5 minutes
public Duration $slowResponseThreshold = new Duration(1.0), // 1 second
public float $warningErrorRateThreshold = 0.05, // 5%
public float $criticalErrorRateThreshold = 0.15, // 15%
public float $warningFallbackRateThreshold = 0.10, // 10%
public float $criticalFallbackRateThreshold = 0.25, // 25%
public bool $enableRealTimeAlerts = true,
public bool $enableTrendAnalysis = true,
public bool $enablePerformanceAnalytics = true,
public int $maxMetricsHistory = 10000,
public array $alertChannels = ['log', 'webhook'],
) {
}
/**
* Create production configuration
*/
public static function production(): self
{
return new self(
metricsRetention: Duration::fromDays(7),
aggregationInterval: Duration::fromMinutes(1),
slowResponseThreshold: Duration::fromMilliseconds(500),
warningErrorRateThreshold: 0.02, // 2%
criticalErrorRateThreshold: 0.05, // 5%
warningFallbackRateThreshold: 0.05, // 5%
criticalFallbackRateThreshold: 0.15, // 15%
enableRealTimeAlerts: true,
enableTrendAnalysis: true,
enablePerformanceAnalytics: true,
maxMetricsHistory: 50000,
alertChannels: ['log', 'webhook', 'email'],
);
}
/**
* Create development configuration
*/
public static function development(): self
{
return new self(
metricsRetention: Duration::fromHours(4),
aggregationInterval: Duration::fromMinutes(5),
slowResponseThreshold: Duration::fromSeconds(2),
warningErrorRateThreshold: 0.10, // 10%
criticalErrorRateThreshold: 0.25, // 25%
warningFallbackRateThreshold: 0.20, // 20%
criticalFallbackRateThreshold: 0.40, // 40%
enableRealTimeAlerts: false,
enableTrendAnalysis: true,
enablePerformanceAnalytics: true,
maxMetricsHistory: 1000,
alertChannels: ['log'],
);
}
/**
* Create high-performance configuration
*/
public static function highPerformance(): self
{
return new self(
metricsRetention: Duration::fromHours(12),
aggregationInterval: Duration::fromSeconds(30),
slowResponseThreshold: Duration::fromMilliseconds(100),
warningErrorRateThreshold: 0.001, // 0.1%
criticalErrorRateThreshold: 0.01, // 1%
warningFallbackRateThreshold: 0.02, // 2%
criticalFallbackRateThreshold: 0.05, // 5%
enableRealTimeAlerts: true,
enableTrendAnalysis: true,
enablePerformanceAnalytics: true,
maxMetricsHistory: 100000,
alertChannels: ['webhook', 'metrics'],
);
}
}

View File

@@ -0,0 +1,181 @@
<?php
declare(strict_types=1);
namespace App\Framework\Monitoring;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Core\ValueObjects\Timestamp;
/**
* System-wide health summary value object
*/
final readonly class SystemHealthSummary
{
public function __construct(
public Timestamp $timestamp,
public int $totalComponents,
public int $healthyComponents,
public int $degradedComponents,
public int $failedComponents,
public Percentage $overallHealthScore,
public int $circuitBreakerCount,
public int $errorBoundaryCount,
) {
}
/**
* Get overall system status
*/
public function getSystemStatus(): string
{
if ($this->failedComponents > 0) {
$failureRate = $this->failedComponents / $this->totalComponents;
if ($failureRate >= 0.5) {
return 'critical';
}
if ($failureRate >= 0.25) {
return 'major_issues';
}
return 'minor_issues';
}
if ($this->degradedComponents > 0) {
$degradedRate = $this->degradedComponents / $this->totalComponents;
if ($degradedRate >= 0.25) {
return 'degraded';
}
return 'mostly_healthy';
}
return 'healthy';
}
/**
* Get health percentage for healthy components
*/
public function getHealthyPercentage(): Percentage
{
if ($this->totalComponents === 0) {
return Percentage::fromFloat(0.0);
}
return Percentage::fromFloat($this->healthyComponents / $this->totalComponents);
}
/**
* Get degraded percentage
*/
public function getDegradedPercentage(): Percentage
{
if ($this->totalComponents === 0) {
return Percentage::fromFloat(0.0);
}
return Percentage::fromFloat($this->degradedComponents / $this->totalComponents);
}
/**
* Get failed percentage
*/
public function getFailedPercentage(): Percentage
{
if ($this->totalComponents === 0) {
return Percentage::fromFloat(0.0);
}
return Percentage::fromFloat($this->failedComponents / $this->totalComponents);
}
/**
* Check if system is fully operational
*/
public function isFullyOperational(): bool
{
return $this->failedComponents === 0 && $this->degradedComponents === 0;
}
/**
* Check if system has critical issues
*/
public function hasCriticalIssues(): bool
{
return $this->getSystemStatus() === 'critical';
}
/**
* Get severity level (0-4: healthy, mostly_healthy, degraded, minor_issues, major_issues, critical)
*/
public function getSeverityLevel(): int
{
return match ($this->getSystemStatus()) {
'healthy' => 0,
'mostly_healthy' => 1,
'degraded' => 2,
'minor_issues' => 3,
'major_issues' => 4,
'critical' => 5,
default => 0,
};
}
/**
* Get summary statistics
*/
public function getStatistics(): array
{
return [
'timestamp' => $this->timestamp->toIsoString(),
'system_status' => $this->getSystemStatus(),
'severity_level' => $this->getSeverityLevel(),
'is_fully_operational' => $this->isFullyOperational(),
'has_critical_issues' => $this->hasCriticalIssues(),
'components' => [
'total' => $this->totalComponents,
'healthy' => $this->healthyComponents,
'degraded' => $this->degradedComponents,
'failed' => $this->failedComponents,
],
'percentages' => [
'healthy' => $this->getHealthyPercentage()->toFloat(),
'degraded' => $this->getDegradedPercentage()->toFloat(),
'failed' => $this->getFailedPercentage()->toFloat(),
],
'overall_health_score' => $this->overallHealthScore->toFloat(),
'component_breakdown' => [
'circuit_breakers' => $this->circuitBreakerCount,
'error_boundaries' => $this->errorBoundaryCount,
],
];
}
/**
* Convert to array for serialization
*/
public function toArray(): array
{
return $this->getStatistics();
}
/**
* Create from array
*/
public static function fromArray(array $data): self
{
return new self(
timestamp: Timestamp::fromFloat(strtotime($data['timestamp'])),
totalComponents: $data['components']['total'],
healthyComponents: $data['components']['healthy'],
degradedComponents: $data['components']['degraded'],
failedComponents: $data['components']['failed'],
overallHealthScore: Percentage::fromFloat($data['overall_health_score']),
circuitBreakerCount: $data['component_breakdown']['circuit_breakers'],
errorBoundaryCount: $data['component_breakdown']['error_boundaries'],
);
}
}