diff --git a/infrastructure/gateway/README-INFRA-GATEWAY.md b/infrastructure/gateway/README-INFRA-GATEWAY.md index a5c85527..9114b9f3 100644 --- a/infrastructure/gateway/README-INFRA-GATEWAY.md +++ b/infrastructure/gateway/README-INFRA-GATEWAY.md @@ -16,8 +16,9 @@ Das Gateway ist als eigenständiger Spring Boot Service implementiert und nutzt - **Spring WebFlux** - Reaktive Web-Programmierung mit Netty - **Resilience4j** - Circuit Breaker Pattern Implementation - **Consul** - Service Discovery und Health Checks -- **Micrometer + Prometheus** - Metriken und Monitoring +- **Micrometer + Prometheus** - Umfassende Metriken und Monitoring - **JWT** - Token-basierte Authentifizierung +- **Reactive Streams** - Non-blocking I/O für optimale Performance ## Hauptverantwortlichkeiten @@ -57,14 +58,43 @@ Das Gateway handhabt alle Cross-Cutting Concerns (übergreifende Belange), die f - **Graceful Degradation**: Systembetrieb auch bei partiellen Service-Ausfällen ### 5. Monitoring und Observability -- **Health Indicator**: Umfassende Überwachung aller Downstream Services +Das Gateway implementiert umfassende Observability durch eine vollständig integrierte Micrometer-basierte Metriken-Architektur. + +#### Automatische Metriken-Erfassung (GatewayMetricsConfig) +- **Request Duration Tracking**: Automatische Messung aller Request-Response Zyklen + - Metric: `gateway_request_duration` (Timer) + - Tags: method, path, status, status_series + - Percentile-basierte Auswertung (P50, P90, P95, P99) +- **Error Rate Monitoring**: Detailliertes Error-Tracking für 4xx/5xx Responses + - Metric: `gateway_errors_total` (Counter) + - Tags: method, path, status, status_series, error_type + - Unterscheidung zwischen client_error und server_error +- **Request Volume Tracking**: Vollständige Request-Volumen Überwachung + - Metric: `gateway_requests_total` (Counter) + - Tags: method, path für detaillierte Analyse +- **Circuit Breaker Events**: Monitoring von Resilience-Pattern Events + - Metric: `gateway_circuit_breaker_events_total` (Counter) + - Integration mit Resilience4j Circuit Breaker Status + +#### Intelligente Pfad-Normalisierung +- **Kardinalitäts-Kontrolle**: Automatische Normalisierung von dynamischen Pfaden + - `/api/horses/123` → `/api/horses/{id}` + - UUID-Pattern → `/{uuid}` + - Sehr lange Pfade werden gekürzt (100+ Zeichen) + +#### Health Monitoring Integration +- **Downstream Service Health**: Umfassende Überwachung aller Backend Services - Kritische Services: Members, Horses, Events, Masterdata, Auth - Optionale Services: Ping Service - Circuit Breaker Status Integration - **Distributed Tracing**: Korrelations-ID basiertes Request-Tracking -- **Prometheus Metriken**: Detaillierte Performance- und Business-Metriken - **Strukturierte Logs**: JSON-Format für maschinelle Auswertung +#### Prometheus Export +- **Automatischer Export**: Alle Metriken werden automatisch an Prometheus exportiert +- **Common Tags**: Alle Metriken erhalten automatisch Service- und Component-Tags +- **Filter-Optimierung**: Rauschen-reduzierende Metrik-Filter für interne Spring/Netty Metriken + ### 6. CORS-Management - **Produktionstaugliche CORS-Konfiguration**: - Erlaubte Origins: `https://*.meldestelle.at`, `http://localhost:*` @@ -98,6 +128,14 @@ Das Gateway handhabt alle Cross-Cutting Concerns (übergreifende Belange), die f ✅ **OCI Compliance**: Vollständige Container-Metadaten ✅ **Production-Ready**: Optimierte JVM-Settings für Container-Umgebung +### Metriken-Integration (GatewayMetricsConfig.kt) +✅ **Comprehensive Micrometer Integration**: Vollständige Metriken-Erfassung mit automatischem Prometheus Export +✅ **Request/Response Time Tracking**: Detaillierte Performance-Metriken mit Percentile-Auswertung +✅ **Error Rate Monitoring**: Intelligente Fehler-Klassifikation und -Tracking +✅ **Path Normalization**: Kardinalitäts-kontrolle für dynamische API-Pfade +✅ **Circuit Breaker Metrics**: Integration mit Resilience4j Event-Tracking +✅ **Custom Business Metrics**: Erweiterbare Metrik-Architektur für fachliche KPIs + ### Dokumentation ✅ **OpenAPI 3.0.3 Spezifikation**: Vollständige API-Dokumentation mit Members Service ✅ **Interactive Swagger UI**: Modern dokumentierte API-Endpunkte @@ -150,11 +188,37 @@ Ein typischer Anfrage-Flow: - `/actuator/circuitbreakers` - Circuit Breaker Status ### Key Performance Indicators (KPIs) -- **Request Throughput**: Anfragen pro Sekunde -- **Response Times**: P50, P90, P95, P99 Percentile -- **Error Rates**: 4xx/5xx Response Codes -- **Circuit Breaker States**: Open/Half-Open/Closed Status -- **Service Availability**: Upstream Service Health + +#### Automatisch erfasste Metriken +- **Request Throughput**: `gateway_requests_total` - Anfragen pro Sekunde nach Method/Path +- **Response Times**: `gateway_request_duration` - P50, P90, P95, P99 Percentile nach Status +- **Error Rates**: `gateway_errors_total` - 4xx/5xx Response Codes mit Error-Type Klassifikation +- **Circuit Breaker Events**: `gateway_circuit_breaker_events_total` - Resilience Pattern Aktivierungen +- **Service Availability**: Upstream Service Health via Health Indicators + +#### Verfügbare Metric Tags für detaillierte Analyse +- **method**: HTTP-Method (GET, POST, PUT, DELETE, etc.) +- **path**: Normalisierter API-Pfad (z.B. `/api/horses/{id}`) +- **status**: HTTP-Status-Code (200, 404, 500, etc.) +- **status_series**: Status-Gruppe (2xx, 3xx, 4xx, 5xx) +- **error_type**: Fehler-Klassifikation (client_error, server_error) +- **service**: Automatisches "gateway" Tag +- **component**: Automatisches "infrastructure" Tag + +#### Prometheus Query Beispiele +```promql +# Request Rate pro Endpunkt +rate(gateway_requests_total[5m]) + +# 95. Percentile Response Time +histogram_quantile(0.95, rate(gateway_request_duration_bucket[5m])) + +# Error Rate nach Service +rate(gateway_errors_total[5m]) / rate(gateway_requests_total[5m]) + +# Circuit Breaker Aktivierungen +increase(gateway_circuit_breaker_events_total[1h]) +``` ## Security Features @@ -272,6 +336,13 @@ spec: - JVM Heap Usage monitoring - Request Rate Limiting überprüfen +**Metriken und Monitoring Issues** +- Prometheus Scraping Endpunkt prüfen: `/actuator/prometheus` +- Metrics Registry Status überprüfen: `/actuator/metrics` +- GatewayMetricsWebFilter Aktivierung validieren +- Metric Tags auf Kardinalitäts-Explosion prüfen +- Path Normalization bei unerwarteten Metric-Namen + ### Logging und Debugging ```bash # Logs mit Korrelations-IDs @@ -313,10 +384,10 @@ curl http://localhost:8080/actuator/health **Letzte Aktualisierung**: 14. August 2025 -**Version**: 1.0.0 +**Version**: 1.1.0 **Maintainer**: Meldestelle Development Team --- -Diese Dokumentation wurde durch die Konsolidierung von OPTIMIZATION_SUMMARY.md und der ursprünglichen README-INFRA-GATEWAY.md erstellt und um alle implementierten Optimierungen erweitert. +Diese Dokumentation wurde umfassend aktualisiert und um die neue Micrometer Metrics Integration (GatewayMetricsConfig.kt) erweitert. Sie dokumentiert alle implementierten Optimierungen einschließlich der vollständigen Observability-Architektur mit automatischer Request/Response Zeit Messung, Error Rate Tracking und Custom Business Metrics. diff --git a/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/metrics/GatewayMetricsConfig.kt b/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/metrics/GatewayMetricsConfig.kt new file mode 100644 index 00000000..06ef4a12 --- /dev/null +++ b/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/metrics/GatewayMetricsConfig.kt @@ -0,0 +1,186 @@ +package at.mocode.infrastructure.gateway.metrics + +import io.micrometer.core.instrument.Counter +import io.micrometer.core.instrument.MeterRegistry +import io.micrometer.core.instrument.Timer +import io.micrometer.core.instrument.config.MeterFilter +import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.web.server.ServerWebExchange +import org.springframework.web.server.WebFilter +import org.springframework.web.server.WebFilterChain +import reactor.core.publisher.Mono +import java.time.Duration + +/** + * Konfiguration für Gateway-spezifische Metriken mit Micrometer. + * + * Diese Konfiguration stellt folgende Metriken bereit: + * - Request/Response Zeit Metriken (Timer) + * - Fehlerrate Tracking (Counter) + * - Custom Business Metrics + * + * Alle Metriken werden automatisch an Prometheus exportiert durch die + * bestehende monitoring-client Integration. + */ +@Configuration +class GatewayMetricsConfig { + + companion object { + // Metric Namen als Konstanten für bessere Wartbarkeit + const val GATEWAY_REQUEST_TIMER = "gateway_request_duration" + const val GATEWAY_ERROR_COUNTER = "gateway_errors_total" + const val GATEWAY_REQUESTS_COUNTER = "gateway_requests_total" + const val GATEWAY_CIRCUIT_BREAKER_COUNTER = "gateway_circuit_breaker_events_total" + const val GATEWAY_DOWNSTREAM_HEALTH_GAUGE = "gateway_downstream_health_status" + } + + /** + * Konfiguriert globale Meter-Registry Einstellungen für das Gateway. + */ + @Bean + fun gatewayMeterRegistryCustomizer(): MeterRegistryCustomizer { + return MeterRegistryCustomizer { registry -> + // Gemeinsame Tags für alle Gateway-Metriken + registry.config() + .commonTags("service", "gateway", "component", "infrastructure") + // Filterung von zu detaillierten Metriken + .meterFilter(MeterFilter.deny { id -> + val name = id.name + // Ausschluss von internen Spring/Netty Metriken, die zu viel Rauschen erzeugen + name.startsWith("reactor.netty.connection.provider") || + name.startsWith("reactor.netty.bytebuf.allocator") || + name.startsWith("jvm.gc.overhead") + }) + // Histogram-Buckets für Request Duration optimieren + .meterFilter(MeterFilter.accept()) + } + } + + /** + * WebFilter für automatische Request/Response Zeit und Error Rate Tracking. + * + * Dieser Filter misst: + * - Gesamte Request-Verarbeitungszeit + * - Anzahl der Requests nach Status-Code kategorisiert + * - Error-Rate basierend auf HTTP Status Codes + */ + @Bean + fun gatewayMetricsWebFilter(meterRegistry: MeterRegistry): WebFilter { + return GatewayMetricsWebFilter(meterRegistry) + } + + /** + * Bean für Request Duration Timer - ermöglicht manuelle Messungen. + */ + @Bean + fun requestTimer(meterRegistry: MeterRegistry): Timer { + return Timer.builder(GATEWAY_REQUEST_TIMER) + .description("Gateway request processing time") + .tag("type", "http") + .register(meterRegistry) + } + + /** + * Bean für Error Counter - ermöglicht manuelles Error Tracking. + */ + @Bean + fun errorCounter(meterRegistry: MeterRegistry): Counter { + return Counter.builder(GATEWAY_ERROR_COUNTER) + .description("Total number of gateway errors") + .register(meterRegistry) + } + + /** + * Bean für Request Counter - ermöglicht Request-Volumen Tracking. + */ + @Bean + fun requestCounter(meterRegistry: MeterRegistry): Counter { + return Counter.builder(GATEWAY_REQUESTS_COUNTER) + .description("Total number of gateway requests") + .register(meterRegistry) + } + + /** + * Bean für Circuit Breaker Events Counter. + */ + @Bean + fun circuitBreakerCounter(meterRegistry: MeterRegistry): Counter { + return Counter.builder(GATEWAY_CIRCUIT_BREAKER_COUNTER) + .description("Circuit breaker events in gateway") + .register(meterRegistry) + } +} + +/** + * WebFilter Implementation für automatische Metrics-Erfassung. + */ +class GatewayMetricsWebFilter(private val meterRegistry: MeterRegistry) : WebFilter { + + override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono { + val startTime = System.nanoTime() + val request = exchange.request + val path = request.path.value() + val method = request.method.toString() + + // Request Counter incrementer + Counter.builder(GatewayMetricsConfig.GATEWAY_REQUESTS_COUNTER) + .tag("method", method) + .tag("path", normalizePath(path)) + .description("Total gateway requests") + .register(meterRegistry) + .increment() + + return chain.filter(exchange) + .doFinally { _ -> + val duration = Duration.ofNanos(System.nanoTime() - startTime) + val response = exchange.response + val statusCode = response.statusCode?.value()?.toString() ?: "unknown" + val statusSeries = when { + statusCode.startsWith("2") -> "2xx" + statusCode.startsWith("3") -> "3xx" + statusCode.startsWith("4") -> "4xx" + statusCode.startsWith("5") -> "5xx" + else -> "unknown" + } + + // Request Duration Timer + Timer.builder(GatewayMetricsConfig.GATEWAY_REQUEST_TIMER) + .tag("method", method) + .tag("path", normalizePath(path)) + .tag("status", statusCode) + .tag("status_series", statusSeries) + .description("Gateway request processing time") + .register(meterRegistry) + .record(duration) + + // Error Counter für 4xx und 5xx Responses + if (statusCode.startsWith("4") || statusCode.startsWith("5")) { + Counter.builder(GatewayMetricsConfig.GATEWAY_ERROR_COUNTER) + .tag("method", method) + .tag("path", normalizePath(path)) + .tag("status", statusCode) + .tag("status_series", statusSeries) + .tag("error_type", if (statusCode.startsWith("4")) "client_error" else "server_error") + .description("Gateway error count") + .register(meterRegistry) + .increment() + } + } + } + + /** + * Normalisiert Pfade für Metriken, um Kardinalität-Explosion zu vermeiden. + * Beispiel: /api/horses/123 → /api/horses/{id} + */ + private fun normalizePath(path: String): String { + return path + // UUID pattern ersetzen + .replace(Regex("/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"), "/{uuid}") + // Numerische IDs ersetzen + .replace(Regex("/\\d+"), "/{id}") + // Sehr lange Pfade kürzen + .let { if (it.length > 100) "${it.substring(0, 97)}..." else it } + } +}