Skip to content

siddharthjaswal/logpose

Repository files navigation

LogPose 🧭

A lightweight Android Studio / IntelliJ plugin for reading your app's network traffic β€” without the logcat pain.

CI Release License

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.


Demo

LogPose β€” capture, filter, inspect, and copy as cURL

Screenshots

Inspect β€” every request and response, fully structured

Filter β€” drill to failures in a single click

Read β€” collapsible, syntax-highlighted JSON

Reproduce β€” copy any call as cURL, paste and replay

Focus β€” mute the noise, keep the signal


Features

  • 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+F inside either card highlights all matches with next/prev navigation and an n/total counter.
  • 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.

Why?

The usual setup β€” OkHttp's HttpLoggingInterceptor at BODY level dumped into logcat β€” breaks down fast:

  • Bodies get mismatched. HttpLoggingInterceptor emits many separate Log lines 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 /orders calls" 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.

How it works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  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 LogPose tag. 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 adb directly and does not depend on the bundled Android plugin, so it works in any JetBrains IDE.

The wire format

The contract between the device and the plugin is a single JSON object per line:

{
  "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 }
  }
}

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

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)

Getting started

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).

1. Install the plugin

From a release zip (until it's on the JetBrains Marketplace):

  1. Download logpose-<version>.zip from Releases.
  2. Android Studio / IntelliJ β†’ Settings β†’ Plugins β†’ βš™οΈ β†’ Install Plugin from Disk…
  3. 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 loaded

2. Add the interceptor to your app

The 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).

3. Capture

  1. Open the LogPose tool window (bottom edge).
  2. Click β–Ά to start capturing β€” it clears the device log buffer and tails new traffic.
  3. Run your app on a device/emulator. Transactions stream in live.
  4. 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 adb device β€” a picker is on the roadmap.

Repository layout

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.

What works today

  • Plugin: tool window, logcat capture, master/detail, chunk reassembly
  • logpose-android interceptor: 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

Road to 1.0 β€” production checklist

Distribution

  • Interceptor published on JitPack β€” com.github.siddharthjaswal.logpose:logpose-android:v0.9.11 (no mavenLocal needed); jitpack.yml builds the logpose-android subproject.
  • No-op release artifact β€” com.github.siddharthjaswal.logpose:logpose-no-op:v0.9.11 lets you strip LogPose from release builds via releaseImplementation (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).

Quality & trust

  • CI (GitHub Actions): buildPlugin + verifyPlugin on 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> in plugin.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), CurlBuilder quoting, FilterState matching, MutedEndpoints.normalize, body capture (multipart/binary/gzip/truncation). (the main remaining item)

Polish / nice-to-have

  • 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 HttpLoggingInterceptor output).

Contributing

Issues and PRs welcome. This is pre-1.0 β€” the wire format may still change.

License

Apache 2.0

About

A lightweight Android Studio / IntelliJ plugin for reading your app's network traffic without the logcat pain. 🧭

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages