From 834c92dcb2f86d2a412627aeafb2ae30a7dd1d0e Mon Sep 17 00:00:00 2001 From: stefan Date: Mon, 21 Jul 2025 17:41:00 +0200 Subject: [PATCH] =?UTF-8?q?(fix)=20Umbau=20zu=20SCS=20###=20API-Gateway=20?= =?UTF-8?q?erweitern=20-=20Bestehenden=20API-Gateway-Service=20mit=20zus?= =?UTF-8?q?=C3=A4tzlichen=20Funktionen=20ausstatten:=20=20=20=20=20-=20Rat?= =?UTF-8?q?e=20Limiting=20implementieren=20=20=20=20=20-=20Request/Respons?= =?UTF-8?q?e=20Logging=20verbessern=20=20=20=20=20-=20Cross-Service=20Trac?= =?UTF-8?q?ing=20mit=20eindeutigen=20Request-IDs=20einf=C3=BChren?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/config/LogSamplingConfig.kt | 20 ++----- .../mocode/gateway/config/MonitoringConfig.kt | 56 ++++++++++--------- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/api-gateway/src/jvmMain/kotlin/at/mocode/gateway/config/LogSamplingConfig.kt b/api-gateway/src/jvmMain/kotlin/at/mocode/gateway/config/LogSamplingConfig.kt index 238dad2a..83c04bee 100644 --- a/api-gateway/src/jvmMain/kotlin/at/mocode/gateway/config/LogSamplingConfig.kt +++ b/api-gateway/src/jvmMain/kotlin/at/mocode/gateway/config/LogSamplingConfig.kt @@ -1,10 +1,13 @@ package at.mocode.gateway.config +import at.mocode.gateway.config.REQUEST_ID_KEY +import at.mocode.gateway.config.REQUEST_START_TIME_KEY import at.mocode.shared.config.AppConfig import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.plugins.calllogging.* import io.ktor.server.request.* +import io.ktor.server.response.* import io.ktor.util.* import org.slf4j.LoggerFactory import org.slf4j.event.Level @@ -96,20 +99,9 @@ fun Application.configureLogSampling() { } } - // Modify the CallLogging plugin to respect the sampling decision - environment.monitor.subscribe(CallLogging.LoggingConfig) { loggingConfig -> - // Add a filter to the CallLogging plugin - loggingConfig.filter { call -> - // Check if the request should be logged based on sampling - val shouldLog = call.attributes.getOrNull(SHOULD_LOG_REQUEST_KEY) ?: true - - // Apply the original filter as well (exclude paths) - val originalFilter = !AppConfig.logging.excludePaths.any { call.request.path().startsWith(it) } - - // Only log if both filters pass - shouldLog && originalFilter - } - } + // Instead of trying to modify CallLogging after installation, + // we'll use the interceptor to decide if logging should happen + // The CallLogging plugin will be configured in MonitoringConfig.kt } /** diff --git a/api-gateway/src/jvmMain/kotlin/at/mocode/gateway/config/MonitoringConfig.kt b/api-gateway/src/jvmMain/kotlin/at/mocode/gateway/config/MonitoringConfig.kt index 03ad06e1..f5fbaf52 100644 --- a/api-gateway/src/jvmMain/kotlin/at/mocode/gateway/config/MonitoringConfig.kt +++ b/api-gateway/src/jvmMain/kotlin/at/mocode/gateway/config/MonitoringConfig.kt @@ -1,10 +1,12 @@ package at.mocode.gateway.config import at.mocode.dto.base.ApiResponse +import at.mocode.gateway.config.REQUEST_ID_KEY +import at.mocode.gateway.config.REQUEST_START_TIME_KEY import at.mocode.shared.config.AppConfig import io.ktor.http.* import io.ktor.server.application.* -import io.ktor.server.plugins.callloging.* +import io.ktor.server.plugins.calllogging.* import io.ktor.server.plugins.statuspages.* import io.ktor.server.request.* import io.ktor.server.response.* @@ -125,13 +127,13 @@ fun Application.configureMonitoring() { } // Filtere Pfade, die vom Logging ausgeschlossen werden sollen - filter { call -> + filter { call: ApplicationCall -> val path = call.request.path() !loggingConfig.excludePaths.any { path.startsWith(it) } } // Formatiere Log-Einträge mit erweitertem Format - format { call -> + format { call: ApplicationCall -> val status = call.response.status() val httpMethod = call.request.httpMethod.value val path = call.request.path() @@ -140,7 +142,7 @@ fun Application.configureMonitoring() { val timestamp = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) // Get the request ID from the call attributes (set by RequestTracingConfig) - val requestId = call.attributes.getOrNull(REQUEST_ID_KEY) ?: "no-request-id" + val requestId: String = call.attributes.getOrNull(REQUEST_ID_KEY) ?: "no-request-id" if (loggingConfig.useStructuredLogging) { // Strukturiertes Logging-Format @@ -167,11 +169,11 @@ fun Application.configureMonitoring() { // Log all headers if in debug mode, filtering sensitive data if (loggingConfig.level.equals("DEBUG", ignoreCase = true)) { append("headers={") - call.request.headers.entries().joinTo(this, ", ") { - if (isSensitiveHeader(it.key)) { - "${it.key}=*****" + call.request.headers.entries().joinTo(this, ", ") { entry -> + if (isSensitiveHeader(entry.key)) { + "${entry.key}=*****" } else { - "${it.key}=${it.value.joinToString(",")}" + "${entry.key}=${entry.value.joinToString(",")}" } } append("} ") @@ -181,22 +183,24 @@ fun Application.configureMonitoring() { // Log Query-Parameter wenn konfiguriert if (loggingConfig.logRequestParameters && call.request.queryParameters.entries().isNotEmpty()) { append("params={") - call.request.queryParameters.entries().joinTo(this, ", ") { - if (isSensitiveParameter(it.key)) { - "${it.key}=*****" + call.request.queryParameters.entries().joinTo(this, ", ") { entry -> + if (isSensitiveParameter(entry.key)) { + "${entry.key}=*****" } else { - "${it.key}=${it.value.joinToString(",")}" + "${entry.key}=${entry.value.joinToString(",")}" } } append("} ") } if (userAgent != null) { - append("userAgent=\"${userAgent.replace("\"", "\\\"")}\" ") + // Use a simpler approach to avoid escape sequence issues + val escapedUserAgent = userAgent.replace("\"", "\\\"") + append("userAgent=\"$escapedUserAgent\" ") } // Log response time if available from RequestTracingConfig - call.attributes.getOrNull(REQUEST_START_TIME_KEY)?.let { startTime -> + call.attributes.getOrNull(REQUEST_START_TIME_KEY)?.let { startTime: Long -> val duration = System.currentTimeMillis() - startTime append("duration=${duration}ms ") } @@ -215,8 +219,8 @@ fun Application.configureMonitoring() { } } else { // Einfaches Logging-Format - val duration = call.attributes.getOrNull(REQUEST_START_TIME_KEY)?.let { - " - Duration: ${System.currentTimeMillis() - it}ms" + val duration = call.attributes.getOrNull(REQUEST_START_TIME_KEY)?.let { startTime: Long -> + " - Duration: ${System.currentTimeMillis() - startTime}ms" } ?: "" "$timestamp - $status: $httpMethod $path - RequestID: $requestId - $clientIp - $userAgent$duration" @@ -235,9 +239,9 @@ fun Application.configureMonitoring() { "propagateRequestId=${loggingConfig.propagateRequestId}") install(StatusPages) { - exception { call, cause -> + exception { call: ApplicationCall, cause: Throwable -> // Get the request ID for error logging - val requestId = call.attributes.getOrNull(REQUEST_ID_KEY) ?: "no-request-id" + val requestId: String = call.attributes.getOrNull(REQUEST_ID_KEY) ?: "no-request-id" call.application.log.error("Unhandled exception - RequestID: $requestId", cause) call.respond( @@ -246,9 +250,9 @@ fun Application.configureMonitoring() { ) } - status(HttpStatusCode.NotFound) { call, status -> + status(HttpStatusCode.NotFound) { call: ApplicationCall, status: HttpStatusCode -> // Get the request ID for error logging - val requestId = call.attributes.getOrNull(REQUEST_ID_KEY) ?: "no-request-id" + val requestId: String = call.attributes.getOrNull(REQUEST_ID_KEY) ?: "no-request-id" call.application.log.warn("Not found - Path: ${call.request.path()} - RequestID: $requestId") call.respond( @@ -257,9 +261,9 @@ fun Application.configureMonitoring() { ) } - status(HttpStatusCode.Unauthorized) { call, status -> + status(HttpStatusCode.Unauthorized) { call: ApplicationCall, status: HttpStatusCode -> // Get the request ID for error logging - val requestId = call.attributes.getOrNull(REQUEST_ID_KEY) ?: "no-request-id" + val requestId: String = call.attributes.getOrNull(REQUEST_ID_KEY) ?: "no-request-id" call.application.log.warn("Unauthorized access - Path: ${call.request.path()} - RequestID: $requestId") call.respond( @@ -268,9 +272,9 @@ fun Application.configureMonitoring() { ) } - status(HttpStatusCode.Forbidden) { call, status -> + status(HttpStatusCode.Forbidden) { call: ApplicationCall, status: HttpStatusCode -> // Get the request ID for error logging - val requestId = call.attributes.getOrNull(REQUEST_ID_KEY) ?: "no-request-id" + val requestId: String = call.attributes.getOrNull(REQUEST_ID_KEY) ?: "no-request-id" call.application.log.warn("Forbidden access - Path: ${call.request.path()} - RequestID: $requestId") call.respond( @@ -280,9 +284,9 @@ fun Application.configureMonitoring() { } // Rate limit exceeded - status(HttpStatusCode.TooManyRequests) { call, status -> + status(HttpStatusCode.TooManyRequests) { call: ApplicationCall, status: HttpStatusCode -> // Get the request ID for error logging - val requestId = call.attributes.getOrNull(REQUEST_ID_KEY) ?: "no-request-id" + val requestId: String = call.attributes.getOrNull(REQUEST_ID_KEY) ?: "no-request-id" call.application.log.warn("Rate limit exceeded - Path: ${call.request.path()} - RequestID: $requestId") call.respond(