---
name: ktormonitor
version: 1.16.0
description: >
  KtorMonitor is a Kotlin Multiplatform library for real-time HTTP traffic monitoring.
  It supports Ktor Client, OkHttp, and http4k on Android, iOS, Desktop JVM, Wasm, and JS.
  Use this skill when a developer needs to integrate, configure, display, or troubleshoot
  HTTP monitoring in a Kotlin or Android project.
tags:
  - kotlin
  - android
  - ios
  - kotlin-multiplatform
  - ktor
  - okhttp
  - http4k
  - compose-multiplatform
  - networking
  - debugging
---

# KtorMonitor Skill

## What this skill does

When a developer asks to:
- Add HTTP traffic monitoring to a Kotlin / Android / KMP project
- Inspect Ktor Client, OkHttp, or http4k requests and responses
- Configure log retention, body size limits, or header sanitization
- Display the KtorMonitor UI on any platform
- Exclude monitoring code from production (release) builds
- Fix issues with KtorMonitor (notifications, body truncation, desugaring, etc.)

…follow the steps below in order, adapting to the developer's specific platform and HTTP client.

---

## Step 1 — Identify the HTTP client and platform

| HTTP client | Supported platforms |
|---|---|
| Ktor Client | Android, iOS, Desktop JVM, Wasm, JS |
| OkHttp | Android, Desktop JVM |
| http4k | Android, Desktop JVM |

---

## Step 2 — Add the dependency

### Ktor Client — Kotlin Multiplatform

```kotlin
// build.gradle.kts
kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation("ro.cosminmihu.ktor:ktor-monitor-logging:1.16.0")
        }
    }
}
```

**No-op for release builds** (KMP):

```kotlin
kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation("ro.cosminmihu.ktor:ktor-monitor-logging-no-op:1.16.0")
        }
    }
}
```

### Ktor Client — Android only

```kotlin
// build.gradle.kts (app module)
dependencies {
    debugImplementation("ro.cosminmihu.ktor:ktor-monitor-logging:1.16.0")
    releaseImplementation("ro.cosminmihu.ktor:ktor-monitor-logging-no-op:1.16.0")
}
```

### OkHttp — Android & JVM

```kotlin
dependencies {
    debugImplementation("ro.cosminmihu.ktor:ktor-monitor-okhttp-interceptor:1.16.0")
    releaseImplementation("ro.cosminmihu.ktor:ktor-monitor-okhttp-interceptor-no-op:1.16.0")
}
```

### http4k — Android & JVM

```kotlin
dependencies {
    debugImplementation("ro.cosminmihu.ktor:ktor-monitor-http4k-filter:1.16.0")
    releaseImplementation("ro.cosminmihu.ktor:ktor-monitor-http4k-filter-no-op:1.16.0")
}
```

> **Android minSdk < 26**: add Core Library Desugaring:
> ```kotlin
> android {
>     compileOptions { isCoreLibraryDesugaringEnabled = true }
> }
> dependencies {
>     coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
> }
> ```

---

## Step 3 — Install the plugin / interceptor / filter

### Ktor Client Plugin

```kotlin
import ro.cosminmihu.ktor.monitor.KtorMonitorLogging
import ro.cosminmihu.ktor.monitor.RetentionPeriod
import ro.cosminmihu.ktor.monitor.ContentLength

val client = HttpClient {
    install(KtorMonitorLogging) {
        // Replace matching header values with "***" in the logs
        sanitizeHeader { header -> header == "Authorization" }

        // Return true to SKIP logging for a request
        filter { request -> !request.url.host.contains("example.com") }

        // Show OS notification summarising active requests (Android & iOS only)
        showNotification = true

        // How long captured calls are kept. Default: OneHour
        retentionPeriod = RetentionPeriod.OneHour

        // Max body bytes captured. Default: 250 000. Use Full to disable the limit.
        maxContentLength = ContentLength.Default
    }
}
```

### OkHttp Interceptor

> Always use `addNetworkInterceptor` — it sees actual network traffic including redirects.

```kotlin
import ro.cosminmihu.ktor.monitor.KtorMonitorInterceptor
import ro.cosminmihu.ktor.monitor.RetentionPeriod
import ro.cosminmihu.ktor.monitor.ContentLength

val client = OkHttpClient.Builder()
    .addNetworkInterceptor(
        KtorMonitorInterceptor {
            sanitizeHeader { header -> header == "Authorization" }
            filter { request -> !request.url.host.contains("example.com") }
            showNotification = true
            retentionPeriod = RetentionPeriod.OneHour
            maxContentLength = ContentLength.Default
        }
    )
    .build()
```

### http4k Filter

```kotlin
import ro.cosminmihu.ktor.monitor.KtorMonitorFilter
import ro.cosminmihu.ktor.monitor.RetentionPeriod
import ro.cosminmihu.ktor.monitor.ContentLength

val app = KtorMonitorFilter {
    sanitizeHeader { header -> header == "Authorization" }
    filter { request -> !request.uri.host.contains("example.com") }
    showNotification = true          // Android only for http4k
    retentionPeriod = RetentionPeriod.OneHour
    maxContentLength = ContentLength.Default
}.then(JavaHttpClient())
```

---

## Step 4 — Add the UI for the target platform

### Compose Multiplatform (all targets)

```kotlin
import ro.cosminmihu.ktor.monitor.KtorMonitor

@Composable
fun App() {
    KtorMonitor()
}
```

### Android

Embed `KtorMonitor()` in any Composable. When `showNotification = true` and
`android.permission.POST_NOTIFICATIONS` is granted, tapping the notification opens the
monitor automatically — no extra Activity setup needed.

### iOS

```kotlin
// Kotlin shared module
import ro.cosminmihu.ktor.monitor.KtorMonitorViewController

fun MainViewController() = KtorMonitorViewController()
```

```swift
// SwiftUI
struct KtorMonitorView: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> UIViewController {
        MainViewControllerKt.MainViewController()
    }
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}

struct ContentView: View {
    var body: some View {
        KtorMonitorView().ignoresSafeArea()
    }
}
```

### Desktop — Compose (Window + optional system tray)

```kotlin
import ro.cosminmihu.ktor.monitor.KtorMonitorWindow
import ro.cosminmihu.ktor.monitor.KtorMonitorMenuItem

fun main() = application {
    var showKtorMonitor by rememberSaveable { mutableStateOf(false) }

    Tray(icon = painterResource(Res.drawable.ic_launcher), menu = {
        KtorMonitorMenuItem { showKtorMonitor = true }
    })

    KtorMonitorWindow(
        show = showKtorMonitor,
        onCloseRequest = { showKtorMonitor = false }
    )

    Window(onCloseRequest = ::exitApplication) { App() }
}
```

### Desktop — Swing

```kotlin
import ro.cosminmihu.ktor.monitor.KtorMonitorPanel

fun main() {
    SwingUtilities.invokeLater {
        val frame = JFrame("Ktor Monitor")
        frame.add(KtorMonitorPanel, BorderLayout.CENTER)
        frame.isVisible = true
    }
}
```

### Wasm / JS (browser)

```kotlin
// build.gradle.kts — add webpack helper
kotlin {
    sourceSets {
        webMain.dependencies {
            implementation(devNpm("copy-webpack-plugin", "9.1.0"))
        }
    }
}
```

```javascript
// {project}/webpack.config.d/sqljs.js
config.resolve = { fallback: { fs: false, path: false, crypto: false } };
const CopyWebpackPlugin = require('copy-webpack-plugin');
config.plugins.push(new CopyWebpackPlugin({
    patterns: ['../../node_modules/sql.js/dist/sql-wasm.wasm']
}));
```

```kotlin
// Main entry point
fun main() {
    ComposeViewport(document.body!!) { App() }
}
```

---

## Configuration reference

| Option | Type | Default | Description |
|---|---|---|---|
| `sanitizeHeader` | `(String) -> Boolean` | — | Matching header values are replaced with `***` |
| `filter` | `(request) -> Boolean` | — | Matching requests are **not** logged |
| `showNotification` | `Boolean` | `true` | OS notification (Android & iOS only; needs permission) |
| `retentionPeriod` | `RetentionPeriod` | `OneHour` | How long calls are stored locally |
| `maxContentLength` | `ContentLength` | `Default` (250 000 B) | Truncation limit; `Full` disables it |

### RetentionPeriod values

```
OneMinute · ThirtyMinutes · OneHour · TwoHours · OneDay · OneWeek
```

### ContentLength values

```
ContentLength.Default   // 250 000 bytes
ContentLength.Full      // no limit
```

---

## No-op artifact map (production safety)

| Debug | Release / No-op |
|---|---|
| `ktor-monitor-logging` | `ktor-monitor-logging-no-op` |
| `ktor-monitor-okhttp-interceptor` | `ktor-monitor-okhttp-interceptor-no-op` |
| `ktor-monitor-http4k-filter` | `ktor-monitor-http4k-filter-no-op` |
| `ktor-monitor-core` | `ktor-monitor-core-no-op` |

All no-op variants are ABI-identical to their counterparts — zero code changes needed when switching.

---

## Common questions & troubleshooting

### Body appears truncated in the UI
Set `maxContentLength = ContentLength.Full` to capture the entire body.

### No notification appears on Android
Grant `android.permission.POST_NOTIFICATIONS` at runtime and ensure `showNotification = true`.

### OkHttp — some requests are missing
Use `addNetworkInterceptor`, not `addInterceptor`. The network interceptor sees all traffic.

### http4k — streaming responses are not captured
Only materialized bodies are supported. Streaming bodies are skipped.

### Build fails on Android with `minSdk < 26`
Enable Core Library Desugaring (see Step 2 note above).

### WebSocket / SSE support?
Yes — KtorMonitorLogging captures WebSocket frames and Server-Sent Events automatically.

### Which body formats get rich preview?
JSON, XML, HTML, CSS, YAML, Markdown, Form Data, Multipart, Images (JPG / PNG / SVG / GIF / WEBP), Hex.

---

## Resources

| Resource | URL |
|---|---|
| Documentation | https://cosminmihumdc.github.io/KtorMonitor |
| API reference | https://cosminmihumdc.github.io/KtorMonitor/api |
| GitHub | https://github.com/CosminMihuMDC/KtorMonitor |
| Maven Central | https://search.maven.org/artifact/ro.cosminmihu.ktor/ktor-monitor-logging |
| Slack (#ktormonitor) | https://kotlinlang.slack.com/archives/C0AB9GA32H0 |
| klibs.io | https://klibs.io/project/CosminMihuMDC/KtorMonitor |
| Context7 | https://context7.com/cosminmihumdc/ktormonitor |
