diff --git a/Makefile b/Makefile index c2e6dbe4..45f8481f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION=1.23.0 +VERSION=1.23.1 default: versioncheck diff --git a/README.md b/README.md index d521df31..603b7dc8 100644 --- a/README.md +++ b/README.md @@ -68,13 +68,13 @@ agent { { name: "App2 metrics" path: app2_metrics - labels: "{\"key3\": \"value3\", \"key4\": \"value4\"}" + labels: "{\"key3\": \"value3\", \"key4\": 4}" url: "http://app2.local:9100/metrics" }, { name: "App3 metrics" path: app3_metrics - labels: "{\"key5\": \"value5\", \"key6\": \"value6\"}" + labels: "{\"key5\": \"value5\", \"key6\": 6}" url: "http://app3.local:9100/metrics" } ] @@ -116,8 +116,8 @@ scrape_configs: The docker images are available via: ```bash -docker pull pambrose/prometheus-proxy:1.23.0 -docker pull pambrose/prometheus-agent:1.23.0 +docker pull pambrose/prometheus-proxy:1.23.1 +docker pull pambrose/prometheus-agent:1.23.1 ``` Start a proxy container with: @@ -126,7 +126,7 @@ Start a proxy container with: docker run --rm -p 8082:8082 -p 8092:8092 -p 50051:50051 -p 8080:8080 \ --env ADMIN_ENABLED=true \ --env METRICS_ENABLED=true \ - pambrose/prometheus-proxy:1.23.0 + pambrose/prometheus-proxy:1.23.1 ``` Start an agent container with: @@ -134,7 +134,7 @@ Start an agent container with: ```bash docker run --rm -p 8083:8083 -p 8093:8093 \ --env AGENT_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \ - pambrose/prometheus-agent:1.23.0 + pambrose/prometheus-agent:1.23.1 ``` Using the config @@ -152,7 +152,7 @@ is in your current directory, run an agent container with: docker run --rm -p 8083:8083 -p 8093:8093 \ --mount type=bind,source="$(pwd)"/prom-agent.conf,target=/app/prom-agent.conf \ --env AGENT_CONFIG=prom-agent.conf \ - pambrose/prometheus-agent:1.23.0 + pambrose/prometheus-agent:1.23.1 ``` **Note:** The `WORKDIR` of the proxy and agent images is `/app`, so make sure to use `/app` as the base directory in the @@ -303,7 +303,7 @@ docker run --rm -p 8082:8082 -p 8092:8092 -p 50440:50440 -p 8080:8080 \ --env PROXY_CONFIG=tls-no-mutual-auth.conf \ --env ADMIN_ENABLED=true \ --env METRICS_ENABLED=true \ - pambrose/prometheus-proxy:1.23.0 + pambrose/prometheus-proxy:1.23.1 docker run --rm -p 8083:8083 -p 8093:8093 \ --mount type=bind,source="$(pwd)"/testing/certs,target=/app/testing/certs \ @@ -311,7 +311,7 @@ docker run --rm -p 8083:8083 -p 8093:8093 \ --env AGENT_CONFIG=tls-no-mutual-auth.conf \ --env PROXY_HOSTNAME=mymachine.lan:50440 \ --name docker-agent \ - pambrose/prometheus-agent:1.23.0 + pambrose/prometheus-agent:1.23.1 ``` **Note:** The `WORKDIR` of the proxy and agent images is `/app`, so make sure to use `/app` as the base directory in the diff --git a/bin/docker-agent.sh b/bin/docker-agent.sh index 2c7a8838..9775af5b 100755 --- a/bin/docker-agent.sh +++ b/bin/docker-agent.sh @@ -3,4 +3,4 @@ docker run --rm -p 8083:8083 -p 8093:8093 \ --env AGENT_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \ --env PROXY_HOSTNAME=mymachine.lan \ - pambrose/prometheus-agent:1.23.0 + pambrose/prometheus-agent:1.23.1 diff --git a/bin/docker-proxy.sh b/bin/docker-proxy.sh index 8eda06fe..46df422b 100755 --- a/bin/docker-proxy.sh +++ b/bin/docker-proxy.sh @@ -2,4 +2,4 @@ docker run --rm -p 8082:8082 -p 8092:8092 -p 50051:50051 -p 8080:8080 \ --env PROXY_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \ - pambrose/prometheus-proxy:1.23.0 + pambrose/prometheus-proxy:1.23.1 diff --git a/build.gradle.kts b/build.gradle.kts index bfe60d71..68b65cb7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,13 +30,13 @@ plugins { } group = "io.prometheus" -version = "1.23.0" +version = "1.23.1" buildConfig { packageName("io.prometheus") buildConfigField("String", "APP_NAME", "\"${project.name}\"") buildConfigField("String", "APP_VERSION", "\"${project.version}\"") - buildConfigField("String", "APP_RELEASE_DATE", "\"11/29/2024\"") + buildConfigField("String", "APP_RELEASE_DATE", "\"12/09/2024\"") buildConfigField("long", "BUILD_TIME", "${System.currentTimeMillis()}L") } diff --git a/etc/compose/proxy.yml b/etc/compose/proxy.yml index fac907ee..56ad6dd9 100644 --- a/etc/compose/proxy.yml +++ b/etc/compose/proxy.yml @@ -1,6 +1,6 @@ prometheus-proxy: autoredeploy: true - image: 'pambrose/prometheus-proxy:1.23.0' + image: 'pambrose/prometheus-proxy:1.23.1' ports: - '8080:8080' - '8082:8082' diff --git a/examples/myapps.conf b/examples/myapps.conf index eca0fb96..82d776ab 100644 --- a/examples/myapps.conf +++ b/examples/myapps.conf @@ -9,13 +9,13 @@ agent { { name: "App2 metrics" path: app2_metrics - labels: "{\"key3\": \"value3\", \"key4\": \"value4\"}" + labels: "{\"key3\": \"value3\", \"key4\": 4}" url: "http://app2.local:9100/metrics" }, { name: "App3 metrics" path: app3_metrics - labels: "{\"key5\": \"value5\", \"key6\": \"value6\"}" + labels: "{\"key5\": \"value5\", \"key6\": 6}" url: "http://app3.local:9100/metrics" } ] diff --git a/examples/simple.conf b/examples/simple.conf index dd8d4167..95fd8f70 100644 --- a/examples/simple.conf +++ b/examples/simple.conf @@ -17,14 +17,14 @@ agent { { name: "Proxy metrics" path: proxy_metrics - labels: "{\"key1\": \"value1\", \"key2\": \"value2\"}" + labels: "{\"key1\": \"value1\", \"key2\": 2}" url: "http://localhost:8082/metrics" //url: "http://"${?HOSTNAME}":8082/metrics" } { name: "Agent metrics" path: agent_metrics - labels: "{\"key3\": \"value3\", \"key4\": \"value4\"}" + labels: "{\"key3\": \"value3\", \"key4\": 4}" url: "http://localhost:8083/metrics" //url: "http://"${?HOSTNAME}":8083/metrics" } diff --git a/gradle.properties b/gradle.properties index 199b1809..e8ccb772 100644 --- a/gradle.properties +++ b/gradle.properties @@ -28,9 +28,9 @@ jettyVersion=10.0.24 junitVersion=5.11.3 kluentVersion=1.73 kotlinVersion=2.1.0 -ktorVersion=3.0.1 +ktorVersion=3.0.2 logbackVersion=1.5.12 -loggingVersion=7.0.0 +loggingVersion=7.0.3 # Keep in sync with grpc tcnativeVersion=2.0.69.Final prometheusVersion=0.16.0 @@ -41,5 +41,5 @@ protocVersion=3.25.4 serializationVersion=1.7.3 slf4jVersion=2.0.13 typesafeVersion=1.4.3 -utilsVersion=2.3.1 +utilsVersion=2.3.4 zipkinVersion=6.0.3 diff --git a/src/main/kotlin/io/prometheus/Proxy.kt b/src/main/kotlin/io/prometheus/Proxy.kt index b3f5e1ab..7b86a0c2 100644 --- a/src/main/kotlin/io/prometheus/Proxy.kt +++ b/src/main/kotlin/io/prometheus/Proxy.kt @@ -28,6 +28,7 @@ import com.github.pambrose.common.time.format import com.github.pambrose.common.util.MetricsUtils.newMapHealthCheck import com.github.pambrose.common.util.getBanner import com.github.pambrose.common.util.isNotNull +import com.github.pambrose.common.util.simpleClassName import com.google.common.base.Joiner import com.google.common.collect.EvictingQueue import io.github.oshai.kotlinlogging.KotlinLogging @@ -127,7 +128,7 @@ class Proxy( listOf( toPlainText(), pathManager.toPlainText(), - if (recentReqs.size > 0) "\n${recentReqs.size} most recent requests:" else "", + if (recentReqs.isNotEmpty()) "\n${recentReqs.size} most recent requests:" else "", recentReqs.reversed().joinToString("\n"), ).joinToString("\n") }, @@ -255,8 +256,12 @@ class Proxy( put("hostName", JsonPrimitive(agentContexts.joinToString { it.hostName })) val labels = agentContextInfo.labels - val json = labels.toJsonElement() - json.jsonObject.forEach { (k, v) -> put(k, v) } + runCatching { + val json = labels.toJsonElement() + json.jsonObject.forEach { (k, v) -> put(k, v) } + }.onFailure { e -> + logger.warn { "Invalid JSON in labels value: $labels - ${e.simpleClassName}: ${e.message}" } + } } else { logger.warn { "No agent context info for path: $path" } } diff --git a/src/main/kotlin/io/prometheus/agent/AgentGrpcService.kt b/src/main/kotlin/io/prometheus/agent/AgentGrpcService.kt index 04edebf2..2bc7dd50 100644 --- a/src/main/kotlin/io/prometheus/agent/AgentGrpcService.kt +++ b/src/main/kotlin/io/prometheus/agent/AgentGrpcService.kt @@ -228,7 +228,7 @@ internal class AgentGrpcService( it.labels = labelsJson } .build() - return stub.registerPath(request)//.toDataClass() + return stub.registerPath(request) .apply { agent.markMsgSent() if (!valid) @@ -246,7 +246,7 @@ internal class AgentGrpcService( it.agentId = agent.agentId it.path = path }.build() - return stub.unregisterPath(request)//.toDataClass() + return stub.unregisterPath(request) .apply { agent.markMsgSent() if (!valid) @@ -260,7 +260,7 @@ internal class AgentGrpcService( if (agentId.isNotEmpty()) runCatching { val request = HeartBeatRequest.newBuilder().also { it.agentId = agentId }.build() - stub.sendHeartBeat(request)//.toDataClass() + stub.sendHeartBeat(request) .apply { agent.markMsgSent() if (!valid) { @@ -294,9 +294,8 @@ internal class AgentGrpcService( .readRequestsFromProxy(agentInfo) .collect { grpcRequest: ScrapeRequest -> // The actual fetch happens at the other end of the channel, not here. - val request = grpcRequest//.toDataClass() - logger.debug { "readRequestsFromProxy():\n$request" } - connectionContext.scrapeRequestsChannel.send { agentHttpService.fetchScrapeUrl(request) } + logger.debug { "readRequestsFromProxy():\n$grpcRequest" } + connectionContext.scrapeRequestsChannel.send { agentHttpService.fetchScrapeUrl(grpcRequest) } agent.scrapeRequestBacklogSize.incrementAndGet() } } @@ -313,7 +312,7 @@ internal class AgentGrpcService( if (!scrapeResults.zipped) { logger.debug { "Writing non-chunked msg scrapeId: $scrapeId length: ${scrapeResults.contentAsText.length}" } - nonChunkedChannel.send(scrapeResults.toScrapeResponse()/*.toProto()*/) + nonChunkedChannel.send(scrapeResults.toScrapeResponse()) agent.metrics { scrapeResultCount.labels(agent.launchId, "non-gzipped").inc() } } else { val zipped = scrapeResults.contentAsZipped @@ -323,7 +322,7 @@ internal class AgentGrpcService( if (zipped.size < chunkContentSize) { logger.debug { "Writing zipped non-chunked msg scrapeId: $scrapeId length: ${zipped.size}" } - nonChunkedChannel.send(scrapeResults.toScrapeResponse() /*.toProto()*/) + nonChunkedChannel.send(scrapeResults.toScrapeResponse()) agent.metrics { scrapeResultCount.labels(agent.launchId, "gzipped").inc() } } else { scrapeResults.toScrapeResponseHeader() @@ -347,14 +346,14 @@ internal class AgentGrpcService( newScrapeResponseChunk(scrapeId, totalChunkCount, readByteCount, checksum, buffer) .also { logger.debug { "Writing chunk $totalChunkCount for scrapeId: $scrapeId" } - chunkedChannel.send(it/*.toProto()*/) + chunkedChannel.send(it) } } newScrapeResponseSummary(scrapeId, totalChunkCount, totalByteCount, checksum) .also { logger.debug { "Writing summary totalChunkCount: $totalChunkCount for scrapeID: $scrapeId" } - chunkedChannel.send(it /*.toProto()*/) + chunkedChannel.send(it) agent.metrics { scrapeResultCount.labels(agent.launchId, "chunked").inc() } } } @@ -376,9 +375,7 @@ internal class AgentGrpcService( CoroutineExceptionHandler { _, e -> if (agent.isRunning) Status.fromThrowable(e) - .apply { - logger.error { "Error in writeResponsesToProxyUntilDisconnected(): $code $description" } - } + .apply { logger.error { "Error in writeResponsesToProxyUntilDisconnected(): $code $description" } } } coroutineScope { diff --git a/src/main/kotlin/io/prometheus/agent/AgentHttpService.kt b/src/main/kotlin/io/prometheus/agent/AgentHttpService.kt index ee3c1170..61fa54e7 100644 --- a/src/main/kotlin/io/prometheus/agent/AgentHttpService.kt +++ b/src/main/kotlin/io/prometheus/agent/AgentHttpService.kt @@ -71,8 +71,8 @@ internal class AgentHttpService( val url = pathContext.url + decodeParams(scrapeRequest.encodedQueryParams) logger.debug { "Fetching $pathContext ${if (url.isNotBlank()) "URL: $url" else ""}" } + // Content is fetched here try { - // Content is fetched here fetchContent(url, scrapeRequest, scrapeResults) } finally { requestTimer?.observeDuration() @@ -132,6 +132,8 @@ internal class AgentHttpService( with(scrapeResults) { if (response.status.isSuccess()) { contentType = response.headers[CONTENT_TYPE].orEmpty() + if (agent.options.debugEnabled) + logger.info { "CT check - setScrapeDetailsAndDebugInfo() contentType: $contentType" } // Zip the content here val content = response.bodyAsText() zipped = content.length > agent.configVals.agent.minGzipSizeBytes diff --git a/src/main/kotlin/io/prometheus/agent/AgentPathManager.kt b/src/main/kotlin/io/prometheus/agent/AgentPathManager.kt index 5bbd761a..644ec3c3 100644 --- a/src/main/kotlin/io/prometheus/agent/AgentPathManager.kt +++ b/src/main/kotlin/io/prometheus/agent/AgentPathManager.kt @@ -75,7 +75,7 @@ internal class AgentPathManager( val labelsJson = labels.defaultEmptyJsonObject() val pathId = agent.grpcService.registerPathOnProxy(path, labelsJson).pathId if (!agent.isTestMode) - logger.info { "Registered $url as /$path with labels: $labelsJson" } + logger.info { "Registered $url as /$path with labels $labelsJson" } pathContextMap[path] = PathContext(pathId, path, url, labelsJson) } diff --git a/src/main/kotlin/io/prometheus/common/GrpcObjects.kt b/src/main/kotlin/io/prometheus/common/GrpcObjects.kt index fe3fee4c..0acde627 100644 --- a/src/main/kotlin/io/prometheus/common/GrpcObjects.kt +++ b/src/main/kotlin/io/prometheus/common/GrpcObjects.kt @@ -64,7 +64,7 @@ internal object GrpcObjects { } .build() } - .build() + .build()!! fun newScrapeResponseSummary( scrapeId: Long, @@ -86,5 +86,5 @@ internal object GrpcObjects { } .build() } - .build() + .build()!! } diff --git a/src/main/kotlin/io/prometheus/common/ScrapeResults.kt b/src/main/kotlin/io/prometheus/common/ScrapeResults.kt index 5e480035..db645ec8 100644 --- a/src/main/kotlin/io/prometheus/common/ScrapeResults.kt +++ b/src/main/kotlin/io/prometheus/common/ScrapeResults.kt @@ -74,7 +74,7 @@ internal class ScrapeResults( it.failureReason = failureReason it.url = url } - .build() + .build()!! fun toScrapeResponseHeader() = ChunkedScrapeResponse @@ -92,7 +92,7 @@ internal class ScrapeResults( it.headerContentType = contentType } .build() - }.build() + }.build()!! companion object { private val logger = KotlinLogging.logger {} diff --git a/src/main/kotlin/io/prometheus/proxy/ProxyHttpConfig.kt b/src/main/kotlin/io/prometheus/proxy/ProxyHttpConfig.kt index dcc055e3..0762d3b5 100644 --- a/src/main/kotlin/io/prometheus/proxy/ProxyHttpConfig.kt +++ b/src/main/kotlin/io/prometheus/proxy/ProxyHttpConfig.kt @@ -18,7 +18,7 @@ package io.prometheus.proxy import com.github.pambrose.common.util.simpleClassName import io.github.oshai.kotlinlogging.KotlinLogging -import io.ktor.http.ContentType.Text.Plain +import io.ktor.http.ContentType.Text import io.ktor.http.HttpHeaders import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode.Companion.NotFound @@ -105,7 +105,7 @@ internal object ProxyHttpConfig { } status(NotFound) { call, cause -> - call.respond(TextContent("${cause.value} ${cause.description}", Plain.withCharset(Charsets.UTF_8), cause)) + call.respond(TextContent("${cause.value} ${cause.description}", Text.Plain.withCharset(Charsets.UTF_8), cause)) } } } diff --git a/src/main/kotlin/io/prometheus/proxy/ProxyHttpRoutes.kt b/src/main/kotlin/io/prometheus/proxy/ProxyHttpRoutes.kt index a06c9dfd..bdde9df8 100644 --- a/src/main/kotlin/io/prometheus/proxy/ProxyHttpRoutes.kt +++ b/src/main/kotlin/io/prometheus/proxy/ProxyHttpRoutes.kt @@ -17,13 +17,16 @@ package io.prometheus.proxy import com.github.pambrose.common.util.isNull +import com.github.pambrose.common.util.simpleClassName import com.github.pambrose.common.util.unzip import io.github.oshai.kotlinlogging.KotlinLogging import io.ktor.http.ContentType +import io.ktor.http.ContentType.Text import io.ktor.http.HttpHeaders import io.ktor.http.HttpStatusCode import io.ktor.http.formUrlEncode import io.ktor.http.isSuccess +import io.ktor.http.withCharset import io.ktor.server.application.Application import io.ktor.server.request.ApplicationRequest import io.ktor.server.request.header @@ -76,7 +79,7 @@ object ProxyHttpRoutes { get(proxy.options.sdPath) { val json = proxy.buildServiceDiscoveryJson() val prettyPrint = format.encodeToString(json) - call.respondWith(prettyPrint, ContentType.Application.Json) + call.respondWith(prettyPrint, ContentType.Application.Json.withCharset(Charsets.UTF_8)) } } else { logger.info { "Not adding /${proxy.options.sdPath} service discovery endpoint" } @@ -100,11 +103,13 @@ object ProxyHttpRoutes { path.isBlank() -> emptyPathResponse(proxy, logger, responseResults) path == FAVICON_FILENAME -> invalidPathResponse(path, proxy, logger, responseResults) proxy.isBlitzRequest(path) -> responseResults.contentText = "42" - else -> processRequestsBasedOnPath(proxy, path, responseResults, queryParams) + else -> processRequestsBasedOnPath(proxy, path, queryParams, responseResults) } responseResults.apply { incrementScrapeRequestCount(proxy, updateMsg) + if (proxy.options.debugEnabled) + logger.info { "CT check - handleClientRequests() contentType: $contentType" } call.respondWith(contentText, contentType, statusCode) } } @@ -113,8 +118,8 @@ object ProxyHttpRoutes { private suspend fun RoutingContext.processRequestsBasedOnPath( proxy: Proxy, path: String, - responseResults: ResponseResults, queryParams: String, + responseResults: ResponseResults, ) { val agentContextInfo = proxy.pathManager.getAgentContextInfo(path) when { @@ -137,6 +142,10 @@ object ProxyHttpRoutes { val updateMsgs: String = results.joinToString("\n") { it.updateMsg } // Grab the contentType of the first OK in the list val okContentType: ContentType? = results.firstOrNull { it.statusCode == HttpStatusCode.OK }?.contentType + if (proxy.options.debugEnabled) { + logger.info { "CT check - processRequests() contentTypes: ${contentTypes.joinToString(", ")}" } + logger.info { "CT check - processRequests() okContentType: $okContentType" } + } responseResults.apply { statusCode = if (statusCodes.contains(HttpStatusCode.OK)) HttpStatusCode.OK else statusCodes[0] @@ -155,9 +164,7 @@ object ProxyHttpRoutes { coroutineScope { agentContextInfo.agentContexts .map { agentContext -> - async { - submitScrapeRequest(agentContext, proxy, path, queryParams, call.request, call.response) - } + async { submitScrapeRequest(agentContext, proxy, path, queryParams, call.request, call.response) } } .map { deferred -> deferred.await() } .onEach { response -> logActivityForResponse(path, response, proxy) } @@ -213,40 +220,43 @@ object ProxyHttpRoutes { scrapeRequest.scrapeResults.also { scrapeResults -> HttpStatusCode.fromValue(scrapeResults.statusCode).also { statusCode -> - scrapeResults.contentType.split("/").also { contentTypeElems -> - val contentType = - if (contentTypeElems.size == 2) - ContentType(contentTypeElems[0], contentTypeElems[1]) - else - ContentType.Text.Plain + val contentType = + runCatching { + if (proxy.options.debugEnabled) + logger.info { "CT check - submitScrapeRequest() contentType: ${scrapeResults.contentType}" } + ContentType.parse(scrapeResults.contentType) + }.getOrElse { + logger.debug { "Error parsing content type: ${scrapeResults.contentType} -- ${it.simpleClassName}" } + Text.Plain.withCharset(Charsets.UTF_8) + } + logger.debug { "Content type: $contentType" } - // Do not return content on error status codes - return if (!statusCode.isSuccess()) - scrapeRequest.scrapeResults.run { - ScrapeRequestResponse( - statusCode = statusCode, - contentType = contentType, - failureReason = failureReason, - url = url, - updateMsg = "path_not_found", - fetchDuration = scrapeRequest.ageDuration(), - ) - } - else - scrapeRequest.scrapeResults.run { - // Unzip content here - ScrapeRequestResponse( - statusCode = statusCode, - contentType = contentType, - contentText = if (zipped) contentAsZipped.unzip() else contentAsText, - failureReason = failureReason, - url = url, - updateMsg = "success", - fetchDuration = scrapeRequest.ageDuration(), - ) - } - } + // Do not return content on error status codes + return if (!statusCode.isSuccess()) + scrapeRequest.scrapeResults.run { + ScrapeRequestResponse( + statusCode = statusCode, + contentType = contentType, + failureReason = failureReason, + url = url, + updateMsg = "path_not_found", + fetchDuration = scrapeRequest.ageDuration(), + ) + } + else + scrapeRequest.scrapeResults.run { + // Unzip content here + ScrapeRequestResponse( + statusCode = statusCode, + contentType = contentType, + contentText = if (zipped) contentAsZipped.unzip() else contentAsText, + failureReason = failureReason, + url = url, + updateMsg = "success", + fetchDuration = scrapeRequest.ageDuration(), + ) + } } } } @@ -272,7 +282,7 @@ object ProxyHttpRoutes { class ScrapeRequestResponse( val statusCode: HttpStatusCode, val updateMsg: String, - var contentType: ContentType = ContentType.Text.Plain, + var contentType: ContentType = Text.Plain.withCharset(Charsets.UTF_8), var contentText: String = "", val failureReason: String = "", val url: String = "", @@ -281,7 +291,7 @@ class ScrapeRequestResponse( class ResponseResults( var statusCode: HttpStatusCode = HttpStatusCode.OK, - var contentType: ContentType = ContentType.Text.Plain, + var contentType: ContentType = Text.Plain.withCharset(Charsets.UTF_8), var contentText: String = "", var updateMsg: String = "", ) diff --git a/src/main/kotlin/io/prometheus/proxy/ProxyServiceImpl.kt b/src/main/kotlin/io/prometheus/proxy/ProxyServiceImpl.kt index 1ef949c5..a0744ebd 100644 --- a/src/main/kotlin/io/prometheus/proxy/ProxyServiceImpl.kt +++ b/src/main/kotlin/io/prometheus/proxy/ProxyServiceImpl.kt @@ -148,7 +148,7 @@ internal class ProxyServiceImpl( PathMapSizeResponse .newBuilder() .also { it.pathCount = proxy.pathManager.pathMapSize } - .build() + .build()!! override suspend fun sendHeartBeat(request: HeartBeatRequest) = proxy.agentContextManager.getAgentContext(request.agentId) @@ -162,7 +162,7 @@ internal class ProxyServiceImpl( it.valid = agentContext.isNotNull() it.reason = "Invalid agentId: ${request.agentId} (sendHeartBeat)" } - .build() + .build()!! } override fun readRequestsFromProxy(request: AgentInfo): Flow = @@ -178,7 +178,7 @@ internal class ProxyServiceImpl( override suspend fun writeResponsesToProxy(requests: Flow): Empty { runCatching { requests.collect { response -> - val scrapeResults = response/*.toDataClass()*/.toScrapeResults() + val scrapeResults = response.toScrapeResults() proxy.scrapeRequestManager.assignScrapeResults(scrapeResults) } }.onFailure { throwable -> diff --git a/src/main/kotlin/io/prometheus/proxy/ProxyUtils.kt b/src/main/kotlin/io/prometheus/proxy/ProxyUtils.kt index 1c92c80c..563198ea 100644 --- a/src/main/kotlin/io/prometheus/proxy/ProxyUtils.kt +++ b/src/main/kotlin/io/prometheus/proxy/ProxyUtils.kt @@ -18,8 +18,10 @@ package io.prometheus.proxy import io.github.oshai.kotlinlogging.KLogger import io.ktor.http.ContentType +import io.ktor.http.ContentType.Text import io.ktor.http.HttpHeaders import io.ktor.http.HttpStatusCode +import io.ktor.http.withCharset import io.ktor.server.application.ApplicationCall import io.ktor.server.response.header import io.ktor.server.response.respondText @@ -119,7 +121,7 @@ object ProxyUtils { suspend fun ApplicationCall.respondWith( text: String, - contentType: ContentType = ContentType.Text.Plain, + contentType: ContentType = Text.Plain.withCharset(Charsets.UTF_8), status: HttpStatusCode = HttpStatusCode.OK, ) { response.header(HttpHeaders.CacheControl, CACHE_CONTROL_VALUE) diff --git a/src/main/kotlin/io/prometheus/proxy/ScrapeRequestWrapper.kt b/src/main/kotlin/io/prometheus/proxy/ScrapeRequestWrapper.kt index 0806a591..8905c3a6 100644 --- a/src/main/kotlin/io/prometheus/proxy/ScrapeRequestWrapper.kt +++ b/src/main/kotlin/io/prometheus/proxy/ScrapeRequestWrapper.kt @@ -58,7 +58,7 @@ internal class ScrapeRequestWrapper( it.encodedQueryParams = encodedQueryParams it.authHeader = authHeader } - .build() + .build()!! var scrapeResults: ScrapeResults by nonNullableReference() diff --git a/src/test/kotlin/io/prometheus/ProxyTests.kt b/src/test/kotlin/io/prometheus/ProxyTests.kt index 0a1257e4..91e023d5 100644 --- a/src/test/kotlin/io/prometheus/ProxyTests.kt +++ b/src/test/kotlin/io/prometheus/ProxyTests.kt @@ -30,6 +30,7 @@ import io.ktor.client.HttpClient import io.ktor.client.statement.bodyAsText import io.ktor.http.ContentType.Text import io.ktor.http.HttpStatusCode +import io.ktor.http.withCharset import io.ktor.server.cio.CIO import io.ktor.server.cio.CIOApplicationEngine import io.ktor.server.engine.EmbeddedServer @@ -56,7 +57,6 @@ import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeNull import org.amshove.kluent.shouldNotBeNull import java.util.concurrent.atomic.AtomicInteger -import kotlin.collections.set import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds @@ -88,7 +88,7 @@ internal object ProxyTests { routing { get("/$agentPath") { delay(60.seconds) - call.respondText("This is never reached", Text.Plain) + call.respondText("This is never reached", Text.Plain.withCharset(Charsets.UTF_8)) error("This should not be reached") } } @@ -156,7 +156,7 @@ internal object ProxyTests { server = embeddedServer(factory = CIO, port = port) { routing { get("/agent-$i") { - call.respondText(content, Text.Plain) + call.respondText(content, Text.Plain.withCharset(Charsets.UTF_8)) } } },