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.
// 1. Add snapshot repository (settings.gradle.kts → repositories block)
maven(url = "https://central.sonatype.com/repository/maven-snapshots")
// 2. Add dependency (app/build.gradle.kts)
debugImplementation("com.ms-square:debugoverlay:2.0.0-SNAPSHOT")
// That's it! Overlay appears automatically on app launch.
// Tap to open debug panel. Long-press to drag.See Installation for full settings.gradle.kts context.
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.
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven(url = "https://central.sonatype.com/repository/maven-snapshots")
}
}Stable release planned. Remove this repository once 2.0.0 is released on Maven Central.
dependencies {
debugImplementation("com.ms-square:debugoverlay:2.0.0-SNAPSHOT")
}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 {
copy(overlayMode = OverlayMode.BugReporterOnly)
}
}
}dependencies {
debugImplementation("com.ms-square:debugoverlay:2.0.0-SNAPSHOT")
debugImplementation("com.ms-square:debugoverlay-extension-okhttp:2.0.0-SNAPSHOT")
}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-SNAPSHOT")
debugImplementation("com.ms-square:debugoverlay-extension-timber:2.0.0-SNAPSHOT")
}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 app cache). 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()
DebugOverlay.configure {
copy(
bugReportDataContributors = listOf(
// Class-based contributor
UserPreferencesContributor(applicationContext),
// Lambda-based for simple cases
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-SNAPSHOT")
}./gradlew installReleaseWithOverlay- Data is stored locally only —> no cloud sync or team collaboration features
To generate a local code coverage report for non-UI code (merging all library modules):
./gradlew mergedJacocoReportThe report will be available at build/reports/jacoco/mergedJacocoReport/html/index.html.
./gradlew :sample:assembleDebug





