
In-app debugging overlay for inspecting logs, HTTP traffic, and analytics with syntax-highlighted JSON, secure header redaction, body truncation, modular plugin panels, and zero release overhead.
Extensible on-device dev tools for Kotlin Multiplatform
An in-app debugging overlay for KMP — inspect logs, network traffic, and analytics events with a beautiful Compose UI. No external tools needed.
Features • Installation • Quick Start • Plugins • Custom Plugins • Documentation
| Feature | Description |
|---|---|
| 🔍 Log Inspector | Search, filter, and copy logs with syntax-highlighted JSON |
| 🌐 Network Viewer | HTTP request/response inspection with method badges |
| 📊 Analytics Tracker | Monitor analytics events in real-time |
| 💥 Crash Reporter | Capture and persist fatal and non-fatal exceptions on device |
| 🎨 Beautiful UI | Material3 design with light/dark mode support |
| 🧩 Plugin System | Extend with custom debug panels through modular dependencies |
| 📱 Adaptive Layout | Bottom sheet on phones, dialog on tablets |
| 🔌 Zero Release Overhead | Disable with a single flag — no runtime cost |
| 🍎 Multiplatform | Android, iOS, Desktop (JVM), Web (WASM) |
AELog is fully modularized. Add only the dependencies you need. Every plugin module carries its dependencies transitively, so you never need to import ae-log-core manually.
Add the following to your gradle/libs.versions.toml:
[versions]
aelog = "1.1.7"
[libraries]
aelog-logs = { module = "io.github.abdo-essam:ae-log-logs", version.ref = "aelog" }
aelog-network-ktor = { module = "io.github.abdo-essam:ae-log-network-ktor", version.ref = "aelog" }
aelog-network-okhttp = { module = "io.github.abdo-essam:ae-log-network-okhttp", version.ref = "aelog" }
aelog-analytics = { module = "io.github.abdo-essam:ae-log-analytics", version.ref = "aelog" }
aelog-crashes = { module = "io.github.abdo-essam:ae-log-crashes", version.ref = "aelog" }Add the required dependencies to your target source sets in build.gradle.kts:
// build.gradle.kts (shared module)
kotlin {
sourceSets {
commonMain.dependencies {
// Pick only what you need (each carries core transitively)
implementation(libs.aelog.logs)
implementation(libs.aelog.network.ktor)
implementation(libs.aelog.analytics)
implementation(libs.aelog.crashes)
}
androidMain.dependencies {
// Add only if your Android target uses OkHttp
implementation(libs.aelog.network.okhttp)
}
}
}📖 See the Full Installation Guide for direct dependency coordinates and details on transitive inclusions.
AELog features zero-config auto-initialisation on Android! Just add the Gradle dependencies for the plugins you want, and AELog automatically boots up with sensible defaults when your app launches. No setup, configuration, or initialization code is required.
Add AELogOverlay() as a sibling anywhere in your root composable — no wrapping required. By default, the floating notch trigger is disabled (showNotch = false), allowing you to control visibility programmatically via custom developer gestures or buttons:
@Composable
fun App() {
// Renders the overlay container in the background
AELogOverlay()
MaterialTheme {
Scaffold(
floatingActionButton = {
FloatingActionButton(onClick = { AELog.show() }) {
Icon(Icons.Default.BugReport, contentDescription = "Open Inspector")
}
}
) {
YourAppContent()
}
}
}To disable the library in release builds, set:
AELog.isEnabled = BuildConfig.DEBUGTo enable the floating notch trigger on the edge of the screen:
AELogOverlay(showNotch = true)AELog is a discoverable object modelled after Android's built-in Log class.
Just type AELog. and the IDE lists every method — no extension hunting required:
AELog.log.v("Auth", "Token checked")
AELog.log.d("Auth", "Token refreshed")
AELog.log.i("HomeScreen", "App launched!")
AELog.log.w("Auth", "Session expiring soon")
AELog.log.e("Database", "Failed to clear cache", exception) // stack trace auto-appended
AELog.log.wtf("Auth", "Unexpected state")All calls are silent no-ops if the library hasn't initialized yet — safe to call from shared modules before app startup.
Omit the tag and AELog derives it from the caller's class name automatically. No repetition, no overhead:
AELog.log.d("Token refreshed") // tag → "AuthViewModel"
AELog.log.i("App launched!") // tag → "HomeScreen"
AELog.log.e("Failed to clear cache", t) // tag → "Database"// Network, Analytics & Crashes APIs
AELog.network.logRequest(method = "GET", url = "https://api.example.com/users")
AELog.network.logResponse(url = "https://api.example.com/users", statusCode = 200)
AELog.analytics.logEvent("item_added_to_cart", properties = mapOf("id" to "123"))
// Capture non-fatal exceptions manually
try {
performDangerousWork()
} catch (t: Throwable) {
AELog.crashes.recordNonFatal(t)
}AELog provides first-class interceptors for OkHttp and Ktor.
Both interceptors are secure by default. They automatically exclude sensitive headers like Authorization and Cookie to prevent credentials from appearing in logs.
// OkHttp
val interceptor = AELogOkHttpInterceptor(
excludeHeaders = setOf("X-Sensitive-Header") // Extends default exclusions
)
// Ktor
val client = HttpClient {
install(AELogKtorInterceptor) {
excludeHeaders = setOf("X-Api-Key")
}
}To prevent memory issues when inspecting large payloads (e.g., file uploads), bodies are automatically truncated (default 250 KB).
AELogOkHttpInterceptor(
maxRequestBodyBytes = 500_000, // 500 KB limit
maxResponseBodyBytes = 1_000_000 // 1 MB limit
)By default, Ktor response streams can only be read once. To enable non-destructive inspection of response bodies:
DoubleReceive plugin in your HttpClient.DoubleReceive is not installed, this may consume the stream—ensure your app logic is compatible or use the recommended plugin.val client = HttpClient {
install(DoubleReceive) // Recommended for Network Plugin
install(AELogKtorInterceptor)
}Three ways to open the inspector:
AELog.show() / AELog.hide()
Create your own debug panel (e.g., a Database Inspector or Feature Flags toggler) in 3 steps:
class FeatureFlagsPlugin : UIPlugin {
override val name = "Flags"
override val icon: @Composable () -> Unit = { Icon(Icons.Default.Flag, contentDescription = null) }
// Optional: Live badge count shown on the tab (omit if not needed)
private val _badgeCount = MutableStateFlow(0)
override val badgeCount: StateFlow<Int> = _badgeCount
@Composable
override fun Content(modifier: Modifier) {
// Your Compose UI here (owns the entire panel layout)
LazyColumn(modifier = modifier) {
items(flags) { flag ->
FlagRow(flag)
}
}
}
}
// Install your custom plugin alongside the auto-registered ones
AELog.install(FeatureFlagsPlugin())📖 See the Custom Plugins Guide for the full API reference.
AELog works with any logging library. Just forward logs to the static AELog.log methods:
// Forward logs using the static shorthands directly
AELog.log.i("MyTag", "Something happened")
AELog.log.e("Database", "Failed to clear cache", exception)📖 See the Logging Integrations Guide for adapter examples (Kermit, Napier, Timber, SLF4J).
The SDK follows an encapsulated Model-Store-API-UI pattern, making plugins 100% reactive, modular, and thread-safe.
graph TD
subgraph UI ["UI Layer"]
OV["AELogOverlay()\n(Popup — zero wrapping)"]
end
subgraph Core ["Core — ae-log-core"]
AE["AELog\n(singleton engine)"]
end
subgraph Plugins ["Plugins (optional, loaded on demand)"]
direction LR
LP1["LogPlugin\nae-log-logs"]
NP["NetworkPlugin\nae-log-network"]
AP["AnalyticsPlugin\nae-log-analytics"]
CP["CrashPlugin\nae-log-crashes"]
end
subgraph Storage ["Data Layer (StateFlow — thread-safe)"]
direction LR
LS[("LogStorage")]
NS[("NetworkStorage")]
AS[("AnalyticsStorage")]
CS[("CrashStorage")]
end
subgraph Interceptors ["Auto-interceptors (optional)"]
direction LR
KI["KtorInterceptor\nae-log-network-ktor"]
OI["OkHttpInterceptor\nae-log-network-okhttp"]
end
OV --> AE
AE --> LP1
AE --> NP
AE --> AP
AE --> CP
LP1 --> LS
NP --> NS
AP --> AS
CP --> CS
KI --> NP
OI --> NPContributions are welcome! Please read the Contributing Guide first.
git clone https://github.com/abdo-essam/AELog.git
cd AELog
./gradlew build
./gradlew allTestsCopyright 2026 Abdo Essam
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Extensible on-device dev tools for Kotlin Multiplatform
An in-app debugging overlay for KMP — inspect logs, network traffic, and analytics events with a beautiful Compose UI. No external tools needed.
Features • Installation • Quick Start • Plugins • Custom Plugins • Documentation
| Feature | Description |
|---|---|
| 🔍 Log Inspector | Search, filter, and copy logs with syntax-highlighted JSON |
| 🌐 Network Viewer | HTTP request/response inspection with method badges |
| 📊 Analytics Tracker | Monitor analytics events in real-time |
| 💥 Crash Reporter | Capture and persist fatal and non-fatal exceptions on device |
| 🎨 Beautiful UI | Material3 design with light/dark mode support |
| 🧩 Plugin System | Extend with custom debug panels through modular dependencies |
| 📱 Adaptive Layout | Bottom sheet on phones, dialog on tablets |
| 🔌 Zero Release Overhead | Disable with a single flag — no runtime cost |
| 🍎 Multiplatform | Android, iOS, Desktop (JVM), Web (WASM) |
AELog is fully modularized. Add only the dependencies you need. Every plugin module carries its dependencies transitively, so you never need to import ae-log-core manually.
Add the following to your gradle/libs.versions.toml:
[versions]
aelog = "1.1.7"
[libraries]
aelog-logs = { module = "io.github.abdo-essam:ae-log-logs", version.ref = "aelog" }
aelog-network-ktor = { module = "io.github.abdo-essam:ae-log-network-ktor", version.ref = "aelog" }
aelog-network-okhttp = { module = "io.github.abdo-essam:ae-log-network-okhttp", version.ref = "aelog" }
aelog-analytics = { module = "io.github.abdo-essam:ae-log-analytics", version.ref = "aelog" }
aelog-crashes = { module = "io.github.abdo-essam:ae-log-crashes", version.ref = "aelog" }Add the required dependencies to your target source sets in build.gradle.kts:
// build.gradle.kts (shared module)
kotlin {
sourceSets {
commonMain.dependencies {
// Pick only what you need (each carries core transitively)
implementation(libs.aelog.logs)
implementation(libs.aelog.network.ktor)
implementation(libs.aelog.analytics)
implementation(libs.aelog.crashes)
}
androidMain.dependencies {
// Add only if your Android target uses OkHttp
implementation(libs.aelog.network.okhttp)
}
}
}📖 See the Full Installation Guide for direct dependency coordinates and details on transitive inclusions.
AELog features zero-config auto-initialisation on Android! Just add the Gradle dependencies for the plugins you want, and AELog automatically boots up with sensible defaults when your app launches. No setup, configuration, or initialization code is required.
Add AELogOverlay() as a sibling anywhere in your root composable — no wrapping required. By default, the floating notch trigger is disabled (showNotch = false), allowing you to control visibility programmatically via custom developer gestures or buttons:
@Composable
fun App() {
// Renders the overlay container in the background
AELogOverlay()
MaterialTheme {
Scaffold(
floatingActionButton = {
FloatingActionButton(onClick = { AELog.show() }) {
Icon(Icons.Default.BugReport, contentDescription = "Open Inspector")
}
}
) {
YourAppContent()
}
}
}To disable the library in release builds, set:
AELog.isEnabled = BuildConfig.DEBUGTo enable the floating notch trigger on the edge of the screen:
AELogOverlay(showNotch = true)AELog is a discoverable object modelled after Android's built-in Log class.
Just type AELog. and the IDE lists every method — no extension hunting required:
AELog.log.v("Auth", "Token checked")
AELog.log.d("Auth", "Token refreshed")
AELog.log.i("HomeScreen", "App launched!")
AELog.log.w("Auth", "Session expiring soon")
AELog.log.e("Database", "Failed to clear cache", exception) // stack trace auto-appended
AELog.log.wtf("Auth", "Unexpected state")All calls are silent no-ops if the library hasn't initialized yet — safe to call from shared modules before app startup.
Omit the tag and AELog derives it from the caller's class name automatically. No repetition, no overhead:
AELog.log.d("Token refreshed") // tag → "AuthViewModel"
AELog.log.i("App launched!") // tag → "HomeScreen"
AELog.log.e("Failed to clear cache", t) // tag → "Database"// Network, Analytics & Crashes APIs
AELog.network.logRequest(method = "GET", url = "https://api.example.com/users")
AELog.network.logResponse(url = "https://api.example.com/users", statusCode = 200)
AELog.analytics.logEvent("item_added_to_cart", properties = mapOf("id" to "123"))
// Capture non-fatal exceptions manually
try {
performDangerousWork()
} catch (t: Throwable) {
AELog.crashes.recordNonFatal(t)
}AELog provides first-class interceptors for OkHttp and Ktor.
Both interceptors are secure by default. They automatically exclude sensitive headers like Authorization and Cookie to prevent credentials from appearing in logs.
// OkHttp
val interceptor = AELogOkHttpInterceptor(
excludeHeaders = setOf("X-Sensitive-Header") // Extends default exclusions
)
// Ktor
val client = HttpClient {
install(AELogKtorInterceptor) {
excludeHeaders = setOf("X-Api-Key")
}
}To prevent memory issues when inspecting large payloads (e.g., file uploads), bodies are automatically truncated (default 250 KB).
AELogOkHttpInterceptor(
maxRequestBodyBytes = 500_000, // 500 KB limit
maxResponseBodyBytes = 1_000_000 // 1 MB limit
)By default, Ktor response streams can only be read once. To enable non-destructive inspection of response bodies:
DoubleReceive plugin in your HttpClient.DoubleReceive is not installed, this may consume the stream—ensure your app logic is compatible or use the recommended plugin.val client = HttpClient {
install(DoubleReceive) // Recommended for Network Plugin
install(AELogKtorInterceptor)
}Three ways to open the inspector:
AELog.show() / AELog.hide()
Create your own debug panel (e.g., a Database Inspector or Feature Flags toggler) in 3 steps:
class FeatureFlagsPlugin : UIPlugin {
override val name = "Flags"
override val icon: @Composable () -> Unit = { Icon(Icons.Default.Flag, contentDescription = null) }
// Optional: Live badge count shown on the tab (omit if not needed)
private val _badgeCount = MutableStateFlow(0)
override val badgeCount: StateFlow<Int> = _badgeCount
@Composable
override fun Content(modifier: Modifier) {
// Your Compose UI here (owns the entire panel layout)
LazyColumn(modifier = modifier) {
items(flags) { flag ->
FlagRow(flag)
}
}
}
}
// Install your custom plugin alongside the auto-registered ones
AELog.install(FeatureFlagsPlugin())📖 See the Custom Plugins Guide for the full API reference.
AELog works with any logging library. Just forward logs to the static AELog.log methods:
// Forward logs using the static shorthands directly
AELog.log.i("MyTag", "Something happened")
AELog.log.e("Database", "Failed to clear cache", exception)📖 See the Logging Integrations Guide for adapter examples (Kermit, Napier, Timber, SLF4J).
The SDK follows an encapsulated Model-Store-API-UI pattern, making plugins 100% reactive, modular, and thread-safe.
graph TD
subgraph UI ["UI Layer"]
OV["AELogOverlay()\n(Popup — zero wrapping)"]
end
subgraph Core ["Core — ae-log-core"]
AE["AELog\n(singleton engine)"]
end
subgraph Plugins ["Plugins (optional, loaded on demand)"]
direction LR
LP1["LogPlugin\nae-log-logs"]
NP["NetworkPlugin\nae-log-network"]
AP["AnalyticsPlugin\nae-log-analytics"]
CP["CrashPlugin\nae-log-crashes"]
end
subgraph Storage ["Data Layer (StateFlow — thread-safe)"]
direction LR
LS[("LogStorage")]
NS[("NetworkStorage")]
AS[("AnalyticsStorage")]
CS[("CrashStorage")]
end
subgraph Interceptors ["Auto-interceptors (optional)"]
direction LR
KI["KtorInterceptor\nae-log-network-ktor"]
OI["OkHttpInterceptor\nae-log-network-okhttp"]
end
OV --> AE
AE --> LP1
AE --> NP
AE --> AP
AE --> CP
LP1 --> LS
NP --> NS
AP --> AS
CP --> CS
KI --> NP
OI --> NPContributions are welcome! Please read the Contributing Guide first.
git clone https://github.com/abdo-essam/AELog.git
cd AELog
./gradlew build
./gradlew allTestsCopyright 2026 Abdo Essam
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0