Ktor HTTP client core library providing asynchronous HTTP client capabilities for Kotlin multiplatform applications
—
Client lifecycle events and monitoring hooks for observing request/response processing, error handling, and performance monitoring throughout the HTTP client lifecycle.
Predefined event definitions for monitoring HTTP client operations.
/**
* Container for HTTP client event definitions
*/
object ClientEvents {
/** Fired when HTTP request is created */
val HttpRequestCreated: EventDefinition<HttpRequestBuilder>
/** Fired when HTTP request is ready for sending */
val HttpRequestIsReadyForSending: EventDefinition<HttpRequestBuilder>
/** Fired when HTTP response is received */
val HttpResponseReceived: EventDefinition<HttpResponse>
/** Fired when HTTP response receive fails */
val HttpResponseReceiveFailed: EventDefinition<HttpResponseReceiveFail>
/** Fired when HTTP response is cancelled */
val HttpResponseCancelled: EventDefinition<HttpResponse>
}Data structures for event information.
/**
* Information about failed response receive
* @param request - The original HTTP request
* @param cause - Exception that caused the failure
*/
data class HttpResponseReceiveFail(
val request: HttpRequest,
val cause: Throwable
)
/**
* Event definition for typed events
* @param T - Type of event data
*/
interface EventDefinition<T>Subscribe to client events for monitoring and logging.
/**
* Event monitoring interface
*/
interface Events {
/** Subscribe to an event */
fun <T> subscribe(definition: EventDefinition<T>, handler: suspend (T) -> Unit)
/** Unsubscribe from an event */
fun <T> unsubscribe(definition: EventDefinition<T>, handler: suspend (T) -> Unit)
/** Raise an event */
suspend fun <T> raise(definition: EventDefinition<T>, value: T)
}
// Access events through HttpClient
val HttpClient.monitor: EventsUsage Examples:
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.utils.*
val client = HttpClient()
// Subscribe to request creation events
client.monitor.subscribe(ClientEvents.HttpRequestCreated) { request ->
println("Request created: ${request.method.value} ${request.url}")
}
// Subscribe to response events
client.monitor.subscribe(ClientEvents.HttpResponseReceived) { response ->
println("Response received: ${response.status} from ${response.call.request.url}")
}
// Subscribe to failure events
client.monitor.subscribe(ClientEvents.HttpResponseReceiveFailed) { failure ->
println("Request failed: ${failure.request.url} - ${failure.cause.message}")
}
// Subscribe to cancellation events
client.monitor.subscribe(ClientEvents.HttpResponseCancelled) { response ->
println("Response cancelled: ${response.call.request.url}")
}
// Make requests - events will be fired automatically
val response = client.get("https://api.example.com/users")Create custom event definitions for application-specific monitoring.
/**
* Create custom event definition
*/
fun <T> EventDefinition(): EventDefinition<T>Custom Events Example:
import io.ktor.client.*
import io.ktor.client.utils.*
// Define custom events
val CustomRequestStart = EventDefinition<String>()
val CustomRequestComplete = EventDefinition<Pair<String, Long>>()
val client = HttpClient()
// Subscribe to custom events
client.monitor.subscribe(CustomRequestStart) { url ->
println("Starting custom operation for: $url")
}
client.monitor.subscribe(CustomRequestComplete) { (url, duration) ->
println("Completed custom operation for: $url in ${duration}ms")
}
// Raise custom events
suspend fun monitoredRequest(url: String) {
client.monitor.raise(CustomRequestStart, url)
val start = System.currentTimeMillis()
try {
val response = client.get(url)
val duration = System.currentTimeMillis() - start
client.monitor.raise(CustomRequestComplete, url to duration)
} catch (e: Exception) {
// Handle error and still fire completion event
val duration = System.currentTimeMillis() - start
client.monitor.raise(CustomRequestComplete, url to duration)
throw e
}
}Implement comprehensive logging using event monitoring.
import io.ktor.client.*
import io.ktor.client.utils.*
import kotlinx.coroutines.*
class HttpClientLogger(private val client: HttpClient) {
init {
setupEventMonitoring()
}
private fun setupEventMonitoring() {
// Log all requests
client.monitor.subscribe(ClientEvents.HttpRequestCreated) { request ->
println("[REQUEST] ${request.method.value} ${request.url}")
request.headers.entries().forEach { (name, values) ->
println("[REQUEST-HEADER] $name: ${values.joinToString(", ")}")
}
}
// Log successful responses
client.monitor.subscribe(ClientEvents.HttpResponseReceived) { response ->
println("[RESPONSE] ${response.status.value} ${response.status.description}")
println("[TIMING] Request: ${response.requestTime}, Response: ${response.responseTime}")
}
// Log failures
client.monitor.subscribe(ClientEvents.HttpResponseReceiveFailed) { failure ->
println("[ERROR] Request to ${failure.request.url} failed: ${failure.cause.message}")
failure.cause.printStackTrace()
}
// Log cancellations
client.monitor.subscribe(ClientEvents.HttpResponseCancelled) { response ->
println("[CANCELLED] Request to ${response.call.request.url} was cancelled")
}
}
}
// Usage
val client = HttpClient()
val logger = HttpClientLogger(client)
// All requests will now be logged automatically
val response = client.get("https://api.example.com/users")Use events for performance tracking and metrics collection.
import io.ktor.client.*
import io.ktor.client.utils.*
class HttpPerformanceMonitor(private val client: HttpClient) {
private val requestTimes = mutableMapOf<HttpRequest, Long>()
init {
// Track request start times
client.monitor.subscribe(ClientEvents.HttpRequestIsReadyForSending) { request ->
requestTimes[request] = System.currentTimeMillis()
}
// Calculate and log response times
client.monitor.subscribe(ClientEvents.HttpResponseReceived) { response ->
val startTime = requestTimes.remove(response.call.request)
if (startTime != null) {
val duration = System.currentTimeMillis() - startTime
println("Request to ${response.call.request.url} took ${duration}ms")
// Could send to metrics system here
recordMetric("http.request.duration", duration,
mapOf("url" to response.call.request.url.toString(),
"method" to response.call.request.method.value,
"status" to response.status.value.toString()))
}
}
// Clean up on failures
client.monitor.subscribe(ClientEvents.HttpResponseReceiveFailed) { failure ->
requestTimes.remove(failure.request)
}
}
private fun recordMetric(name: String, value: Long, tags: Map<String, String>) {
// Implementation for your metrics system
println("METRIC: $name = $value, tags = $tags")
}
}HttpRequestBuilder is created and configuredEvents provide a clean way to implement cross-cutting concerns like logging, metrics, caching, and debugging without modifying core client code.
Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-client-core-jvm