integrations

Client-agnostic by design.
One line per callback.

HTTP capture is automatic with the Ktor plugin. For everything else, Sharingan deliberately ships no client adapters — your MQTT/BLE library already has callbacks, and one logger call inside each is the whole integration. No version coupling, no transitive dependencies.

Ktor — automatic HTTP capture

zero-config
val client = HttpClient {
    install(SharinganKtor)
}

Tunable when you need it:

full configuration
install(SharinganKtor) {
    captureBodies = true            // default
    maxBodyBytes = 64 * 1024        // default — longer bodies truncate with a marker
    redactedHeaders = setOf("Authorization", "X-Api-Key")  // additive to defaults
}

OkHttp / NSURLSession — manual logging

Using OkHttp or NSURLSession directly today? Log through the public HttpLogger — same event model, same UI, same exports:

e.g. inside an OkHttp interceptor you own
Sharingan.http.log(
    method = "GET",
    url = "https://api.example.dev/v1/devices",
    statusCode = 200,
    durationMillis = 214,
    requestHeaders = listOf("Accept" to "application/json"),
    responseBody = bodyText,          // JSON renders pretty-printed
    timing = listOf(TimingPhase("TTFB", 180), TimingPhase("Download", 34)),
)
Coming up: a first-class OkHttp interceptor — sharingan-okhttp — is designed and on the roadmap: application-interceptor capture with the same config vocabulary as the Ktor plugin. Until it ships, manual logging above is the supported path.

MQTT — clients with callbacks (KMQTT, HiveMQ, Paho…)

wire each callback
client.subscribe(filter, qos) { /* granted */ Sharingan.mqtt.subscribed(filter, qos) }

client.onMessage { msg ->
    Sharingan.mqtt.received(msg.topic, msg.payload.decodeToString(), msg.qos)
}

client.publish(topic, bytes, qos, retained).also {
    Sharingan.mqtt.publish(topic, bytes.decodeToString(), qos, retained)
}
Tip: log payloads as JSON text whenever possible — Sharingan pretty-prints and syntax-colors valid JSON in the detail view and in agent exports.

BLE — Kable

connection state + notifications
val peripheral = scope.peripheral(advertisement)

peripheral.state.onEach { state ->
    when (state) {
        is State.Connected -> Sharingan.ble.connect(peripheral.nameOrId())
        is State.Disconnected -> Sharingan.ble.disconnect(peripheral.nameOrId(), state.status?.toString())
        else -> Unit
    }
}.launchIn(scope)

peripheral.observe(hrCharacteristic).onEach { bytes ->
    Sharingan.ble.notify(
        device = peripheral.nameOrId(),
        characteristic = "Heart Rate Measurement",
        uuid = hrCharacteristic.characteristicUuid.toString(),
        value = """{"bpm":${bytes.toHeartRate()}}""",
    )
}.launchIn(scope)

private fun Peripheral.nameOrId() = name ?: identifier.toString()

Errors get a first-class call too: Sharingan.ble.error(device, message, characteristic, uuid) — GATT status codes land on the failure rail like any HTTP 500.

iOS — SwiftUI wrapper

SharinganView.swift
import SwiftUI
import shared // your shared framework

// No-wrapper alternative — presents over topmost VC, any thread:
SharinganViewControllerKt.presentSharingan(animated: true)

// Or embed the view controller yourself:
struct SharinganView: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> UIViewController {
        SharinganViewControllerKt.SharinganViewController()
    }
    func updateUIViewController(_ vc: UIViewController, context: Context) {}
}

// usage
.sheet(isPresented: $showLogs) { SharinganView() }

Extras

Shake-to-open (Android)

any shake detector
// on shake trigger:
Sharingan.show(context)

iOS Live Activity analog (app-side)

iOS doesn't let a library ship a sticky notification. If you want a lock-screen capture card, add an ActivityKit widget extension in your app and drive it from Sharingan.events (via SKIE / KMP-NativeCoroutines) — counters plus the last event line. This stays app-side by design: widget extensions can't ship from a Kotlin library.