https://kotlinlang.org logo
#ktor
Title
# ktor
z

Zhiqiang Bian

10/20/2021, 11:06 AM
Hi everyone, I am using ktor client for a KMM project. And I have installed the ktor
logging
feature. Does anyone know how to match an HTTP request to the corresponding response in the logs? Assuming I send 10 http requests to the backend immediately. And then, I should receive 10 responses. But I cannot tell which response belongs to which request from the logs. Is it possible to assign a UUID to each request and its corresponding responses, then we can match them in the logs easily? Or it must involve some changes on the backend too? For example, the frontend generates a UUID in the header, and backend use the same one in the response header? ----------------------------------------------------------- I assume that in the responsePipeline receives each response in the same order of the requestPipeline. But I am not sure if it is correct. So my temporal solution is to modify the
Logging
class (since it does not allow extends). I added two Int variables
requestSequenceId
and
responseSequenceId
. Every time the
logRequest
and
logResponse
get called, I just plus 1 for each sequenceId.
Copy code
private var requestSequenceId: Int = 0
private var responseSequenceId: Int = 0
...
private suspend fun logRequest(request: HttpRequestBuilder): OutgoingContent? {
        if (<http://level.info|level.info>) {
            logger.log("REQUEST SEQUENCE ID: ${requestSequenceId++}")
...
private fun logResponse(response: HttpResponse) {
        if (<http://level.info|level.info>) {
            logger.log("RESPONSE SEQUENCE ID: ${responseSequenceId++}")
i

Ivan Pavlov

10/20/2021, 12:24 PM
Looks similar to what CallId plugin does https://ktor.io/docs/call-id.html#generating-call-ids
a

Aleksei Tirman [JB]

10/20/2021, 12:25 PM
I didn't find a way of how to make the
Logging
feature identify request/response pairs but here is an example of how you can use attributes to achieve this:
Copy code
suspend fun main(): Unit = coroutineScope {
    val client = HttpClient(CIO)
    val key = AttributeKey<String>("Request id")

    client.sendPipeline.intercept(HttpSendPipeline.Monitoring) {
        val uuid = UUID.randomUUID().toString()
        context.attributes.put(key, uuid)
        println("[$uuid] REQUEST: ${Url(context.url)}")
    }

    client.receivePipeline.intercept(HttpReceivePipeline.State) {
        try {
            val uuid = context.attributes[key]
            println("[$uuid] RESPONSE: ${context.response.status}")
            proceedWith(subject)
        } catch (cause: Throwable) {
            throw cause
        }
    }

    (1..10).map {
        async {
            client.get<String>("<https://httpbin.org/get>")
        }
    }.awaitAll()
}
z

Zhiqiang Bian

10/20/2021, 1:05 PM
@Aleksei Tirman [JB] Thanks for your answer! I have tried the attributes, and it works. I am not sure how the pipeline is implemented in the low level. So, I just want to confirm the execution order of the interception of the two pipelines. For example,
send1=>receive1=>send2 =>receive2
should be expected order. But if
send1=>send2=>receive1=>receive2
, then will the UUID of
receive1
to be overwritten by
send2
?
@Ivan Pavlov The
CallId
thing seems to be server-side only? I cannot find a similar one on the ktor client.
a

Aleksei Tirman [JB]

10/20/2021, 1:26 PM
No, because there is a separate instance of attributes for each request. In other words attributes aren't shared between requests.
z

Zhiqiang Bian

10/20/2021, 1:32 PM
Thanks Aleksei! The ability to customise of ktor is beyond my expectation, 😁
4 Views