Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.2.4 #6

Merged
merged 1 commit into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions src/main/kotlin/com/wire/bots/polls/setup/HttpClient.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.wire.bots.polls.setup

import com.wire.bots.polls.utils.ClientRequestMetric
import com.wire.bots.polls.utils.createLogger
import com.wire.bots.polls.utils.httpCall
import io.ktor.client.HttpClient
Expand All @@ -9,7 +10,6 @@ import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.logging.LogLevel
import io.ktor.client.features.logging.Logger
import io.ktor.client.features.logging.Logging
import io.ktor.client.features.observer.ResponseObserver
import io.micrometer.core.instrument.MeterRegistry


Expand All @@ -22,14 +22,8 @@ fun createHttpClient(meterRegistry: MeterRegistry) =
serializer = JacksonSerializer()
}

// TODO check https://github.com/ktorio/ktor/issues/1813
@Suppress("ConstantConditionIf") // temporary disabled until https://github.com/ktorio/ktor/issues/1813 is resolved
if (false) {
install(ResponseObserver) {
onResponse {
meterRegistry.httpCall(it)
}
}
install(ClientRequestMetric) {
onResponse { meterRegistry.httpCall(it) }
}

install(Logging) {
Expand Down
72 changes: 72 additions & 0 deletions src/main/kotlin/com/wire/bots/polls/utils/ClientRequestMetric.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.wire.bots.polls.utils

import io.ktor.client.HttpClient
import io.ktor.client.features.HttpClientFeature
import io.ktor.client.statement.HttpReceivePipeline
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.request
import io.ktor.util.AttributeKey
import mu.KLogging

/**
* [ClientRequestMetric] callback.
*/
typealias RequestMetricHandler = suspend (RequestMetric) -> Unit


/**
* Enables callback after HttpClient sends a request with [RequestMetric].
*/
class ClientRequestMetric(private val metricHandler: RequestMetricHandler) {

class Config {
internal var metricHandler: RequestMetricHandler = {}

/**
* Set [RequestMetricHandler] called at the end of the request.
*/
fun onResponse(block: RequestMetricHandler) {
metricHandler = block
}
}

companion object Feature : HttpClientFeature<Config, ClientRequestMetric>, KLogging() {

override val key: AttributeKey<ClientRequestMetric> =
AttributeKey("ClientRequestMetric")

override fun prepare(block: Config.() -> Unit) =
ClientRequestMetric(Config().apply(block).metricHandler)

override fun install(feature: ClientRequestMetric, scope: HttpClient) {
// synchronous response pipeline hook
// instead of ResponseObserver - which spawns a new coroutine
scope.receivePipeline.intercept(HttpReceivePipeline.After) { response ->
// WARNING: Do not consume HttpResponse.content here,
// or you will corrupt the client response.
runCatching { feature.metricHandler(response.toRequestMetric()) }
.onFailure { logger.error(it) { "Error during metering!" } }
}
}

// does not touch the content
private fun HttpResponse.toRequestMetric() = RequestMetric(
requestTime = requestTime.timestamp,
responseTime = responseTime.timestamp,
method = request.method.value,
url = request.url.toString(),
responseCode = status.value
)
}
}


data class RequestMetric(
// number of epoch milliseconds
val requestTime: Long,
val responseTime: Long,
val method: String,
val url: String,
val responseCode: Int
)

17 changes: 7 additions & 10 deletions src/main/kotlin/com/wire/bots/polls/utils/PrometheusExtensions.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.wire.bots.polls.utils

import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.request
import io.micrometer.core.instrument.MeterRegistry
import io.micrometer.core.instrument.Tag
import java.util.concurrent.TimeUnit
Expand All @@ -18,18 +16,17 @@ fun MeterRegistry.countException(exception: Throwable, additionalTags: Map<Strin
counter("exceptions", tags).increment()
}


/**
* Register http call.
*
*/
fun MeterRegistry.httpCall(response: HttpResponse) {
val startTime = response.requestTime.timestamp
val endTime = response.responseTime.timestamp

val duration = endTime - startTime
fun MeterRegistry.httpCall(requestMetric: RequestMetric) {
val duration = requestMetric.responseTime - requestMetric.requestTime
val tags = mapOf(
"method" to response.request.method.value,
"url" to response.request.url.toString(),
"response_code" to response.status.value.toString()
"method" to requestMetric.method,
"url" to requestMetric.url,
"response_code" to requestMetric.responseCode.toString()
).toTags()

timer("http_calls", tags).record(duration, TimeUnit.MILLISECONDS)
Expand Down