Zero-configuration runtime diagnostics for debug/internal builds—available instantly when you need it.
DebugOverlay gives you a lightweight, always-available look into your app's runtime state so you can spot regressions before you reach for heavy profilers. Use it during development, QA testing, CI runs, or customer repro investigations.
What makes it different:
- Proactive – Catch issues while developing, not after a QA report
- Self-contained – No companion app, no adb, no cloud account
- No special permissions – No
SYSTEM_ALERT_WINDOWrequired - Developer-first – Low-friction access to runtime state
Not a replacement for: Deep profiling (Android Profiler), leak detection (LeakCanary), or crash reporting (Crashlytics). DebugOverlay is your "check engine light"—it tells you when to look deeper.
// app/build.gradle.kts
debugImplementation("com.ms-square:debugoverlay:2.0.0")
// That's it! Overlay appears automatically on app launch.
// Tap to open debug panel. Long-press to drag.Draggable overlay with real-time metrics and sparklines:
- CPU – App CPU usage from
/proc/self/stat - Heap – JVM heap usage percentage
- PSS – Proportional Set Size in MB
- FPS – Real-time frame rate
Tap the overlay to open a full-screen diagnostic panel:
- Logcat – Live system logcat stream with level filtering and search
- [Custom Log] – Additional tab when using Timber or custom log sources
- AppExits – App exit (e.g., Crash/ANR) history on Android 11+ with stack traces
- Network – Request list with timing and inspection (setup required)
- JankStats – Frame timing analysis and jank breakdown
- UI – View hierarchy via Radiography
- Device – Hardware specs, OS info, battery, network status
- Bug Report – One-tap HTML report with screenshot and diagnostics
Requirements: Android 7.0+ (API 24). Pure Java/XML apps work fine—no Compose setup needed in your app.
ProGuard/R8: No additional rules required. Works with R8 out of the box.
Add to gradle/libs.versions.toml:
[versions]
debugoverlay = "2.0.0"
[libraries]
debugoverlay = { module = "com.ms-square:debugoverlay", version.ref = "debugoverlay" }
debugoverlay-okhttp = { module = "com.ms-square:debugoverlay-extension-okhttp", version.ref = "debugoverlay" }
debugoverlay-timber = { module = "com.ms-square:debugoverlay-extension-timber", version.ref = "debugoverlay" }Then in app/build.gradle.kts:
dependencies {
debugImplementation(libs.debugoverlay)
// Optional extensions
debugImplementation(libs.debugoverlay.okhttp)
debugImplementation(libs.debugoverlay.timber)
}// app/build.gradle.kts
dependencies {
debugImplementation("com.ms-square:debugoverlay:2.0.0")
}The overlay installs automatically via AndroidX Startup. No code required—just add the dependency.
To disable auto-install (e.g., for specific build flavors), add this to your AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="merge">
<meta-data
android:name="com.ms.square.debugoverlay.DebugOverlayStartupInitializer"
tools:node="remove" />
</provider>
</application>
</manifest>Note: Manual lifecycle control is not supported in v2.0.0 at the moment. Disabling auto-install means the overlay won't appear.
By default DebugOverlay shows the real-time metrics overlay (OverlayMode.FullMetrics). For QA/internal builds, you can show only the Bug Reporter FAB:
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
DebugOverlay.configure {
overlayMode = OverlayMode.BugReporterOnly
}
}
}dependencies {
debugImplementation("com.ms-square:debugoverlay:2.0.0")
debugImplementation("com.ms-square:debugoverlay-extension-okhttp:2.0.0")
}val client = OkHttpClient.Builder()
.addNetworkInterceptor(DebugOverlayNetworkInterceptor())
.build()By default it redacts common auth headers and query params. To customize redaction and body size limits:
import com.ms.square.debugoverlay.extension.okhttp.DEFAULT_HEADERS_REDACT
import com.ms.square.debugoverlay.extension.okhttp.DEFAULT_QUERY_PARAMS_REDACT
val client = OkHttpClient.Builder()
.addNetworkInterceptor(
DebugOverlayNetworkInterceptor(
headersNameToRedact = DEFAULT_HEADERS_REDACT + setOf("x-my-custom-token"),
queryParamsNameToRedact = DEFAULT_QUERY_PARAMS_REDACT + setOf("sessionId"),
maxBodySize = 128 * 1024L, // 128KB (use 0L to omit all bodies)
)
)
.build()dependencies {
debugImplementation("com.ms-square:debugoverlay:2.0.0")
debugImplementation("com.ms-square:debugoverlay-extension-timber:2.0.0")
}Auto-plants via AndroidX Startup. Adds a separate "Timber" tab alongside Logcat with full stack traces.
To disable auto-plant, remove TimberTreeStartupInitializer via manifest merger:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="merge">
<meta-data
android:name="com.ms.square.debugoverlay.extension.timber.TimberTreeStartupInitializer"
tools:node="remove" />
</provider>
</application>
</manifest>Then call Timber.plant(DebugOverlayTimberTree()) manually.
In OverlayMode.FullMetrics, tap the bug icon in the debug panel toolbar. In OverlayMode.BugReporterOnly, tap the bug reporter FAB. Generates a ZIP with:
- Interactive HTML dashboard
- Embedded screenshot
- Logs (Logcat/Timber)
- Network history (requires OkHttp extension)
- JankStats
- App exit history (Android 11+)
- UI hierarchy
- Device info
If you dismiss the metadata dialog instead of submitting, DebugOverlay saves the capture as a draft (stored in internal storage, excluded from backups). Next time you open Bug Report, you can resume or delete saved drafts.
Privacy: Reports contain raw logs and network data. Review before sharing externally. The OkHttp extension supports header redaction and body size limits to minimize sensitive data capture.
Add app-specific diagnostic data (preferences, feature flags, etc.) to bug reports:
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// Class-based contributor
DebugOverlay.addBugReportContributor(UserPreferencesContributor(applicationContext))
// Lambda-based for simple cases
DebugOverlay.addBugReportContributor(
BugReportDataContributor("build_info.txt") { out ->
out.write("version=${BuildConfig.VERSION_NAME}\n".toByteArray())
out.write("code=${BuildConfig.VERSION_CODE}\n".toByteArray())
}
)
}
}
class UserPreferencesContributor(
private val context: Context
) : BugReportDataContributor {
override val filename = "preferences.txt"
override fun writeTo(outputStream: OutputStream) {
PrintWriter(outputStream).use { writer ->
context.getSharedPreferences("settings", MODE_PRIVATE)
.all
.filterNot { it.key.contains("token", ignoreCase = true) } // Filter sensitive data
.forEach { (key, value) -> writer.println("$key = $value") }
}
}
}Custom files appear in the bug report ZIP with a custom_ prefix (e.g., custom_preferences.txt).
Note: Contributors have a 5-second timeout. Use Application context to avoid memory leaks.
Test R8-optimized builds while keeping diagnostics:
android {
buildTypes {
create("releaseWithOverlay") {
initWith(getByName("release"))
matchingFallbacks += "release"
applicationIdSuffix = ".internal"
}
}
}
dependencies {
"releaseWithOverlayImplementation"("com.ms-square:debugoverlay:2.0.0")
}./gradlew installReleaseWithOverlay./gradlew :sample:assembleDebugThe sample app includes a releaseWithOverlay build type (see Advanced Setup) for including DebugOverlay in an internal release build:
./gradlew :sample:installReleaseWithOverlaySee CHANGELOG.md for release history.






