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:
304
src/Framework/ErrorReporting/ErrorStatistics.php
Normal file
304
src/Framework/ErrorReporting/ErrorStatistics.php
Normal file
@@ -0,0 +1,304 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\ErrorReporting;
|
||||
|
||||
use DateTimeImmutable;
|
||||
|
||||
/**
|
||||
* Error statistics and analytics data
|
||||
*/
|
||||
final readonly class ErrorStatistics
|
||||
{
|
||||
public function __construct(
|
||||
public int $totalErrors,
|
||||
public int $uniqueErrors,
|
||||
public array $errorsByLevel,
|
||||
public array $errorsByException,
|
||||
public array $errorsByRoute,
|
||||
public array $errorsByUser,
|
||||
public array $errorsByHour,
|
||||
public array $errorsByDay,
|
||||
public array $topErrors,
|
||||
public array $trendingErrors,
|
||||
public float $errorRate,
|
||||
public array $responseTimeImpact,
|
||||
public array $environmentBreakdown,
|
||||
public DateTimeImmutable $periodStart,
|
||||
public DateTimeImmutable $periodEnd
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create empty statistics
|
||||
*/
|
||||
public static function empty(): self
|
||||
{
|
||||
return new self(
|
||||
totalErrors: 0,
|
||||
uniqueErrors: 0,
|
||||
errorsByLevel: [],
|
||||
errorsByException: [],
|
||||
errorsByRoute: [],
|
||||
errorsByUser: [],
|
||||
errorsByHour: [],
|
||||
errorsByDay: [],
|
||||
topErrors: [],
|
||||
trendingErrors: [],
|
||||
errorRate: 0.0,
|
||||
responseTimeImpact: [],
|
||||
environmentBreakdown: [],
|
||||
periodStart: new DateTimeImmutable(),
|
||||
periodEnd: new DateTimeImmutable()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total critical errors (emergency, alert, critical levels)
|
||||
*/
|
||||
public function getCriticalErrorCount(): int
|
||||
{
|
||||
return ($this->errorsByLevel['emergency'] ?? 0) +
|
||||
($this->errorsByLevel['alert'] ?? 0) +
|
||||
($this->errorsByLevel['critical'] ?? 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get error rate as percentage
|
||||
*/
|
||||
public function getErrorRatePercentage(): float
|
||||
{
|
||||
return round($this->errorRate * 100, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get most problematic route
|
||||
*/
|
||||
public function getMostProblematicRoute(): ?array
|
||||
{
|
||||
if (empty($this->errorsByRoute)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$route = array_key_first($this->errorsByRoute);
|
||||
$count = reset($this->errorsByRoute);
|
||||
|
||||
return [
|
||||
'route' => $route,
|
||||
'count' => $count,
|
||||
'percentage' => $this->totalErrors > 0 ? round(($count / $this->totalErrors) * 100, 2) : 0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get most frequent exception
|
||||
*/
|
||||
public function getMostFrequentException(): ?array
|
||||
{
|
||||
if (empty($this->errorsByException)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$exception = array_key_first($this->errorsByException);
|
||||
$count = reset($this->errorsByException);
|
||||
|
||||
return [
|
||||
'exception' => $exception,
|
||||
'count' => $count,
|
||||
'percentage' => $this->totalErrors > 0 ? round(($count / $this->totalErrors) * 100, 2) : 0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get peak error hour
|
||||
*/
|
||||
public function getPeakErrorHour(): ?array
|
||||
{
|
||||
if (empty($this->errorsByHour)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$hour = array_key_first($this->errorsByHour);
|
||||
$count = reset($this->errorsByHour);
|
||||
|
||||
return [
|
||||
'hour' => $hour,
|
||||
'count' => $count,
|
||||
'time' => sprintf('%02d:00', $hour),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get error trend direction
|
||||
*/
|
||||
public function getErrorTrend(): string
|
||||
{
|
||||
if (count($this->errorsByDay) < 2) {
|
||||
return 'stable';
|
||||
}
|
||||
|
||||
$days = array_values($this->errorsByDay);
|
||||
$recent = array_slice($days, -3); // Last 3 days
|
||||
$previous = array_slice($days, -6, 3); // Previous 3 days
|
||||
|
||||
if (empty($recent) || empty($previous)) {
|
||||
return 'stable';
|
||||
}
|
||||
|
||||
$recentAvg = array_sum($recent) / count($recent);
|
||||
$previousAvg = array_sum($previous) / count($previous);
|
||||
|
||||
$change = $previousAvg > 0 ? (($recentAvg - $previousAvg) / $previousAvg) * 100 : 0;
|
||||
|
||||
if ($change > 20) {
|
||||
return 'increasing';
|
||||
} elseif ($change < -20) {
|
||||
return 'decreasing';
|
||||
} else {
|
||||
return 'stable';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get error distribution health score (0-100)
|
||||
*/
|
||||
public function getHealthScore(): int
|
||||
{
|
||||
$score = 100;
|
||||
|
||||
// Penalize high error rates
|
||||
if ($this->errorRate > 0.05) { // > 5%
|
||||
$score -= 30;
|
||||
} elseif ($this->errorRate > 0.02) { // > 2%
|
||||
$score -= 15;
|
||||
} elseif ($this->errorRate > 0.01) { // > 1%
|
||||
$score -= 5;
|
||||
}
|
||||
|
||||
// Penalize critical errors
|
||||
$criticalCount = $this->getCriticalErrorCount();
|
||||
if ($criticalCount > 0) {
|
||||
$score -= min(40, $criticalCount * 5);
|
||||
}
|
||||
|
||||
// Penalize trending up
|
||||
if ($this->getErrorTrend() === 'increasing') {
|
||||
$score -= 15;
|
||||
}
|
||||
|
||||
// Penalize concentrated errors (single route causing many errors)
|
||||
$topRoute = $this->getMostProblematicRoute();
|
||||
if ($topRoute && $topRoute['percentage'] > 50) {
|
||||
$score -= 10;
|
||||
}
|
||||
|
||||
return max(0, $score);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get insights and recommendations
|
||||
*/
|
||||
public function getInsights(): array
|
||||
{
|
||||
$insights = [];
|
||||
|
||||
// Critical error insight
|
||||
$criticalCount = $this->getCriticalErrorCount();
|
||||
if ($criticalCount > 0) {
|
||||
$insights[] = [
|
||||
'type' => 'critical',
|
||||
'message' => "Found {$criticalCount} critical errors that require immediate attention.",
|
||||
'priority' => 'high',
|
||||
];
|
||||
}
|
||||
|
||||
// Error rate insight
|
||||
if ($this->errorRate > 0.05) {
|
||||
$insights[] = [
|
||||
'type' => 'error_rate',
|
||||
'message' => sprintf('Error rate is %.2f%%, which is above the recommended threshold of 2%%.', $this->getErrorRatePercentage()),
|
||||
'priority' => 'high',
|
||||
];
|
||||
}
|
||||
|
||||
// Route concentration insight
|
||||
$topRoute = $this->getMostProblematicRoute();
|
||||
if ($topRoute && $topRoute['percentage'] > 40) {
|
||||
$insights[] = [
|
||||
'type' => 'route_concentration',
|
||||
'message' => sprintf('Route "%s" accounts for %.1f%% of all errors. Consider reviewing this endpoint.', $topRoute['route'], $topRoute['percentage']),
|
||||
'priority' => 'medium',
|
||||
];
|
||||
}
|
||||
|
||||
// Exception concentration insight
|
||||
$topException = $this->getMostFrequentException();
|
||||
if ($topException && $topException['percentage'] > 50) {
|
||||
$insights[] = [
|
||||
'type' => 'exception_concentration',
|
||||
'message' => sprintf('Exception "%s" accounts for %.1f%% of all errors. This indicates a systemic issue.', basename($topException['exception']), $topException['percentage']),
|
||||
'priority' => 'medium',
|
||||
];
|
||||
}
|
||||
|
||||
// Trend insight
|
||||
$trend = $this->getErrorTrend();
|
||||
if ($trend === 'increasing') {
|
||||
$insights[] = [
|
||||
'type' => 'trend',
|
||||
'message' => 'Error trend is increasing over the last few days. Monitor closely.',
|
||||
'priority' => 'medium',
|
||||
];
|
||||
} elseif ($trend === 'decreasing') {
|
||||
$insights[] = [
|
||||
'type' => 'trend',
|
||||
'message' => 'Error trend is decreasing. Good progress!',
|
||||
'priority' => 'low',
|
||||
];
|
||||
}
|
||||
|
||||
// No errors insight
|
||||
if ($this->totalErrors === 0) {
|
||||
$insights[] = [
|
||||
'type' => 'no_errors',
|
||||
'message' => 'No errors recorded in this period. System is operating smoothly.',
|
||||
'priority' => 'low',
|
||||
];
|
||||
}
|
||||
|
||||
return $insights;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'total_errors' => $this->totalErrors,
|
||||
'unique_errors' => $this->uniqueErrors,
|
||||
'errors_by_level' => $this->errorsByLevel,
|
||||
'errors_by_exception' => $this->errorsByException,
|
||||
'errors_by_route' => $this->errorsByRoute,
|
||||
'errors_by_user' => $this->errorsByUser,
|
||||
'errors_by_hour' => $this->errorsByHour,
|
||||
'errors_by_day' => $this->errorsByDay,
|
||||
'top_errors' => $this->topErrors,
|
||||
'trending_errors' => $this->trendingErrors,
|
||||
'error_rate' => $this->errorRate,
|
||||
'error_rate_percentage' => $this->getErrorRatePercentage(),
|
||||
'response_time_impact' => $this->responseTimeImpact,
|
||||
'environment_breakdown' => $this->environmentBreakdown,
|
||||
'period_start' => $this->periodStart->format('c'),
|
||||
'period_end' => $this->periodEnd->format('c'),
|
||||
'critical_error_count' => $this->getCriticalErrorCount(),
|
||||
'most_problematic_route' => $this->getMostProblematicRoute(),
|
||||
'most_frequent_exception' => $this->getMostFrequentException(),
|
||||
'peak_error_hour' => $this->getPeakErrorHour(),
|
||||
'error_trend' => $this->getErrorTrend(),
|
||||
'health_score' => $this->getHealthScore(),
|
||||
'insights' => $this->getInsights(),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user