NetLens
0.1.0indexedNetwork inspector for Ktor Client enabling opt-in request and response body capture, SQLDelight persistence, header and JSON key redaction, safe response interception, and optional in-app UI.
Network inspector for Ktor Client enabling opt-in request and response body capture, SQLDelight persistence, header and JSON key redaction, safe response interception, and optional in-app UI.
NetLens is a Kotlin Multiplatform, opt-in network inspector for Ktor Client.
It lets you see what your app is actually sending and receiving — on Android and iOS — without leaving your app.
Think Chucker, but for Ktor + KMP.
.body<T>())NetLens is published as multiple artifacts so you only include what you need:
| Module | Purpose |
|---|---|
commonMain.dependencies {
implementation("io.github.myapplabs.netlens:netlens-core:0.1.0")
implementation("io.github.myapplabs.netlens:netlens-ktor:0.1.0")
implementation("io.github.myapplabs.netlens:netlens-db:0.1.0")
// Optional UI
implementation("io.github.myapplabs.netlens:netlens-ui:0.1.0")
}
Android
val repo = SqlDelightNetLensRepository(
SqlDriverFactory()
)
iOS
val repo = SqlDelightNetLensRepository(
SqlDriverFactory()
)
val netLens = NetLens(
config = NetLensConfig(
enabled = BuildConfig.DEBUG // strongly recommended
),
repo = repo,
scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
)
val client = HttpClient {
install(NetLensPlugin) {
this.netLens = netLens
}
}
What gets logged right away
Request and response bodies are captured only when explicitly enabled per request
client.post("https://api.example.com/login") {
enableNetLensBodyCapture()
contentType(ContentType.Application.Json)
setBody("""{"email":"a@b.com","password":"secret"}""")
}
Why opt-in?
Bodies are still:
maxBodyBytes)If you include netlens-ui, you can show an in-app inspector
@Composable
fun NetLensRoot(repo: NetLensRepository) {
var selectedId by remember { mutableStateOf<String?>(null) }
if (selectedId == null) {
NetLensListScreen(repo) { selectedId = it }
} else {
NetLensDetailScreen(repo, selectedId!!) {
selectedId = null
}
}
}
Recommended usage:
NetLensConfig(
enabled = true,
maxEntries = 500,
maxBodyBytes = 64_000,
captureResponseBodyWhenRequestOptedIn = true,
contentTypeAllowList = setOf(
"application/json",
"text/plain"
)
)
Headers
AuthorizationCookieSet-CookieJSON keys
passwordtokenrefreshTokenaccessTokensecretapiKeyDevelopers can supply their own redactor through NetLensConfig.redactor to override header or body sanitization logic.
NetLensConfig(
redactor = object : NetLensRedactor {
override fun redactHeaders(headers: Map<String, List<String>>): Map<String, List<String>> = headers
override fun redactBody(contentType: String?, body: String): String = body
}
)
Android
enabled = BuildConfig.DEBUG
iOS User you own flag or configuration
| Good for | Not for |
|---|---|
| 🟢 Debugging API responses | 🔴 Production analytics |
| 🟢 Verifying serialization / headers | 🔴 Long-term sensitive logging |
| 🟢 Debugging KMP networking issues |
Apache-2.0
Issues and PRs are welcome.
NetLens is designed to be safe, explicit, and developer-friendly — contributions should follow the same philosophy.
netlens-core| Models, config, redaction, repository interface |
netlens-ktor | Ktor client plugin + opt-in capture |
netlens-db | SQLDelight persistence (Android + iOS) |
netlens-ui | Optional Compose Multiplatform UI |
| Option | Description |
|---|
enabled | Turns NetLens on/off entirely |
maxEntries | Oldest entries are pruned |
maxBodyBytes | Body truncation limit |
contentTypeAllowList | Only these bodies are logged |
captureResponseBodyWhenRequestOptedIn | Disable to never capture responses |
Surfaced from shared tags and platforms — no rankings paid for.