Skip to content

Commit

Permalink
Oppgradere ktor feature til ny prometheus versjon
Browse files Browse the repository at this point in the history
  • Loading branch information
gtcno committed Sep 20, 2024
1 parent 90f3e9a commit 40516c6
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 43 deletions.
2 changes: 1 addition & 1 deletion ktor-client-metrics/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
}
dependencies {
api(libs.ktor.client.cio)
implementation("io.prometheus:simpleclient_common:0.16.0")
implementation("io.prometheus:prometheus-metrics-core:1.3.1")
testImplementation(libs.ktor.client.mock)
testImplementation(libs.kotest.assertions.core)
testImplementation("org.junit.jupiter:junit-jupiter-api:${libs.versions.junit.get()}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,33 @@ import io.ktor.client.request.HttpSendPipeline
import io.ktor.client.statement.HttpReceivePipeline
import io.ktor.util.AttributeKey
import io.ktor.util.pipeline.PipelinePhase
import io.prometheus.client.CollectorRegistry
import io.prometheus.client.Counter
import io.prometheus.client.Histogram
import io.prometheus.metrics.core.datapoints.Timer
import io.prometheus.metrics.core.metrics.Counter
import io.prometheus.metrics.core.metrics.Histogram
import io.prometheus.metrics.model.registry.PrometheusRegistry

class PrometheusMetricsPlugin private constructor(registry: CollectorRegistry, baseName: String) {
class PrometheusMetricsPlugin private constructor(
registry: PrometheusRegistry = PrometheusRegistry.defaultRegistry,
baseName: String,
) {
private val duration =
Histogram
.build("duration", "Time spent in requests")
.namespace(baseName)
.builder()
.name("${baseName}_duration")
.help("Time spent in requests")
.register(registry)

private val httpStatus =
Counter
.build("status_total", "Count status codes for responses")
.namespace(baseName)
.builder()
.name("${baseName}_status")
.help("Count status codes for responses")
.labelNames("status")
.register(registry)

class Config {
var baseName: String = "ktor_client_metrics"
var registry: CollectorRegistry = CollectorRegistry.defaultRegistry
var registry: PrometheusRegistry = PrometheusRegistry.defaultRegistry
}

companion object Feature : HttpClientPlugin<Config, PrometheusMetricsPlugin> {
Expand Down Expand Up @@ -60,7 +66,7 @@ class PrometheusMetricsPlugin private constructor(registry: CollectorRegistry, b
}
}

private data class CallMeasure(val timer: Histogram.Timer)
private data class CallMeasure(val timer: Timer)

private val metricKey = AttributeKey<CallMeasure>("metrics")

Expand All @@ -70,7 +76,7 @@ class PrometheusMetricsPlugin private constructor(registry: CollectorRegistry, b

private fun after(call: HttpClientCall) {
httpStatus
.labels(call.response.status.value.toString())
.labelValues(call.response.status.value.toString())
.inc()

call.attributes.getOrNull(metricKey)?.apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,26 @@ import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.request.get
import io.ktor.http.HttpStatusCode
import io.ktor.http.path
import io.prometheus.client.Collector
import io.prometheus.client.CollectorRegistry
import io.prometheus.metrics.model.registry.PrometheusRegistry
import io.prometheus.metrics.model.snapshots.CounterSnapshot
import io.prometheus.metrics.model.snapshots.HistogramSnapshot
import io.prometheus.metrics.model.snapshots.MetricSnapshot
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test

class PrometheusMetricsPluginTest {
private val defaultRegistry: CollectorRegistry = CollectorRegistry.defaultRegistry
private lateinit var defaultRegistry: PrometheusRegistry
private lateinit var client: HttpClient

@BeforeEach
fun setUp() {
defaultRegistry = PrometheusRegistry()
client =
HttpClient(MockEngine) {
install(PrometheusMetricsPlugin) {
baseName = ""
baseName = "basename"
this.registry = defaultRegistry
}
engine {
Expand All @@ -37,6 +39,7 @@ class PrometheusMetricsPluginTest {
delay(100L)
respondOk("Hello, world")
}

"/ok" -> respondOk("Hello, world")
"/not-found" -> respondError(HttpStatusCode.NotFound)
else -> error("Unhandled URL ${request.url.encodedPath}")
Expand All @@ -46,19 +49,19 @@ class PrometheusMetricsPluginTest {
}
}

@AfterEach
fun tearDown() {
defaultRegistry.clear()
}
private inline fun <reified T : MetricSnapshot> MetricSnapshot.getSnapShot(): T = this as T

@Test
fun `calls are timed`() {
runBlocking {
client.get { url { path("/measured") } }
}

getCount("duration") shouldBe 1
getSum("duration").shouldBeGreaterThan(0.1)
defaultRegistry.scrape { it.contains("duration") }.single().getSnapShot<HistogramSnapshot>().let {
it.dataPoints.single().let { data ->
data.count shouldBe 1
data.sum shouldBeGreaterThan 0.1
}
}
}

@Test
Expand All @@ -71,25 +74,14 @@ class PrometheusMetricsPluginTest {
}
}

getStatus("201") shouldBe null
getStatus("200") shouldBe 1
getStatus("404") shouldBe 1
defaultRegistry.scrape { it.contains("status") }.single().getSnapShot<CounterSnapshot>().let {
it.getStatusValue("200") shouldBe 1.0
it.getStatusValue("404") shouldBe 1.0
it.getStatusValue("201") shouldBe null
}
}

private fun getStatus(statusCode: String) =
defaultRegistry.getSampleValue("status_total", listOf("status").toTypedArray(), listOf(statusCode).toTypedArray())

private fun getCount(name: String): Double = defaultRegistry.getSampleValue("${name}_count").toDouble()

private fun getSum(name: String): Double = defaultRegistry.getSampleValue("${name}_sum").toDouble()

private fun getBucket(
name: String,
bucket: Double,
): Double =
defaultRegistry.getSampleValue(
"${name}_bucket",
listOf("le").toTypedArray(),
listOf(Collector.doubleToGoString(bucket)).toTypedArray(),
)
private fun CounterSnapshot.getStatusValue(status: String): Double? {
return this.dataPoints.singleOrNull { it.labels["status"] == status }?.value
}
}

0 comments on commit 40516c6

Please sign in to comment.