Skip to content

Latest commit

 

History

History
 
 

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

README.md

sentry-android-sqlite

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 with SentrySQLiteDriver.create(...) and pass it to Room.databaseBuilder(...).setDriver(...).
  • androidx.sqlite.db.SupportSQLiteOpenHelper (legacy Room): Wrap your open helper with SentrySupportSQLiteOpenHelper.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.

Avoiding duplicate spans with Room 2.7+

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()

Migration

db.name across paths

The two instrumentation paths derive the db.name span field differently, which matters while a migration from openHelperFactory to setDriver is in flight:

  • SentrySQLiteDriver sets db.name to the basename of the path passed to SQLiteDriver.open(fileName) (e.g., myapp.db from /data/.../databases/myapp.db). This is not the Room.databaseBuilder name unless that name happens to match the on-disk filename.
  • SentrySupportSQLiteOpenHelper sets db.name from SupportSQLiteOpenHelper.databaseName, which for Room is the builder name (e.g., "tracks" from Room.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).

Span granularity for multi-statement scripts

The two paths hook in at different layers, which changes how multi-statement scripts are reported:

  • SentrySupportSQLiteOpenHelper wraps high-level calls like execSQL(String). A script such as "CREATE TABLE ...; INSERT ...; INSERT ...;" passed to execSQL produces a single span whose description is the full script.
  • SentrySQLiteDriver wraps SQLiteStatement.step(). The Driver API compiles one statement per prepare(...) 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.

Package layout

This module is organized as two separate packages:

  • io.sentry.android.sqlite: Android-specific code. Classes here depend on android.database.* (e.g., CrossProcessCursor, SQLException) and/or on androidx.sqlite.db.*, the Android-only compatibility layer over the platform's SQLite. The SentrySupportSQLiteOpenHelper path and its span helper SQLiteSpanManager live here.
  • io.sentry.sqlite: Code whose contract depends only on the multiplatform androidx.sqlite.* interfaces (e.g., SQLiteDriver and SQLiteConnection). SentrySQLiteDriver and its span helper SQLiteSpanRecorder live 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.