This module provides automatic SQLite query instrumentation for Android, creating a Sentry span for each SQL statement executed.
Two instrumentation paths are supported, matching the two SQLite APIs offered by AndroidX:
androidx.sqlite.SQLiteDriver(Room 2.7+): Wrap your driver withSentrySQLiteDriver.create(...)and pass it toRoom.databaseBuilder(...).setDriver(...).androidx.sqlite.db.SupportSQLiteOpenHelper(legacy Room): Wrap your open helper withSentrySupportSQLiteOpenHelper.create(...), or let the Sentry Android Gradle plugin apply it automatically.
Use one instrumentation path per database file to avoid duplicate spans: either setDriver or openHelperFactory, not both on the same stack.
AndroidX ships a public adapter class, androidx.sqlite.driver.SupportSQLiteDriver, which lets developers convert an existing SupportSQLiteOpenHelper into a SQLiteDriver that Room 2.7+ accepts. Be careful not to wrap both the open helper and the driver with Sentry! If you do, you'll produce duplicate spans for every SQL statement. (And remember that the Sentry Android Gradle Plugin will wrap the open helper for you at the byte code level if configured to do so.)
// AVOID — this configuration produces duplicate spans for every SQL statement.
// Step 1: Developer wraps their open helper with Sentry, either manually or
// via the Sentry Android Gradle Plugin.
val sentryWrappedHelper: SupportSQLiteOpenHelper =
SentrySupportSQLiteOpenHelper.create(
FrameworkSQLiteOpenHelperFactory().create(configuration)
)
// Step 2: Developer builds the compat driver around that wrapped helper.
val driver: SQLiteDriver = SupportSQLiteDriver(sentryWrappedHelper)
// Step 3: Developer (wrongly!) wraps the driver with Sentry as well. All
// spans will now be duplicated.
val sentryWrappedDriver: SQLiteDriver = SentrySQLiteDriver.create(driver)
Room.databaseBuilder(context, MyDb::class.java, "mydb")
.setDriver(sentryWrappedDriver)
.build()The two instrumentation paths derive the db.name span field differently, which matters while a migration from openHelperFactory to setDriver is in flight:
SentrySQLiteDriversetsdb.nameto the basename of the path passed toSQLiteDriver.open(fileName)(e.g.,myapp.dbfrom/data/.../databases/myapp.db). This is not theRoom.databaseBuildername unless that name happens to match the on-disk filename.SentrySupportSQLiteOpenHelpersetsdb.namefromSupportSQLiteOpenHelper.databaseName, which for Room is the builder name (e.g.,"tracks"fromRoom.databaseBuilder(context, MyDb::class.java, "tracks")).
While both paths are in use for the same logical database, expect the same underlying file to appear under two different db.name values in the Sentry UI (e.g., tracks vs. tracks.db).
The two paths hook in at different layers, which changes how multi-statement scripts are reported:
SentrySupportSQLiteOpenHelperwraps high-level calls likeexecSQL(String). A script such as"CREATE TABLE ...; INSERT ...; INSERT ...;"passed toexecSQLproduces a single span whose description is the full script.SentrySQLiteDriverwrapsSQLiteStatement.step(). The Driver API compiles one statement perprepare(...)call, so the same logical work is split into separate prepare/step cycles by the caller (or by Room) and produces one span per statement.
This is generally a more accurate model — each statement gets its own timing and description — but expect span counts to go up for code paths that previously bundled multiple statements into one execSQL call.
This module is organized as two separate packages:
io.sentry.android.sqlite: Android-specific code. Classes here depend onandroid.database.*(e.g.,CrossProcessCursor,SQLException) and/or onandroidx.sqlite.db.*, the Android-only compatibility layer over the platform's SQLite. TheSentrySupportSQLiteOpenHelperpath and its span helperSQLiteSpanManagerlive here.io.sentry.sqlite: Code whose contract depends only on the multiplatformandroidx.sqlite.*interfaces (e.g.,SQLiteDriverandSQLiteConnection).SentrySQLiteDriverand its span helperSQLiteSpanRecorderlive here.
The split anticipates the possibility of future Kotlin Multiplatform support. The androidx.sqlite.* driver interfaces are defined in the library's commonMain source set and are reused by Room across Android, JVM, and native targets. Classes in io.sentry.sqlite are written against those portable interfaces and are intended to lift cleanly into a KMP commonMain source set if/when the sentry core gains multiplatform targets. Classes in io.sentry.android.sqlite are Android-only by construction and will stay where they are.
Note that the module artifact itself (sentry-android-sqlite) is currently an Android-only AAR regardless of package layout.