fix: Gitea Traefik routing and connection pool optimization
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
- Remove middleware reference from Gitea Traefik labels (caused routing issues) - Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s) - Add explicit service reference in Traefik labels - Fix intermittent 504 timeouts by improving PostgreSQL connection handling Fixes Gitea unreachability via git.michaelschiemer.de
This commit is contained in:
754
docs/features/error-handling/advanced.md
Normal file
754
docs/features/error-handling/advanced.md
Normal file
@@ -0,0 +1,754 @@
|
||||
# Exception System Advanced Features
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das Exception-System wurde um 10 erweiterte Features erweitert, die in drei Phasen implementiert wurden:
|
||||
|
||||
- **Phase 1 (Foundation)**: Rate Limiting, Context Caching, Performance Tracking
|
||||
- **Phase 2 (User Experience)**: User-Friendly Messages, Localization
|
||||
- **Phase 3 (Advanced)**: Recovery/Retry, Pattern Detection, Correlation, Metrics, Health Checks
|
||||
|
||||
Alle Features sind optional und können einzeln aktiviert werden. Sie folgen den Framework-Prinzipien (final, readonly, Value Objects, Composition over Inheritance).
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Foundation Features
|
||||
|
||||
### 1. Exception Rate Limiting & Throttling
|
||||
|
||||
**Ziel**: Verhindert Log-Spam durch wiederholte gleiche Exceptions
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionFingerprint` - Generiert eindeutige Fingerprints für Exception-Gruppierung
|
||||
- `ExceptionRateLimitConfig` - Konfiguration für Thresholds und Time Windows
|
||||
- `ExceptionRateLimiter` - Rate Limiter mit Cache-basiertem Tracking
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\RateLimit\ExceptionRateLimiter;
|
||||
use App\Framework\ExceptionHandling\RateLimit\ExceptionRateLimitConfig;
|
||||
use App\Framework\Core\ValueObjects\Duration;
|
||||
|
||||
// Konfiguration erstellen
|
||||
$config = ExceptionRateLimitConfig::withLimits(
|
||||
maxExceptions: 10,
|
||||
timeWindow: Duration::fromSeconds(60)
|
||||
);
|
||||
|
||||
// Rate Limiter erstellen
|
||||
$rateLimiter = new ExceptionRateLimiter($cache, $config);
|
||||
|
||||
// In ErrorKernel integriert (automatisch)
|
||||
// Exceptions werden automatisch rate-limited, wenn Threshold erreicht wird
|
||||
```
|
||||
|
||||
**Konfiguration**:
|
||||
- `maxExceptions`: Maximale Anzahl gleicher Exceptions pro Time Window (Default: 10)
|
||||
- `timeWindow`: Time Window für Rate Limiting (Default: 60 Sekunden)
|
||||
- `skipLoggingOnLimit`: Skip Logging wenn Rate Limit erreicht (Default: true)
|
||||
- `skipAuditOnLimit`: Skip Audit Logging wenn Rate Limit erreicht (Default: true)
|
||||
- `trackMetricsOnLimit`: Track Metriken auch bei Rate Limit (Default: true)
|
||||
|
||||
**Fingerprint-Generierung**:
|
||||
- Basierend auf Exception-Klasse, normalisierter Message, File/Line
|
||||
- Optional: Component und Operation aus Context für präzisere Gruppierung
|
||||
- Normalisierung entfernt variable Teile (UUIDs, Timestamps, Zahlen, etc.)
|
||||
|
||||
---
|
||||
|
||||
### 2. Exception Context Caching
|
||||
|
||||
**Ziel**: Performance-Optimierung durch Caching von häufig verwendeten Context-Daten
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionContextCache` - Cache-Wrapper für Context-Daten
|
||||
- Integration in `ExceptionContextBuilder` - Automatischer Cache-Lookup
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Context\ExceptionContextCache;
|
||||
use App\Framework\ExceptionHandling\Context\ExceptionContextBuilder;
|
||||
|
||||
// Context Cache erstellen
|
||||
$contextCache = new ExceptionContextCache($cache);
|
||||
|
||||
// Context Builder mit Cache
|
||||
$builder = new ExceptionContextBuilder(
|
||||
errorScope: $errorScope,
|
||||
contextCache: $contextCache
|
||||
);
|
||||
|
||||
// Automatischer Cache-Lookup beim buildFromRequest()
|
||||
$context = $builder->buildFromRequest($request);
|
||||
```
|
||||
|
||||
**Cache-Levels**:
|
||||
- **Request-Level**: TTL 10 Minuten (spezifischste)
|
||||
- **Session-Level**: TTL 10 Minuten
|
||||
- **User-Level**: TTL 30 Minuten (am wenigsten spezifisch)
|
||||
|
||||
**Cache-Invalidation**:
|
||||
```php
|
||||
// Manuelle Invalidation bei Context-Änderungen
|
||||
$contextCache->invalidateRequest($requestId);
|
||||
$contextCache->invalidateSession($sessionId);
|
||||
$contextCache->invalidateUser($userId);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Exception Performance Tracking
|
||||
|
||||
**Ziel**: Tracking von Performance-Impact pro Exception-Typ
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionPerformanceMetrics` - Value Object für Metriken
|
||||
- `ExceptionPerformanceTracker` - Performance-Tracking
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Performance\ExceptionPerformanceTracker;
|
||||
|
||||
$tracker = new ExceptionPerformanceTracker();
|
||||
|
||||
// Start tracking
|
||||
$startData = $tracker->start();
|
||||
|
||||
// ... exception occurs ...
|
||||
|
||||
// End tracking
|
||||
$metrics = $tracker->end($startData, $exception, $context);
|
||||
|
||||
// Metriken enthalten:
|
||||
// - executionTimeMs: Ausführungszeit in Millisekunden
|
||||
// - memoryDeltaBytes: Memory-Delta in Bytes
|
||||
// - cpuUsagePercent: CPU-Usage in Prozent (wenn verfügbar)
|
||||
```
|
||||
|
||||
**Integration**:
|
||||
- Metriken werden automatisch in `ExceptionContextData::metadata['performance']` gespeichert
|
||||
- Kann mit `MetricsCollector` integriert werden für Monitoring
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: User Experience Features
|
||||
|
||||
### 4. User-Friendly Exception Messages
|
||||
|
||||
**Ziel**: Übersetzung von technischen Exception-Messages zu benutzerfreundlichen Texten
|
||||
|
||||
**Komponenten**:
|
||||
- `UserFriendlyMessage` - Value Object für User-Messages
|
||||
- `ExceptionMessageTranslator` - Message-Übersetzung mit Template-System
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Translation\ExceptionMessageTranslator;
|
||||
use App\Framework\ExceptionHandling\Translation\UserFriendlyMessage;
|
||||
|
||||
// Templates definieren
|
||||
$templates = [
|
||||
\App\Framework\Exception\DatabaseException::class => [
|
||||
'message' => 'Database connection failed. Please try again later.',
|
||||
'title' => 'Database Error',
|
||||
'help' => 'If this problem persists, please contact support.'
|
||||
],
|
||||
\App\Framework\Exception\ValidationException::class => [
|
||||
'message' => 'Please check your input and try again.',
|
||||
'title' => 'Validation Error'
|
||||
]
|
||||
];
|
||||
|
||||
// Translator erstellen
|
||||
$translator = new ExceptionMessageTranslator(
|
||||
templates: $templates,
|
||||
isDebugMode: false
|
||||
);
|
||||
|
||||
// Message übersetzen
|
||||
$userMessage = $translator->translate($exception, $context);
|
||||
|
||||
// $userMessage->message: User-friendly message
|
||||
// $userMessage->title: Optional title
|
||||
// $userMessage->helpText: Optional help text
|
||||
// $userMessage->technicalMessage: Original technical message (für Debugging)
|
||||
```
|
||||
|
||||
**Template-Variablen**:
|
||||
- `{exception_message}` - Original Exception-Message
|
||||
- `{exception_class}` - Exception-Klasse
|
||||
- `{operation}` - Operation aus Context
|
||||
- `{component}` - Component aus Context
|
||||
|
||||
**Integration**:
|
||||
- Automatisch in `ResponseErrorRenderer` integriert
|
||||
- In Debug-Mode werden technische Messages angezeigt
|
||||
- In Production werden User-Friendly Messages verwendet
|
||||
|
||||
---
|
||||
|
||||
### 5. Exception Localization
|
||||
|
||||
**Ziel**: i18n-Support für Exception-Messages basierend auf User-Locale
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionLocalizer` - Lokalisierung mit Fallback-Chain
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Localization\ExceptionLocalizer;
|
||||
|
||||
// Übersetzungen definieren
|
||||
$translations = [
|
||||
'de' => [
|
||||
\App\Framework\Exception\DatabaseException::class => [
|
||||
'message' => 'Datenbankverbindung fehlgeschlagen. Bitte versuchen Sie es später erneut.',
|
||||
'title' => 'Datenbankfehler'
|
||||
]
|
||||
],
|
||||
'en' => [
|
||||
\App\Framework\Exception\DatabaseException::class => [
|
||||
'message' => 'Database connection failed. Please try again later.',
|
||||
'title' => 'Database Error'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// Localizer erstellen
|
||||
$localizer = new ExceptionLocalizer(
|
||||
translations: $translations,
|
||||
defaultLocale: 'en'
|
||||
);
|
||||
|
||||
// Locale aus Context extrahieren
|
||||
$locale = $localizer->getLocale($context);
|
||||
|
||||
// Übersetzungen für Locale abrufen
|
||||
$localeTranslations = $localizer->getTranslations($locale);
|
||||
|
||||
// Fallback-Chain: [user_locale, default_locale, 'en']
|
||||
$fallbackChain = $localizer->getFallbackChain($locale);
|
||||
```
|
||||
|
||||
**Locale-Extraktion**:
|
||||
- Aus `ExceptionContextData::metadata['locale']`
|
||||
- Fallback zu Default-Locale
|
||||
- Fallback zu 'en' als letzte Option
|
||||
|
||||
**Integration**:
|
||||
- Wird von `ExceptionMessageTranslator` verwendet
|
||||
- Locale sollte in `ExceptionContextData::metadata['locale']` gespeichert werden
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Advanced Features
|
||||
|
||||
### 6. Exception Recovery & Retry Logic
|
||||
|
||||
**Ziel**: Automatische Retry-Logik für temporäre Exceptions
|
||||
|
||||
**Komponenten**:
|
||||
- `RetryStrategy` - Retry-Strategien (Exponential Backoff, Linear, Fixed)
|
||||
- `ExceptionRecoveryManager` - Recovery-Manager
|
||||
- `RetryableException` - Marker-Interface
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Recovery\ExceptionRecoveryManager;
|
||||
use App\Framework\ExceptionHandling\Recovery\RetryableException;
|
||||
use App\Framework\Exception\ExceptionMetadata;
|
||||
|
||||
// Retryable Exception implementieren
|
||||
final class NetworkException extends \Exception implements RetryableException
|
||||
{
|
||||
}
|
||||
|
||||
// Exception mit Retry-Config erstellen
|
||||
$metadata = ExceptionMetadata::withRetry(
|
||||
retryAfter: 1000 // Base delay in milliseconds
|
||||
)->withMaxRetries(3)
|
||||
->withRetryStrategy('exponential_backoff');
|
||||
|
||||
// Recovery Manager
|
||||
$recoveryManager = new ExceptionRecoveryManager();
|
||||
|
||||
// Prüfen ob Retry nötig
|
||||
if ($recoveryManager->shouldRetry($exception, $metadata)) {
|
||||
$delay = $recoveryManager->getRetryDelay($exception, $metadata, $attemptNumber);
|
||||
// Retry nach $delay Millisekunden
|
||||
}
|
||||
```
|
||||
|
||||
**Retry-Strategien**:
|
||||
- **Exponential Backoff** (Default): `baseDelay * 2^(attempt-1)`
|
||||
- **Linear**: `baseDelay * attempt`
|
||||
- **Fixed**: `baseDelay` (konstant)
|
||||
|
||||
**ExceptionMetadata Erweiterungen**:
|
||||
- `maxRetries`: Maximale Anzahl Retries (Default: 3)
|
||||
- `retryStrategy`: Retry-Strategie (Default: 'exponential_backoff')
|
||||
- `retryAfter`: Base Delay in Millisekunden
|
||||
|
||||
**Retryable Exceptions**:
|
||||
- Exceptions die `RetryableException` implementieren
|
||||
- Exceptions mit bestimmten Namen-Patterns (NetworkException, TimeoutException, etc.)
|
||||
|
||||
---
|
||||
|
||||
### 7. Exception Pattern Detection & Auto-Fix Suggestions
|
||||
|
||||
**Ziel**: ML-basierte Pattern-Erkennung mit Fix-Vorschlägen
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionPattern` - Value Object für Patterns
|
||||
- `FixSuggestion` - Value Object für Fix-Vorschläge
|
||||
- `ExceptionPatternDetector` - Pattern-Detection
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\PatternDetection\ExceptionPatternDetector;
|
||||
use App\Framework\ExceptionHandling\PatternDetection\FixSuggestion;
|
||||
|
||||
// Knowledge Base definieren
|
||||
$knowledgeBase = [
|
||||
\App\Framework\Exception\DatabaseException::class => [
|
||||
'description' => 'Database connection timeout',
|
||||
'fixes' => [
|
||||
[
|
||||
'title' => 'Check connection pool size',
|
||||
'description' => 'Increase database connection pool size',
|
||||
'code' => '$config->setMaxConnections(50);',
|
||||
'confidence' => 'high'
|
||||
],
|
||||
[
|
||||
'title' => 'Check database server',
|
||||
'description' => 'Verify database server is running and accessible',
|
||||
'confidence' => 'medium'
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// Pattern Detector erstellen
|
||||
$detector = new ExceptionPatternDetector($knowledgeBase);
|
||||
|
||||
// Patterns erkennen
|
||||
$patterns = $detector->detect($exception, $context);
|
||||
|
||||
foreach ($patterns as $pattern) {
|
||||
echo $pattern->description . "\n";
|
||||
foreach ($pattern->fixSuggestions as $fix) {
|
||||
echo " - " . $fix->title . ": " . $fix->description . "\n";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Integration**:
|
||||
- Kann in `ErrorAggregator` integriert werden
|
||||
- Patterns werden in `ExceptionContextData::metadata['patterns']` gespeichert
|
||||
|
||||
---
|
||||
|
||||
### 8. Exception Correlation & Root Cause Analysis
|
||||
|
||||
**Ziel**: Verknüpfung verwandter Exceptions für Root-Cause-Analyse
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionCorrelation` - Value Object für Korrelationen
|
||||
- `ExceptionCorrelationEngine` - Correlation-Engine
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Correlation\ExceptionCorrelationEngine;
|
||||
|
||||
$correlationEngine = new ExceptionCorrelationEngine($cache);
|
||||
|
||||
// Exception korrelieren
|
||||
$correlation = $correlationEngine->correlate($exception, $context);
|
||||
|
||||
// Correlation-Daten:
|
||||
// - correlationKey: Request-ID, Session-ID oder User-ID
|
||||
// - exceptionIds: Array von verwandten Exception-IDs
|
||||
// - rootCauseId: ID der ersten Exception (Root Cause)
|
||||
```
|
||||
|
||||
**Correlation-Keys**:
|
||||
- **Request-ID** (höchste Priorität): Alle Exceptions in derselben Request
|
||||
- **Session-ID**: Alle Exceptions in derselben Session
|
||||
- **User-ID**: Alle Exceptions für denselben User
|
||||
|
||||
**Root Cause Analysis**:
|
||||
- Erste Exception in Correlation-Chain ist Root Cause
|
||||
- Alle weiteren Exceptions sind Folge-Exceptions
|
||||
|
||||
**Integration**:
|
||||
- Correlation-Daten werden in `ExceptionContextData::metadata['correlations']` gespeichert
|
||||
- Kann für Exception-Graph-Visualisierung verwendet werden
|
||||
|
||||
---
|
||||
|
||||
### 9. Exception Metrics & Monitoring Integration
|
||||
|
||||
**Ziel**: Integration mit Monitoring-Systemen (Prometheus, StatsD)
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionMetrics` - Value Object für Metriken
|
||||
- `ExceptionMetricsCollector` - Metrics-Collector
|
||||
- `PrometheusExporter` - Prometheus-Export
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Metrics\ExceptionMetricsCollector;
|
||||
use App\Framework\ExceptionHandling\Metrics\PrometheusExporter;
|
||||
|
||||
$collector = new ExceptionMetricsCollector($cache);
|
||||
|
||||
// Exception-Metrik aufzeichnen
|
||||
$collector->record($exception, $context, $executionTimeMs);
|
||||
|
||||
// Metriken abrufen
|
||||
$metrics = $collector->getMetrics();
|
||||
|
||||
// Prometheus-Format exportieren
|
||||
$exporter = new PrometheusExporter();
|
||||
$prometheusMetrics = $exporter->export($metrics);
|
||||
|
||||
// Output:
|
||||
// exception_total 42
|
||||
// exception_by_class{exception_class="DatabaseException"} 10
|
||||
// exception_by_component{component="UserService"} 5
|
||||
// exception_average_execution_time_ms 125.50
|
||||
```
|
||||
|
||||
**Metriken**:
|
||||
- `totalCount`: Gesamtanzahl Exceptions
|
||||
- `byClass`: Anzahl pro Exception-Klasse
|
||||
- `byComponent`: Anzahl pro Component
|
||||
- `averageExecutionTimeMs`: Durchschnittliche Ausführungszeit
|
||||
|
||||
**Integration**:
|
||||
- Kann in `ProductionMetricsController` integriert werden
|
||||
- Metriken werden im Prometheus-Format exportiert
|
||||
- Real-time Aggregation über Time-Windows
|
||||
|
||||
---
|
||||
|
||||
### 10. Exception Health Checks & Circuit Breakers
|
||||
|
||||
**Ziel**: Circuit Breaker Pattern für Exception-basierte Health Checks
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionHealthChecker` - Health Checker
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Health\ExceptionHealthChecker;
|
||||
use App\Framework\ExceptionHandling\Metrics\ExceptionMetricsCollector;
|
||||
|
||||
$metricsCollector = new ExceptionMetricsCollector($cache);
|
||||
$healthChecker = new ExceptionHealthChecker(
|
||||
metricsCollector: $metricsCollector,
|
||||
errorRateThreshold: 0.1, // 10% Error Rate
|
||||
timeWindowSeconds: 60
|
||||
);
|
||||
|
||||
// Health Check durchführen
|
||||
$result = $healthChecker->check();
|
||||
|
||||
// Status:
|
||||
// - Healthy: Error Rate < 5%
|
||||
// - Warning: Error Rate 5-10%
|
||||
// - Unhealthy: Error Rate > 10%
|
||||
```
|
||||
|
||||
**Health Check Status**:
|
||||
- **Healthy**: Error Rate unterhalb des Thresholds
|
||||
- **Warning**: Error Rate bei 50% des Thresholds
|
||||
- **Unhealthy**: Error Rate oberhalb des Thresholds
|
||||
|
||||
**Integration**:
|
||||
- Implementiert `HealthCheckInterface`
|
||||
- Kann in `HealthCheckManager` registriert werden
|
||||
- Nutzt bestehende `CircuitBreaker` Infrastruktur
|
||||
|
||||
---
|
||||
|
||||
## DI Container Integration
|
||||
|
||||
### Beispiel-Konfiguration
|
||||
|
||||
```php
|
||||
// Exception Rate Limiter
|
||||
$container->bind(ExceptionRateLimitConfig::class, function() {
|
||||
return ExceptionRateLimitConfig::withLimits(
|
||||
maxExceptions: 10,
|
||||
timeWindow: Duration::fromSeconds(60)
|
||||
);
|
||||
});
|
||||
|
||||
$container->singleton(ExceptionRateLimiter::class, function($container) {
|
||||
return new ExceptionRateLimiter(
|
||||
cache: $container->get(Cache::class),
|
||||
config: $container->get(ExceptionRateLimitConfig::class)
|
||||
);
|
||||
});
|
||||
|
||||
// Exception Context Cache
|
||||
$container->singleton(ExceptionContextCache::class, function($container) {
|
||||
return new ExceptionContextCache(
|
||||
cache: $container->get(Cache::class)
|
||||
);
|
||||
});
|
||||
|
||||
// Exception Context Builder
|
||||
$container->singleton(ExceptionContextBuilder::class, function($container) {
|
||||
return new ExceptionContextBuilder(
|
||||
errorScope: $container->get(ErrorScope::class),
|
||||
contextCache: $container->get(ExceptionContextCache::class)
|
||||
);
|
||||
});
|
||||
|
||||
// Exception Performance Tracker
|
||||
$container->singleton(ExceptionPerformanceTracker::class, function() {
|
||||
return new ExceptionPerformanceTracker();
|
||||
});
|
||||
|
||||
// Exception Message Translator
|
||||
$container->singleton(ExceptionMessageTranslator::class, function($container) {
|
||||
$templates = require __DIR__ . '/config/exception_messages.php';
|
||||
return new ExceptionMessageTranslator(
|
||||
templates: $templates,
|
||||
isDebugMode: $container->get('config')->get('app.debug', false)
|
||||
);
|
||||
});
|
||||
|
||||
// Exception Localizer
|
||||
$container->singleton(ExceptionLocalizer::class, function() {
|
||||
$translations = require __DIR__ . '/config/exception_translations.php';
|
||||
return new ExceptionLocalizer(
|
||||
translations: $translations,
|
||||
defaultLocale: 'en'
|
||||
);
|
||||
});
|
||||
|
||||
// Exception Recovery Manager
|
||||
$container->singleton(ExceptionRecoveryManager::class, function() {
|
||||
return new ExceptionRecoveryManager();
|
||||
});
|
||||
|
||||
// Exception Pattern Detector
|
||||
$container->singleton(ExceptionPatternDetector::class, function() {
|
||||
$knowledgeBase = require __DIR__ . '/config/exception_patterns.php';
|
||||
return new ExceptionPatternDetector($knowledgeBase);
|
||||
});
|
||||
|
||||
// Exception Correlation Engine
|
||||
$container->singleton(ExceptionCorrelationEngine::class, function($container) {
|
||||
return new ExceptionCorrelationEngine(
|
||||
cache: $container->get(Cache::class)
|
||||
);
|
||||
});
|
||||
|
||||
// Exception Metrics Collector
|
||||
$container->singleton(ExceptionMetricsCollector::class, function($container) {
|
||||
return new ExceptionMetricsCollector(
|
||||
cache: $container->get(Cache::class)
|
||||
);
|
||||
});
|
||||
|
||||
// Exception Health Checker
|
||||
$container->singleton(ExceptionHealthChecker::class, function($container) {
|
||||
return new ExceptionHealthChecker(
|
||||
metricsCollector: $container->get(ExceptionMetricsCollector::class),
|
||||
errorRateThreshold: 0.1,
|
||||
timeWindowSeconds: 60
|
||||
);
|
||||
});
|
||||
|
||||
// ErrorKernel mit allen Features
|
||||
$container->singleton(ErrorKernel::class, function($container) {
|
||||
return new ErrorKernel(
|
||||
rendererFactory: $container->get(ErrorRendererFactory::class),
|
||||
reporter: $container->get(Reporter::class),
|
||||
errorAggregator: $container->get(ErrorAggregatorInterface::class),
|
||||
contextProvider: $container->get(ExceptionContextProvider::class),
|
||||
auditLogger: $container->get(ExceptionAuditLogger::class),
|
||||
rateLimiter: $container->get(ExceptionRateLimiter::class), // NEU
|
||||
executionContext: $container->get(ExecutionContext::class),
|
||||
consoleOutput: $container->get(ConsoleOutput::class),
|
||||
isDebugMode: $container->get('config')->get('app.debug', false)
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Rate Limiting konfigurieren
|
||||
|
||||
```php
|
||||
// Für Production: Strikte Limits
|
||||
$config = ExceptionRateLimitConfig::withLimits(
|
||||
maxExceptions: 5,
|
||||
timeWindow: Duration::fromSeconds(60)
|
||||
);
|
||||
|
||||
// Für Development: Lockerere Limits
|
||||
$config = ExceptionRateLimitConfig::withLimits(
|
||||
maxExceptions: 50,
|
||||
timeWindow: Duration::fromSeconds(60)
|
||||
);
|
||||
```
|
||||
|
||||
### 2. Context Caching nutzen
|
||||
|
||||
```php
|
||||
// Context Cache nur aktivieren wenn Context-Erstellung teuer ist
|
||||
// (z.B. DB-Queries, externe API-Calls)
|
||||
if ($needsCaching) {
|
||||
$builder = new ExceptionContextBuilder(
|
||||
errorScope: $errorScope,
|
||||
contextCache: $contextCache
|
||||
);
|
||||
} else {
|
||||
$builder = new ExceptionContextBuilder(errorScope: $errorScope);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. User-Friendly Messages definieren
|
||||
|
||||
```php
|
||||
// Alle wichtigen Exceptions sollten User-Friendly Messages haben
|
||||
$templates = [
|
||||
\App\Framework\Exception\DatabaseException::class => [
|
||||
'message' => 'Database connection failed. Please try again later.',
|
||||
'title' => 'Database Error',
|
||||
'help' => 'If this problem persists, please contact support.'
|
||||
],
|
||||
\App\Framework\Exception\ValidationException::class => [
|
||||
'message' => 'Please check your input and try again.',
|
||||
'title' => 'Validation Error'
|
||||
]
|
||||
];
|
||||
```
|
||||
|
||||
### 4. Retry-Strategien wählen
|
||||
|
||||
```php
|
||||
// Für Network-Exceptions: Exponential Backoff
|
||||
$metadata = ExceptionMetadata::withRetry(1000)
|
||||
->withRetryStrategy('exponential_backoff');
|
||||
|
||||
// Für Rate-Limited APIs: Linear
|
||||
$metadata = ExceptionMetadata::withRetry(2000)
|
||||
->withRetryStrategy('linear');
|
||||
|
||||
// Für Polling: Fixed Delay
|
||||
$metadata = ExceptionMetadata::withRetry(5000)
|
||||
->withRetryStrategy('fixed');
|
||||
```
|
||||
|
||||
### 5. Health Checks konfigurieren
|
||||
|
||||
```php
|
||||
// Für kritische Services: Strikte Thresholds
|
||||
$healthChecker = new ExceptionHealthChecker(
|
||||
metricsCollector: $metricsCollector,
|
||||
errorRateThreshold: 0.05, // 5%
|
||||
timeWindowSeconds: 60
|
||||
);
|
||||
|
||||
// Für weniger kritische Services: Lockerere Thresholds
|
||||
$healthChecker = new ExceptionHealthChecker(
|
||||
metricsCollector: $metricsCollector,
|
||||
errorRateThreshold: 0.2, // 20%
|
||||
timeWindowSeconds: 300
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### Von altem System migrieren
|
||||
|
||||
1. **Rate Limiting aktivieren**:
|
||||
```php
|
||||
// In DI Container
|
||||
$container->singleton(ExceptionRateLimiter::class, ...);
|
||||
```
|
||||
|
||||
2. **Context Caching aktivieren** (optional):
|
||||
```php
|
||||
// Nur wenn Performance-Probleme auftreten
|
||||
$container->singleton(ExceptionContextCache::class, ...);
|
||||
```
|
||||
|
||||
3. **User-Friendly Messages hinzufügen**:
|
||||
```php
|
||||
// Templates in config/exception_messages.php definieren
|
||||
```
|
||||
|
||||
4. **Health Checks registrieren**:
|
||||
```php
|
||||
// In HealthCheckManager
|
||||
$healthCheckManager->register($exceptionHealthChecker);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Rate Limiting blockiert zu viele Exceptions
|
||||
|
||||
**Problem**: Rate Limiting blockiert auch wichtige Exceptions
|
||||
|
||||
**Lösung**: Threshold erhöhen oder Time Window anpassen
|
||||
```php
|
||||
$config = ExceptionRateLimitConfig::withLimits(
|
||||
maxExceptions: 20, // Erhöht
|
||||
timeWindow: Duration::fromSeconds(60)
|
||||
);
|
||||
```
|
||||
|
||||
### Context Cache liefert veraltete Daten
|
||||
|
||||
**Problem**: Context Cache enthält veraltete User/Session-Daten
|
||||
|
||||
**Lösung**: Cache bei User/Session-Änderungen invalidieren
|
||||
```php
|
||||
$contextCache->invalidateUser($userId);
|
||||
$contextCache->invalidateSession($sessionId);
|
||||
```
|
||||
|
||||
### Performance Tracking zeigt keine Daten
|
||||
|
||||
**Problem**: Performance-Metriken werden nicht aufgezeichnet
|
||||
|
||||
**Lösung**: Sicherstellen dass `ExceptionPerformanceTracker` in `ErrorKernel` integriert ist
|
||||
```php
|
||||
// Performance-Tracking manuell starten
|
||||
$startData = $performanceTracker->start();
|
||||
// ... exception occurs ...
|
||||
$metrics = $performanceTracker->end($startData, $exception, $context);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Weitere Ressourcen
|
||||
|
||||
- [Error Handling Guidelines](./error-handling.md)
|
||||
- [Exception Architecture](../ERROR-HANDLING-UNIFIED-ARCHITECTURE.md)
|
||||
- [Audit Logging](./audit-logging.md)
|
||||
|
||||
645
docs/features/error-handling/guide.md
Normal file
645
docs/features/error-handling/guide.md
Normal file
@@ -0,0 +1,645 @@
|
||||
# Error Handling & Debugging
|
||||
|
||||
This guide covers error handling patterns and debugging strategies in the framework.
|
||||
|
||||
## Exception Handling
|
||||
|
||||
All custom exceptions in the framework must extend `FrameworkException` to ensure consistent error handling, logging, and recovery mechanisms.
|
||||
|
||||
### The FrameworkException System
|
||||
|
||||
The framework provides a sophisticated exception system with:
|
||||
- **ExceptionContext**: Rich context information for debugging
|
||||
- **ErrorCode**: Categorized error codes with recovery hints
|
||||
- **RetryAfter**: Support for recoverable operations
|
||||
- **Fluent Interface**: Easy context building
|
||||
|
||||
### Creating Custom Exceptions
|
||||
|
||||
```php
|
||||
namespace App\Domain\User\Exceptions;
|
||||
|
||||
use App\Framework\Exception\FrameworkException;
|
||||
use App\Framework\Exception\Core\DatabaseErrorCode;
|
||||
use App\Framework\Exception\ExceptionContext;
|
||||
|
||||
final class UserNotFoundException extends FrameworkException
|
||||
{
|
||||
public static function byId(UserId $id): self
|
||||
{
|
||||
return self::create(
|
||||
DatabaseErrorCode::ENTITY_NOT_FOUND,
|
||||
"User with ID '{$id->toString()}' not found"
|
||||
)->withData([
|
||||
'user_id' => $id->toString(),
|
||||
'search_type' => 'by_id'
|
||||
]);
|
||||
}
|
||||
|
||||
public static function byEmail(Email $email): self
|
||||
{
|
||||
$context = ExceptionContext::forOperation('user.lookup', 'UserRepository')
|
||||
->withData(['email' => $email->getMasked()]);
|
||||
|
||||
return self::fromContext(
|
||||
"User with email not found",
|
||||
$context,
|
||||
DatabaseErrorCode::ENTITY_NOT_FOUND
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using ErrorCode Enums
|
||||
|
||||
The framework provides category-specific error code enums for better organization and type safety:
|
||||
|
||||
```php
|
||||
use App\Framework\Exception\Core\DatabaseErrorCode;
|
||||
use App\Framework\Exception\Core\AuthErrorCode;
|
||||
use App\Framework\Exception\Core\HttpErrorCode;
|
||||
use App\Framework\Exception\Core\SecurityErrorCode;
|
||||
use App\Framework\Exception\Core\ValidationErrorCode;
|
||||
|
||||
// Database errors
|
||||
DatabaseErrorCode::CONNECTION_FAILED
|
||||
DatabaseErrorCode::QUERY_FAILED
|
||||
DatabaseErrorCode::TRANSACTION_FAILED
|
||||
DatabaseErrorCode::CONSTRAINT_VIOLATION
|
||||
|
||||
// Authentication errors
|
||||
AuthErrorCode::CREDENTIALS_INVALID
|
||||
AuthErrorCode::TOKEN_EXPIRED
|
||||
AuthErrorCode::SESSION_EXPIRED
|
||||
AuthErrorCode::ACCOUNT_LOCKED
|
||||
|
||||
// HTTP errors
|
||||
HttpErrorCode::BAD_REQUEST
|
||||
HttpErrorCode::NOT_FOUND
|
||||
HttpErrorCode::METHOD_NOT_ALLOWED
|
||||
HttpErrorCode::RATE_LIMIT_EXCEEDED
|
||||
|
||||
// Security errors
|
||||
SecurityErrorCode::CSRF_TOKEN_INVALID
|
||||
SecurityErrorCode::SQL_INJECTION_DETECTED
|
||||
SecurityErrorCode::XSS_DETECTED
|
||||
SecurityErrorCode::PATH_TRAVERSAL_DETECTED
|
||||
|
||||
// Validation errors
|
||||
ValidationErrorCode::INVALID_INPUT
|
||||
ValidationErrorCode::REQUIRED_FIELD_MISSING
|
||||
ValidationErrorCode::BUSINESS_RULE_VIOLATION
|
||||
ValidationErrorCode::INVALID_FORMAT
|
||||
|
||||
// Using error codes in exceptions:
|
||||
throw FrameworkException::create(
|
||||
DatabaseErrorCode::QUERY_FAILED,
|
||||
"Failed to execute user query"
|
||||
)->withContext(
|
||||
ExceptionContext::forOperation('user.find', 'UserRepository')
|
||||
->withData(['query' => 'SELECT * FROM users WHERE id = ?'])
|
||||
->withDebug(['bind_params' => [$userId]])
|
||||
);
|
||||
```
|
||||
|
||||
### Exception Context Building
|
||||
|
||||
```php
|
||||
// Method 1: Using factory methods
|
||||
$exception = FrameworkException::forOperation(
|
||||
'payment.process',
|
||||
'PaymentService',
|
||||
'Payment processing failed',
|
||||
HttpErrorCode::BAD_GATEWAY
|
||||
)->withData([
|
||||
'amount' => $amount->toArray(),
|
||||
'gateway' => 'stripe',
|
||||
'customer_id' => $customerId
|
||||
])->withMetadata([
|
||||
'attempt' => 1,
|
||||
'idempotency_key' => $idempotencyKey
|
||||
]);
|
||||
|
||||
// Method 2: Building context separately
|
||||
$context = ExceptionContext::empty()
|
||||
->withOperation('order.validate', 'OrderService')
|
||||
->withData([
|
||||
'order_id' => $orderId,
|
||||
'total' => $total->toDecimal()
|
||||
])
|
||||
->withDebug([
|
||||
'validation_rules' => ['min_amount', 'max_items'],
|
||||
'failed_rule' => 'min_amount'
|
||||
]);
|
||||
|
||||
throw FrameworkException::fromContext(
|
||||
'Order validation failed',
|
||||
$context,
|
||||
ValidationErrorCode::BUSINESS_RULE_VIOLATION
|
||||
);
|
||||
```
|
||||
|
||||
### Recoverable Exceptions
|
||||
|
||||
```php
|
||||
// Creating recoverable exceptions with retry hints
|
||||
final class RateLimitException extends FrameworkException
|
||||
{
|
||||
public static function exceeded(int $retryAfter): self
|
||||
{
|
||||
return self::create(
|
||||
HttpErrorCode::RATE_LIMIT_EXCEEDED,
|
||||
'API rate limit exceeded'
|
||||
)->withRetryAfter($retryAfter)
|
||||
->withData(['retry_after_seconds' => $retryAfter]);
|
||||
}
|
||||
}
|
||||
|
||||
// Using in code
|
||||
try {
|
||||
$response = $apiClient->request($endpoint);
|
||||
} catch (RateLimitException $e) {
|
||||
if ($e->isRecoverable()) {
|
||||
$waitTime = $e->getRetryAfter();
|
||||
// Schedule retry after $waitTime seconds
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
```
|
||||
|
||||
### Exception Categories
|
||||
|
||||
```php
|
||||
// Check exception category for handling strategies
|
||||
try {
|
||||
$result = $operation->execute();
|
||||
} catch (FrameworkException $e) {
|
||||
if ($e->isCategory('AUTH')) {
|
||||
// Handle authentication errors
|
||||
return $this->redirectToLogin();
|
||||
}
|
||||
|
||||
if ($e->isCategory('VAL')) {
|
||||
// Handle validation errors
|
||||
return $this->validationErrorResponse($e);
|
||||
}
|
||||
|
||||
if ($e->isErrorCode(DatabaseErrorCode::CONNECTION_FAILED)) {
|
||||
// Handle specific database connection errors
|
||||
$this->notifyOps($e);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
```
|
||||
|
||||
### Simple Exceptions for Quick Use
|
||||
|
||||
```php
|
||||
// When you don't need the full context system
|
||||
throw FrameworkException::simple('Quick error message');
|
||||
|
||||
// With previous exception
|
||||
} catch (\PDOException $e) {
|
||||
throw FrameworkException::simple(
|
||||
'Database operation failed',
|
||||
$e,
|
||||
500
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Exception Data Sanitization
|
||||
|
||||
The framework automatically sanitizes sensitive data in exceptions:
|
||||
|
||||
```php
|
||||
// Sensitive keys are automatically redacted
|
||||
$exception->withData([
|
||||
'username' => 'john@example.com',
|
||||
'password' => 'secret123', // Will be logged as '[REDACTED]'
|
||||
'api_key' => 'sk_live_...' // Will be logged as '[REDACTED]'
|
||||
]);
|
||||
```
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Always extend FrameworkException** for custom exceptions
|
||||
2. **Use ErrorCode enum** for categorizable errors
|
||||
3. **Provide rich context** with operation, component, and data
|
||||
4. **Use factory methods** for consistent exception creation
|
||||
5. **Sanitize sensitive data** (automatic for common keys)
|
||||
6. **Make exceptions domain-specific** (UserNotFoundException vs generic NotFoundException)
|
||||
7. **Include recovery hints** for recoverable errors
|
||||
|
||||
## Unified Error Kernel Architecture
|
||||
|
||||
The framework uses a **context-aware error handling system** centered around the `ErrorKernel` class that automatically detects execution context (CLI vs HTTP) and handles errors accordingly.
|
||||
|
||||
### ErrorKernel Overview
|
||||
|
||||
**Location**: `src/Framework/ExceptionHandling/ErrorKernel.php`
|
||||
|
||||
**Key Features**:
|
||||
- Automatic context detection (CLI vs HTTP)
|
||||
- Colored console output for CLI errors
|
||||
- HTTP Response objects for web errors
|
||||
- Integration with OWASP Security Event System
|
||||
- Unified error logging via LogReporter
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\ErrorKernel;
|
||||
|
||||
final readonly class ErrorKernel
|
||||
{
|
||||
public function __construct(
|
||||
private ErrorRendererFactory $rendererFactory = new ErrorRendererFactory,
|
||||
private ?ExecutionContext $executionContext = null,
|
||||
private ?ConsoleOutput $consoleOutput = null
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Context-aware exception handler
|
||||
* - CLI: Colored console output
|
||||
* - HTTP: Logs error (middleware creates response)
|
||||
*/
|
||||
public function handle(Throwable $e, array $context = []): mixed
|
||||
{
|
||||
// Automatic logging
|
||||
$log = new LogReporter();
|
||||
$log->report($e);
|
||||
|
||||
// Context-aware handling
|
||||
$executionContext = $this->executionContext ?? ExecutionContext::detect();
|
||||
|
||||
if ($executionContext->isCli()) {
|
||||
$this->handleCliException($e);
|
||||
return null;
|
||||
}
|
||||
|
||||
// HTTP context - middleware will create response
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create HTTP Response from exception (for middleware recovery)
|
||||
*/
|
||||
public function createHttpResponse(
|
||||
Throwable $exception,
|
||||
?ExceptionContextProvider $contextProvider = null,
|
||||
bool $isDebugMode = false
|
||||
): Response {
|
||||
$renderer = new ResponseErrorRenderer($isDebugMode);
|
||||
return $renderer->createResponse($exception, $contextProvider);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### CLI Error Handling
|
||||
|
||||
**CliErrorHandler** registers global PHP error handlers for CLI context:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\CliErrorHandler;
|
||||
use App\Framework\Console\ConsoleOutput;
|
||||
|
||||
// Registration in AppBootstrapper
|
||||
$output = new ConsoleOutput();
|
||||
$cliErrorHandler = new CliErrorHandler($output);
|
||||
$cliErrorHandler->register();
|
||||
|
||||
// Automatic colored output for errors:
|
||||
// - Red for uncaught exceptions
|
||||
// - Yellow for warnings
|
||||
// - Cyan for notices
|
||||
// - Full stack traces in CLI
|
||||
```
|
||||
|
||||
### HTTP Error Handling
|
||||
|
||||
**ExceptionHandlingMiddleware** catches exceptions in HTTP request pipeline:
|
||||
|
||||
```php
|
||||
use App\Framework\Http\Middlewares\ExceptionHandlingMiddleware;
|
||||
|
||||
#[MiddlewarePriorityAttribute(MiddlewarePriority::ERROR_HANDLING)]
|
||||
final readonly class ExceptionHandlingMiddleware implements HttpMiddleware
|
||||
{
|
||||
public function __invoke(
|
||||
MiddlewareContext $context,
|
||||
Next $next,
|
||||
RequestStateManager $stateManager
|
||||
): MiddlewareContext {
|
||||
try {
|
||||
return $next($context);
|
||||
} catch (\Throwable $e) {
|
||||
// Log exception
|
||||
$this->logger->error('Unhandled exception in HTTP request', [
|
||||
'exception' => get_class($e),
|
||||
'message' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
// Create HTTP Response
|
||||
$errorKernel = new ErrorKernel();
|
||||
$response = $errorKernel->createHttpResponse(
|
||||
$e,
|
||||
null,
|
||||
isDebugMode: false
|
||||
);
|
||||
|
||||
return $context->withResponse($response);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### OWASP Security Event Integration
|
||||
|
||||
Exceptions can trigger OWASP security events for audit logging:
|
||||
|
||||
```php
|
||||
use App\Application\Security\OWASPSecurityEventLogger;
|
||||
use App\Application\Security\OWASPEventIdentifier;
|
||||
|
||||
// Automatic security logging
|
||||
try {
|
||||
$this->authenticateUser($credentials);
|
||||
} catch (AuthenticationException $e) {
|
||||
// ErrorKernel logs exception
|
||||
$this->errorKernel->handle($e);
|
||||
|
||||
// OWASP event for security audit trail
|
||||
$this->eventDispatcher->dispatch(
|
||||
new AuthenticationFailedEvent(
|
||||
OWASPEventIdentifier::AUTHN_LOGIN_FAILURE,
|
||||
$credentials->username,
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
```
|
||||
|
||||
### Legacy ErrorHandling Module Removed
|
||||
|
||||
**IMPORTANT**: The legacy `ErrorHandling` module (`src/Framework/ErrorHandling/`) has been **completely removed** as of the unified exception architecture migration.
|
||||
|
||||
**Migration Path**:
|
||||
- All error handling now uses `ErrorKernel` and `FrameworkException`
|
||||
- CLI errors: `CliErrorHandler` → `ErrorKernel`
|
||||
- HTTP errors: `ExceptionHandlingMiddleware` → `ErrorKernel`
|
||||
- Security events: Direct event dispatch via `EventDispatcher`
|
||||
|
||||
**Old Pattern** (removed):
|
||||
```php
|
||||
// ❌ Legacy - NO LONGER EXISTS
|
||||
use App\Framework\ErrorHandling\ErrorHandler;
|
||||
use App\Framework\ErrorHandling\SecurityEventLogger;
|
||||
|
||||
$errorHandler = new ErrorHandler();
|
||||
$errorHandler->register();
|
||||
```
|
||||
|
||||
**New Pattern** (current):
|
||||
```php
|
||||
// ✅ Unified - ErrorKernel
|
||||
use App\Framework\ExceptionHandling\ErrorKernel;
|
||||
|
||||
$errorKernel = new ErrorKernel();
|
||||
$errorKernel->handle($exception);
|
||||
```
|
||||
|
||||
## Logging Best Practices
|
||||
|
||||
### Automatic Exception Logging
|
||||
|
||||
All exceptions handled by `ErrorKernel` are automatically logged via `LogReporter`:
|
||||
|
||||
```php
|
||||
// Automatic logging happens in ErrorKernel::handle()
|
||||
$log = new LogReporter();
|
||||
$log->report($exception);
|
||||
|
||||
// Logs include:
|
||||
// - Exception class and message
|
||||
// - Stack trace
|
||||
// - File and line number
|
||||
// - Context data
|
||||
```
|
||||
|
||||
### Manual Logging
|
||||
|
||||
```php
|
||||
use App\Framework\Logging\Logger;
|
||||
|
||||
// Log exceptions with context
|
||||
try {
|
||||
$user = $this->userRepository->find($userId);
|
||||
} catch (UserNotFoundException $e) {
|
||||
Logger::error('User lookup failed', [
|
||||
'user_id' => $userId,
|
||||
'exception' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// Log levels
|
||||
Logger::debug('Debugging information');
|
||||
Logger::info('Informational message');
|
||||
Logger::warning('Warning condition');
|
||||
Logger::error('Error condition');
|
||||
Logger::critical('Critical failure');
|
||||
```
|
||||
|
||||
## Debug Strategies
|
||||
|
||||
### Development vs Production
|
||||
|
||||
**Development** (`APP_DEBUG=true`):
|
||||
- Full stack traces displayed
|
||||
- Detailed error messages
|
||||
- Debug data in responses
|
||||
- SQL query logging
|
||||
|
||||
**Production** (`APP_DEBUG=false`):
|
||||
- Generic error messages
|
||||
- Stack traces hidden from users
|
||||
- Errors logged server-side
|
||||
- Security-safe responses
|
||||
|
||||
### Debugging Tools
|
||||
|
||||
```php
|
||||
// Enable debug mode in ErrorKernel
|
||||
$errorKernel = new ErrorKernel(
|
||||
executionContext: ExecutionContext::cli(),
|
||||
consoleOutput: new ConsoleOutput()
|
||||
);
|
||||
|
||||
// HTTP Response with debug mode
|
||||
$response = $errorKernel->createHttpResponse(
|
||||
$exception,
|
||||
$contextProvider,
|
||||
isDebugMode: true // Shows stack trace in response
|
||||
);
|
||||
```
|
||||
|
||||
### Error Context Providers
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Context\ExceptionContextProvider;
|
||||
|
||||
// Attach custom context to exceptions
|
||||
$contextProvider = new ExceptionContextProvider();
|
||||
$contextProvider->attachContext($exception, [
|
||||
'request_id' => $requestId,
|
||||
'user_id' => $userId,
|
||||
'operation' => 'payment.process'
|
||||
]);
|
||||
|
||||
$response = $errorKernel->createHttpResponse(
|
||||
$exception,
|
||||
$contextProvider,
|
||||
isDebugMode: false
|
||||
);
|
||||
```
|
||||
|
||||
## Error Recovery Patterns
|
||||
|
||||
### Graceful Degradation
|
||||
|
||||
```php
|
||||
// Try primary service, fallback to secondary
|
||||
try {
|
||||
return $this->primaryCache->get($key);
|
||||
} catch (CacheException $e) {
|
||||
Logger::warning('Primary cache failed, using fallback', [
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
return $this->fallbackCache->get($key);
|
||||
}
|
||||
```
|
||||
|
||||
### Circuit Breaker Pattern
|
||||
|
||||
```php
|
||||
use App\Framework\Resilience\CircuitBreaker;
|
||||
|
||||
$circuitBreaker = new CircuitBreaker(
|
||||
failureThreshold: 5,
|
||||
timeout: Duration::fromSeconds(60)
|
||||
);
|
||||
|
||||
try {
|
||||
return $circuitBreaker->call(function() {
|
||||
return $this->externalApi->request($endpoint);
|
||||
});
|
||||
} catch (CircuitOpenException $e) {
|
||||
// Circuit is open - use cached response
|
||||
return $this->cachedResponse;
|
||||
}
|
||||
```
|
||||
|
||||
### Retry with Exponential Backoff
|
||||
|
||||
```php
|
||||
use App\Framework\Queue\ValueObjects\RetryStrategy;
|
||||
|
||||
$retryStrategy = new ExponentialBackoffStrategy(
|
||||
maxAttempts: 3,
|
||||
baseDelaySeconds: 60
|
||||
);
|
||||
|
||||
$attempt = 0;
|
||||
while ($attempt < $retryStrategy->getMaxAttempts()) {
|
||||
try {
|
||||
return $this->performOperation();
|
||||
} catch (TransientException $e) {
|
||||
$attempt++;
|
||||
if (!$retryStrategy->shouldRetry($attempt)) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$delay = $retryStrategy->getDelay($attempt);
|
||||
sleep($delay->toSeconds());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Error Scenarios
|
||||
|
||||
### 1. Database Connection Failure
|
||||
|
||||
```php
|
||||
try {
|
||||
$connection = $this->connectionPool->getConnection();
|
||||
} catch (ConnectionException $e) {
|
||||
// Log error
|
||||
$this->errorKernel->handle($e);
|
||||
|
||||
// Return cached data or error response
|
||||
return $this->getCachedData() ?? $this->errorResponse();
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Validation Errors
|
||||
|
||||
```php
|
||||
try {
|
||||
$user = User::create($email, $name);
|
||||
} catch (ValidationException $e) {
|
||||
// Return validation errors to user
|
||||
return new JsonResult([
|
||||
'errors' => $e->getErrors()
|
||||
], status: Status::UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Authentication Failures
|
||||
|
||||
```php
|
||||
try {
|
||||
$user = $this->authenticator->authenticate($credentials);
|
||||
} catch (AuthenticationException $e) {
|
||||
// Log security event
|
||||
$this->eventDispatcher->dispatch(
|
||||
new AuthenticationFailedEvent($credentials->username)
|
||||
);
|
||||
|
||||
// Return 401 Unauthorized
|
||||
return new JsonResult([
|
||||
'error' => 'Invalid credentials'
|
||||
], status: Status::UNAUTHORIZED);
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Resource Not Found
|
||||
|
||||
```php
|
||||
try {
|
||||
$order = $this->orderRepository->find($orderId);
|
||||
} catch (OrderNotFoundException $e) {
|
||||
// Return 404 Not Found
|
||||
return new JsonResult([
|
||||
'error' => 'Order not found'
|
||||
], status: Status::NOT_FOUND);
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Rate Limit Exceeded
|
||||
|
||||
```php
|
||||
try {
|
||||
$this->rateLimiter->checkLimit($userId);
|
||||
} catch (RateLimitException $e) {
|
||||
// Return 429 Too Many Requests with retry hint
|
||||
return new JsonResult([
|
||||
'error' => 'Rate limit exceeded',
|
||||
'retry_after' => $e->getRetryAfter()
|
||||
], status: Status::TOO_MANY_REQUESTS);
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user