Files
michaelschiemer/docs/features/error-handling/advanced.md
Michael Schiemer 36ef2a1e2c
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
fix: Gitea Traefik routing and connection pool optimization
- 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
2025-11-09 14:46:15 +01:00

22 KiB

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:

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:

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:

// 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:

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:

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:

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:

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:

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:

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:

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:

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

// 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

// 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

// 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

// 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

// 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

// 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:

    // In DI Container
    $container->singleton(ExceptionRateLimiter::class, ...);
    
  2. Context Caching aktivieren (optional):

    // Nur wenn Performance-Probleme auftreten
    $container->singleton(ExceptionContextCache::class, ...);
    
  3. User-Friendly Messages hinzufügen:

    // Templates in config/exception_messages.php definieren
    
  4. Health Checks registrieren:

    // 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

$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

$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

// Performance-Tracking manuell starten
$startData = $performanceTracker->start();
// ... exception occurs ...
$metrics = $performanceTracker->end($startData, $exception, $context);

Weitere Ressourcen