A lightweight Android Studio / IntelliJ plugin for reading your app's network traffic β without the logcat pain.
LogPose is named after the navigational device from One Piece that reads an island's "log" to point you the right way. This one reads your logcat and points you straight at the HTTP request you care about.
- Modern "Studio" tool window β a master/detail view with color-coded method/status pill badges, a hero Overview card (status, URL, duration/size/started/host/id stat chips), and side-by-side Request / Response cards.
- Collapsible JSON trees β request/response bodies are parsed back into navigable,
syntax-colored trees; bodies that are JSON nest directly under
body. Toggle Tree / Raw (raw is syntax-highlighted too). - Find in body β
βF/Ctrl+Finside either card highlights all matches with next/prev navigation and ann/totalcounter. - One-click filter bar β a compact URL search box plus Method (GET/POST/PUT/DELETE) and Status (2xxβ5xx) toggles, and a Hide noise switch. No typing required.
- Mute noisy endpoints β right-click β mute; muted calls stay visible but fade into the background (numeric path segments are normalized, so one mute covers all ids). Persists across restarts.
- Copy everything β Copy as cURL (hover a row or right-click), Copy as JSON (per-section or the whole transaction), Copy URL, Copy response body.
- First-class multipart uploads β S3/GCS media uploads show per-part metadata, never raw bytes.
- Atomic, ordered capture β no interleaved or mismatched bodies, even under load; oversized payloads are chunked and reassembled.
The usual setup β OkHttp's HttpLoggingInterceptor at BODY level dumped into logcat β
breaks down fast:
- Bodies get mismatched.
HttpLoggingInterceptoremits many separateLoglines per call. Concurrent requests on different threads interleave, so request/response bodies get switched. - Too much noise, mixed in with every other app log.
- No expand/collapse β it's a flat text stream.
- Pretty JSON eats the screen β it's all-or-nothing.
- Large bodies get truncated (logcat caps entries at ~4 KB) and multipart media uploads (S3 / GCS) are unreadable binary dumps.
- Hard to filter to "just the
/orderscalls" or "only 5xx".
The root cause is that logcat is the wrong layer. LogPose fixes it by emitting one structured transaction per HTTP exchange and rendering it in a real UI.
βββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββ
β Android app β β Android Studio / IntelliJ β
β β one JSON line β LogPose tool window β
β LogPose interceptor β per exchange β β’ list of transactions β
β builds ONE Transaction β βββ(logcat)βββββΆ β β’ expand / collapse β
β (request + response) β tag: LogPose β β’ pretty JSON β
β β β β’ filter / search β
βββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββ
- The on-device interceptor serializes the whole request+response exchange into a
single JSON object and logs it as one line under the
LogPosetag. Atomic emission is what eliminates interleaving and mismatched bodies. - The plugin runs
adb logcat -v raw -s LogPose:V(only our tag, raw payloads β no noise), parses each line, and renders a filterable master/detail view. - Payloads bigger than a logcat line are split into ordered chunks and reassembled by the plugin.
- Multipart uploads ship per-part metadata (name, filename, content-type, size) β not raw bytes β so media uploads stay readable and cheap.
The plugin talks to
adbdirectly and does not depend on the bundled Android plugin, so it works in any JetBrains IDE.
The contract between the device and the plugin is a single JSON object per line:
Multipart upload body example (no raw bytes):
"body": {
"contentType": "multipart/form-data",
"parts": [
{ "name": "file", "filename": "receipt.jpg", "contentType": "image/jpeg", "sizeBytes": 824123 },
{ "name": "meta", "contentType": "application/json", "sizeBytes": 64 }
]
}Chunk envelope (for oversized payloads):
{ "id": "a1b2c3", "seq": 0, "total": 3, "payload": "<json-fragment>" }See Transaction.kt
for the canonical schema.
Filtering is a one-line, zero-typing bar (all conditions AND-ed):
| Control | Effect |
|---|---|
| Search box | URL/path contains the text (case-insensitive) |
| METHOD GET / POST / PUT / DELETE | multi-select; show only the picked methods |
| STATUS 2xx / 3xx / 4xx / 5xx | multi-select; show only the picked status classes |
| Hide noise switch | hide muted/noise endpoints entirely (right-click a row β mute to mark it noise) |
LogPose has two halves and you need both: the IDE plugin (reads logcat) and a one-line OkHttp interceptor in your app (emits the structured transactions the plugin reads).
From a release zip (until it's on the JetBrains Marketplace):
- Download
logpose-<version>.zipfrom Releases. - Android Studio / IntelliJ β Settings β Plugins β βοΈ β Install Plugin from Diskβ¦
- Pick the zip and restart. A LogPose tool window appears at the bottom.
Or build it yourself:
git clone https://github.com/siddharthjaswal/logpose.git
cd logpose
./gradlew buildPlugin # zip in build/distributions/
# or: ./gradlew runIde # launch a sandbox IDE with the plugin loadedThe interceptor is distributed via JitPack:
// settings.gradle.kts
dependencyResolutionManagement {
repositories { maven("https://jitpack.io") }
}
// app/build.gradle.kts
dependencies {
// Debug builds: the real interceptor.
debugImplementation("com.github.siddharthjaswal.logpose:logpose-android:v0.9.11")
// Release builds: a zero-overhead no-op with the SAME api β keeps LogPose out of
// production entirely (no logcat output, no kotlinx-serialization, zero transitive deps).
releaseImplementation("com.github.siddharthjaswal.logpose:logpose-no-op:v0.9.11")
}val client = OkHttpClient.Builder()
// Add LAST so LogPose sees the final request and the decoded response.
// Compiles unchanged in both variants β the no-op exposes the same LogPoseInterceptor.
.addInterceptor(LogPoseInterceptor(LogPoseConfig(enabled = BuildConfig.DEBUG)))
.build()With the debug/release split above, the release build links against the no-op stub, so
LogPose is gone from production by construction. enabled = BuildConfig.DEBUG is then just
belt-and-suspenders. (Prefer a single artifact in all variants? Use only the
debugImplementation line and rely on the enabled flag.) See
logpose-android/README.md for config (body-size limits,
header redaction, custom tag, custom transport).
- Open the LogPose tool window (bottom edge).
- Click βΆ to start capturing β it clears the device log buffer and tails new traffic.
- Run your app on a device/emulator. Transactions stream in live.
- Filter by method/status/URL, click a row to inspect the JSON, Copy as cURL, mute noisy endpoints, etc.
Multiple devices attached? LogPose currently uses the default
adbdevice β a picker is on the roadmap.
logpose/
βββ src/β¦ # the IntelliJ / Android Studio plugin (this build)
βββ logpose-android/ # the drop-in OkHttp interceptor (separate Gradle build)
The two halves talk over the wire format above β the interceptor
emits it, the plugin reads it. See logpose-android/README.md
for the device-side setup.
- Plugin: tool window, logcat capture, master/detail, chunk reassembly
-
logpose-androidinterceptor: atomic transaction, multipart metadata, gzip, header redaction, chunking - Collapsible JSON tree + real JSON editor (folding) in Raw mode
- Copy as cURL / JSON, endpoint muting, one-click filter bar, find-in-body
- Live in-flight requests β appear on hit, ticking timer + loader until the response
- Modern "Studio" card UI, custom icon, light & dark theme
- Interceptor published on JitPack β
com.github.siddharthjaswal.logpose:logpose-android:v0.9.11(nomavenLocalneeded);jitpack.ymlbuilds thelogpose-androidsubproject. - No-op release artifact β
com.github.siddharthjaswal.logpose:logpose-no-op:v0.9.11lets you strip LogPose from release builds viareleaseImplementation(same API, zero deps). - Plugin published on the JetBrains Marketplace
β search "LogPose" in Plugins; signing + publishing wired via GitHub Actions (
RELEASING.md). - Maven Central for the interceptor (optional, more "official" than JitPack).
- CI (GitHub Actions):
buildPlugin+verifyPluginon push/PR; GitHub Release on tag. - Plugin compatibility verified (Plugin Verifier vs 2024.1 / 2024.3;
since-build 233, no upper bound; bundled JSON module). -
CHANGELOG.md+<change-notes>inplugin.xml; semantic versioning. - Security/privacy: documented β runs on debug/staging only,
Authorization& cookies redacted on-device, bodies never leave logcat. - Tests for the pure logic:
TransactionParser(incl. chunk reassembly),CurlBuilderquoting,FilterStatematching,MutedEndpoints.normalize, body capture (multipart/binary/gzip/truncation). (the main remaining item)
- Per-device picker when multiple devices/emulators are attached.
- Settings panel (tag, body limits, default filters) instead of code-only config.
- Optional socket transport (
adb reverse) to bypass logcat truncation entirely. - Persist/replay captured sessions; export HAR.
- Zero-setup "raw OkHttp" capture mode (parse stock
HttpLoggingInterceptoroutput).
Issues and PRs welcome. This is pre-1.0 β the wire format may still change.






{ "id": "a1b2c3", // correlates request + response "startedAtMillis": 1733500000000, "durationMillis": 142, "request": { "method": "POST", "url": "https://api.example.com/v1/orders", "host": "api.example.com", "path": "/v1/orders", "headers": { "Content-Type": "application/json" }, "body": { "contentType": "application/json", "sizeBytes": 57, "text": "{...}" } }, "response": { "code": 200, "message": "OK", "headers": { "Content-Type": "application/json" }, "body": { "contentType": "application/json", "sizeBytes": 1203, "text": "{...}", "truncated": false } } }