Ktor 2.3.12 Help

CallId

The CallId plugin allows you to trace client requests end-to-end by using unique request IDs or call IDs. Typically, working with a call ID in Ktor might look as follows:

  1. First, you need to obtain a call ID for a specific request in one of the following ways:

    • A reverse proxy (such as Nginx) or cloud provider (such as Heroku) might add a call ID in a specific header, for example, X-Request-Id. In this case, Ktor allows you to retrieve a call ID.

    • Otherwise, if a request comes without a call ID, you can generate it on the Ktor server.

  2. Next, Ktor verifies a retrieved/generated call ID using a predefined dictionary. You can also provide your own condition to verify a call ID.

  3. Finally, you can send a call ID to the client in a specific header, for example, X-Request-Id.

Using CallId along with CallLogging helps you troubleshoot calls by putting a call ID in the MDC context and configuring a logger to show a call ID for each request.

Add dependencies

To use CallId, you need to include the ktor-server-call-id artifact in the build script:

implementation("io.ktor:ktor-server-call-id:$ktor_version")
implementation "io.ktor:ktor-server-call-id:$ktor_version"
<dependency> <groupId>io.ktor</groupId> <artifactId>ktor-server-call-id-jvm</artifactId> <version>${ktor_version}</version> </dependency>

Install CallId

To install the CallId plugin to the application, pass it to the install function in the specified module. The code snippets below show how to install CallId...

  • ... inside the embeddedServer function call.

  • ... inside the explicitly defined module, which is an extension function of the Application class.

import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.server.application.* import io.ktor.server.plugins.callid.* fun main() { embeddedServer(Netty, port = 8080) { install(CallId) // ... }.start(wait = true) }
import io.ktor.server.application.* import io.ktor.server.plugins.callid.* // ... fun Application.module() { install(CallId) // ... }

Configure CallId

Retrieve a call ID

CallId provides several ways to retrieve a call ID:

  • To retrieve a call ID from the specified header, use the retrieveFromHeader function, for example:

    install(CallId) { retrieveFromHeader(HttpHeaders.XRequestId) }

    You can also use the header function to retrieve and send a call ID in the same header.

  • If required, you can retrieve a call ID from the ApplicationCall:

    install(CallId) { retrieve { call -> call.request.header(HttpHeaders.XRequestId) } }

Note that all retrieved call IDs are verified using a default dictionary.

Generate a call ID

If an incoming request doesn't include a call ID, you can generate it using the generate function:

  • The example below shows how to generate a call ID with a specific length from the predefined dictionary:

    install(CallId) { generate(10, "abcde12345") }
  • In the example below, the generate function accepts a block for generating a call ID:

    install(CallId) { val counter = atomic(0) generate { "generated-call-id-${counter.getAndIncrement()}" } }

Verify a call ID

All retrieved/generated call IDs are verified using a default dictionary, which looks as follows:

CALL_ID_DEFAULT_DICTIONARY: String = "abcdefghijklmnopqrstuvwxyz0123456789+/=-"

This means that call IDs containing capital letters won't pass verification. If required, you can apply less strict rules by using the verify function:

install(CallId) { verify { callId: String -> callId.isNotEmpty() } }

You can find the full example here: call-id.

Send a call ID to the client

After retrieving/generating a call ID, you can send it to the client:

  • The header function can be used to retrieve a call ID and send it in the same header:

    install(CallId) { header(HttpHeaders.XRequestId) }

    You can find the full example here: call-id.

  • The replyToHeader function sends a call ID in the specified header:

    install(CallId) { replyToHeader(HttpHeaders.XRequestId) }
  • If required, you can use ApplicationCall to send a call ID in a response:

    reply { call, callId -> call.response.header(HttpHeaders.XRequestId, callId) }

Put a call ID into MDC

Using CallId along with CallLogging helps you troubleshoot calls by putting a call ID in the MDC context and configuring a logger to show a call ID for each request. To do this, call the callIdMdc function inside the CallLogging configuration block and specify the desired key to be put in the MDC context:

install(CallLogging) { callIdMdc("call-id") }

This key can be passed to a logger configuration to show call IDs in the log. For instance, the logback.xml file might look as follows:

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %X{call-id} %-5level %logger{36} - %msg%n</pattern> </encoder> </appender>

You can find the full example here: call-id.

Last modified: 02 April 2024