HTTP client logging plugin for Ktor client framework with configurable logging formats, levels, and platform-specific logger integrations
—
The Ktor Client Logging plugin provides platform-optimized implementations and features tailored to different runtime environments.
import io.ktor.client.*
import io.ktor.client.plugins.logging.*
import kotlinx.coroutines.*
import org.slf4j.MDC // JVM only
import kotlin.coroutines.CoroutineContextThe JVM implementation seamlessly integrates with SLF4J, the standard logging facade for Java applications.
val Logger.Companion.DEFAULT: LoggerJVM Implementation Details:
LoggerFactory.getLogger(HttpClient::class.java)Configuration Example:
// Inherits your SLF4J configuration
Logging {
logger = Logger.DEFAULT // Uses SLF4J
level = LogLevel.ALL
}Special Android-optimized logger with Logcat integration and automatic message chunking.
val Logger.Companion.ANDROID: LoggerFeatures:
android.util.Log when availableAndroid-Specific Behavior:
android.util.Log class not foundUsage in Android Projects:
val client = HttpClient {
install(Logging) {
logger = Logger.ANDROID
level = LogLevel.HEADERS
}
}Maintain Mapped Diagnostic Context (MDC) across coroutine boundaries for enhanced logging correlation.
// JVM Platform
actual fun MDCContext(): CoroutineContext.Element
// JavaScript/WASM Platform
actual fun MDCContext(): CoroutineContext.Element
// Native/Posix Platform
actual fun MDCContext(): CoroutineContext.ElementPurpose:
Usage with Coroutines:
// Set up MDC context
MDC.put("requestId", "req-123")
MDC.put("userId", "user-456")
// Create client with MDC context preservation
val client = HttpClient {
install(Logging) {
logger = Logger.DEFAULT // Will include MDC context
level = LogLevel.INFO
}
}
// MDC context is automatically propagated through coroutines
runBlocking(MDCContext()) {
val response = client.get("https://api.example.com/data")
// Logs will include requestId and userId from MDC
}Integration with Structured Logging:
// Example with logback-structured-config
MDC.put("traceId", UUID.randomUUID().toString())
MDC.put("operation", "user-creation")
val client = HttpClient {
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
filter { request ->
request.url.pathSegments.contains("users")
}
}
}
withContext(MDCContext()) {
// All HTTP logs will include traceId and operation
client.post("https://api.example.com/users") {
contentType(ContentType.Application.Json)
setBody(userCreateRequest)
}
}val Logger.Companion.DEFAULT: Logger // Points to Logger.SIMPLEFeatures:
Browser Example:
val client = HttpClient {
install(Logging) {
logger = Logger.DEFAULT // Uses console
level = LogLevel.INFO
}
}val Logger.Companion.DEFAULT: Logger // Points to Logger.SIMPLEFeatures:
The Logger.DEFAULT implementation automatically selects the most appropriate logging mechanism for each platform:
// Same code works across all platforms
val client = HttpClient {
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.HEADERS
}
}Platform Mappings:
Logger.ANDROID for Logcat)val client = HttpClient {
install(Logging) {
// Leverage SLF4J configuration
logger = Logger.DEFAULT
// Use Android-specific features if needed
logger = Logger.ANDROID
// Or customize with message limiting
logger = MessageLengthLimitingLogger(
maxLength = 8000, // Higher limit for JVM
delegate = Logger.DEFAULT
)
level = LogLevel.ALL
}
}val client = HttpClient {
install(Logging) {
// Optimized for Android Logcat
logger = Logger.ANDROID
// Conservative logging to preserve performance/battery
level = LogLevel.INFO
format = LoggingFormat.OkHttp // More compact
// Filter to reduce log volume
filter { request ->
!request.url.host.contains("analytics") &&
!request.url.pathSegments.contains("ping")
}
}
}val client = HttpClient {
install(Logging) {
logger = Logger.DEFAULT // Console logging
// Minimal logging for production builds
level = when (js("process.env.NODE_ENV")) {
"production" -> LogLevel.NONE
"development" -> LogLevel.ALL
else -> LogLevel.INFO
}
}
}All platform-specific features maintain the same API surface:
// This code works identically across all platforms
class ApiClient {
private val client = HttpClient {
install(Logging) {
logger = Logger.DEFAULT // Platform-appropriate implementation
level = LogLevel.HEADERS
format = LoggingFormat.OkHttp
filter { request ->
request.url.host == "api.myapp.com"
}
sanitizeHeader { headerName ->
headerName.equals("Authorization", ignoreCase = true)
}
}
}
suspend fun fetchUser(id: String): User {
return client.get("https://api.myapp.com/users/$id").body()
}
}val platformOptimizedClient = HttpClient {
install(Logging) {
// Platform-specific logger selection
logger = when (Platform.Current) {
is JvmPlatform -> if (isAndroid()) Logger.ANDROID else Logger.DEFAULT
is JsPlatform -> Logger.SIMPLE
is NativePlatform -> Logger.SIMPLE
}
// Platform-appropriate log levels
level = when (Platform.Current) {
is JvmPlatform -> LogLevel.ALL // JVM can handle verbose logging
else -> LogLevel.INFO // Mobile/web prefer less verbose
}
format = LoggingFormat.OkHttp // Compact format for all platforms
}
}LogLevel.ALL with large payloadsInstall with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-client-logging-jvm