SQLite

Instrument SQLite databases – including Room and SQLDelight – via SentrySQLiteDriver and SentrySupportSQLiteOpenHelper.

The sentry-android-sqlite library generates spans for your SQLite queries, whether you use Room, SQLDelight, or another persistence library. It does so by wrapping your existing SQLiteDriver or SupportSQLiteOpenHelper.

If you're using the Sentry Android Gradle Plugin, wrapping is performed automatically. (See Auto-Instrumentation for more details.)

Room users can also take advantage of our auto-instrumentation of DAO methods.

The Sentry Android Gradle Plugin (SAGP) uses bytecode manipulation to automatically wrap your driver and/or open helper.

To use the SAGP, follow the plugin installation instructions. The SAGP auto-installs the Sentry Android SDK by default.

† For now, the SAGP only auto-wraps uses of SQLiteDriver with Room. Any non-Room uses of the driver must be wrapped manually. SAGP auto-wraps open helpers in all contexts.

The type or call site wrapped depends on the SAGP version:

APIMinimum SAGP versionCoverage
SQLiteDriver≥6.13.0Room.databaseBuilder().setDriver(...) call sites
SupportSQLiteOpenHelper≥3.0.0FrameworkSQLiteOpenHelperFactory
SupportSQLiteOpenHelper≥3.11.0Any SupportSQLiteOpenHelper.Factory (SQLDelight, custom factories, etc.)

No additional configuration is required, as database auto-instrumentation is enabled by default. To disable it, use the DATABASE instrumentation feature:

build.gradle
Copied
import io.sentry.android.gradle.extensions.InstrumentationFeature

sentry {
  tracingInstrumentation {
    enabled = true
    features = EnumSet.allOf(InstrumentationFeature) - InstrumentationFeature.DATABASE
  }
}

Disabling DATABASE instrumentation disables SQLiteDriver wrapping, SupportSQLiteOpenHelper wrapping, and Room DAO spans.

See our Gradle page for other SAGP configuration options.

If you don't use the SAGP, you can always wrap your driver or open helper explicitly. Add the Sentry Android SDK and the sentry-android-sqlite artifact:

build.gradle
Copied
dependencies {
  implementation 'io.sentry:sentry-android:8.50.1'
  implementation 'io.sentry:sentry-android-sqlite:8.50.1'
}

The sentry-android-sqlite artifact ships both wrappers; the minimum version depends on which API you need to wrap:

APIMinimum sentry-android-sqlite version
SQLiteDriver≥8.45.0
SupportSQLiteOpenHelper≥6.21.0

Wrap your driver or open helper directly with SentrySQLiteDriver.create(...) or SentrySupportSQLiteOpenHelper.create(...). Use the wrapped instance wherever you would have used the unwrapped one. For most teams, that means wiring it into Room or SQLDelight:

Copied
import androidx.room.Room
import androidx.sqlite.driver.AndroidSQLiteDriver
import io.sentry.sqlite.SentrySQLiteDriver

val database = Room.databaseBuilder(context, MyDatabase::class.java, "dbName")
    .setDriver(SentrySQLiteDriver.create(AndroidSQLiteDriver()))
    .build()

Note: If you're using the AndroidX SupportSQLiteDriver, you'll want to make sure you're wrapping the open helper but not the support driver itself. See the alert under the migration section for more details.

Copied
import androidx.sqlite.db.SupportSQLiteOpenHelper
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
import io.sentry.android.sqlite.SentrySupportSQLiteOpenHelper

val driver = AndroidSqliteDriver(
    schema = MyDatabase.Schema,
    context = context,
    name = "myapp.db",
    factory = SupportSQLiteOpenHelper.Factory { configuration ->
        SentrySupportSQLiteOpenHelper.create(FrameworkSQLiteOpenHelperFactory().create(configuration))
    },
)

Note: SQLDelight doesn't currently support SQLiteDriver.

If you're switching your app's SQLite API from SupportSQLiteOpenHelper to SQLiteDriver, you may need to update your Sentry dependencies:

PathPrevious minimumNew minimum
Manual instrumentationsentry-android-sqlite 6.21.0sentry-android-sqlite 8.45.0
Auto-instrumentationSentry Android Gradle Plugin 3.11.0Sentry Android Gradle Plugin 6.13.0

† SAGP installs sentry-android-sqlite transitively when your project depends on androidx.sqlite:sqlite, so you typically don't need to add it. But if your project explicitly pins sentry-android-sqlite, bump it to ≥8.45.0.

Replace SentrySupportSQLiteOpenHelper.create(openHelper) with SentrySQLiteDriver.create(driver), and you're all set! (See Manual Instrumentation → Room for the full call-site context.)

Note on span attributes: SentrySQLiteDriver derives the db.name span attribute from the base name of the file path passed to open() (for example, /data/.../my.dbmy.db), while SentrySupportSQLiteOpenHelper uses AndroidX's databaseName verbatim.

Note on span durations: Due to underlying API differences, SQLiteDriver and SupportSQLiteOpenHelper span durations may differ. Driver spans are limited to work performed by the database during statement execution, while open helpers may also track time spent on statement preparation or work the owning app performs consuming native SQLite output.

To confirm that your wrapped driver or open helper is producing spans, execute a query inside a Sentry transaction and check for the SQL span in sentry.io.

Copied
import io.sentry.Sentry
import io.sentry.SpanStatus
import io.sentry.TransactionOptions

// `driver` is your SentrySQLiteDriver-wrapped instance (see Configure section above).
val transaction = Sentry.startTransaction(
    "DB Smoke Test",
    "db.query",
    TransactionOptions().apply { isBindToScope = true },
)

driver.open(":memory:").use { connection ->
    connection.prepare("SELECT 1").use { it.step() }
}

transaction.finish(SpanStatus.OK)

To view the recorded transaction, log into sentry.io and open the Traces page. Filter by your transaction name (for example, transaction:"DB Smoke Test"), then open the trace. You should see a SQL-level span emitted by the Sentry wrapper around your driver or open helper, with the executed query as its description:

For users of Room, the SAGP will also auto-instrument your DAO classes, adding a span around each DAO method invocation. This gives you a higher-level span for the DAO operation on top of the SQL-level spans produced by the underlying driver or open helper wrapper, making it easier to attribute queries back to the code that issued them.

DAO methods are instrumented via bytecode manipulation. We don't currently support manual instrumentation.

Was this helpful?
Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").