Skip to content

Commit 9946092

Browse files
committed
docs(android): Add SQLiteDriver support to SQLite docs
Rewrites the Room and SQLite integration page to accommodate the introduction of the new SentrySQLiteDriver (see [sentry-java #5466](getsentry/sentry-java#5466)). Page is now called simply "SQLite" in order to emphasize the fact that we support Room, SQLDelight, and direct driver use – without making the title so long as to be overwhelming.
1 parent 796d7e9 commit 9946092

3 files changed

Lines changed: 164 additions & 110 deletions

File tree

Binary file not shown.
Lines changed: 160 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,76 @@
11
---
2-
title: Room and SQLite
2+
title: SQLite (Room & SQLDelight)
33
caseStyle: camelCase
44
supportLevel: production
55
sdk: sentry.java.android.sqlite
66
description: >-
7-
Learn more about the Sentry Room and AndroidX SQLite integrations for the
8-
Android SDK.
7+
Instrument SQLite databases – including Room and SQLDelight – via SentrySQLiteDriver and SentrySupportSQLiteOpenHelper.
98
categories:
109
- mobile
1110
og_image: /og-images/platforms-android-integrations-room-and-sqlite.png
1211
---
1312

14-
<Alert>
13+
The `sentry-android-sqlite` library generates spans for your SQLite queries, whether you use [Room](https://developer.android.com/training/data-storage/room/), [SQLDelight](https://sqldelight.github.io/sqldelight/), or another persistence library. It does so by wrapping your existing `SQLiteDriver` or `SupportSQLiteOpenHelper`.
1514

16-
Supported in Sentry's Android SDK version `4.0.0` and above.
15+
If you're using the Sentry Android Gradle Plugin, wrapping is performed automatically. (See [Auto-Instrumentation](#auto-instrumentation) for more details.)
1716

18-
Supported in Sentry Android Gradle Plugin version `3.0.0` and above.
17+
Room users can also take advantage of our [auto-instrumentation of DAO methods](#room-dao-methods).
1918

20-
Custom instrumentation is supported in Sentry's Android SDK, version `6.21.0` and above.
19+
<Alert>
2120

22-
</Alert>
21+
Check out our [migration advice](#migrating-from-supportsqliteopenhelper-to-sqlitedriver) if you're migrating from `SupportSQLiteOpenHelper` to `SQLiteDriver`.
2322

24-
## Auto-Installation With the Sentry Android Gradle Plugin
23+
Spans require [tracing to be enabled](/platforms/android/tracing/#configure-the-sample-rate).
2524

26-
The [Sentry Android Gradle Plugin](/platforms/android/configuration/gradle/) provides Room and AndroidX SQLite support through bytecode manipulation. The source can be found [on GitHub](https://github.com/getsentry/sentry-android-gradle-plugin/tree/main/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation).
25+
</Alert>
2726

28-
On this page, we get you up and running with Sentry's Room and SQLite Integration, so that it will automatically start a span from an active transaction that's bound to the scope of each sqlite/dao query.
27+
## Auto-Instrumentation
2928

3029
### Install
3130

32-
To use the Room and AndroidX SQLite integration, add the Sentry Android Gradle plugin and the Sentry Android SDK (version `4.0.0` or above) in `build.gradle`:
31+
The [Sentry Android Gradle Plugin](/platforms/android/configuration/gradle/) (SAGP) uses bytecode manipulation to automatically wrap your driver and/or open helper.<sup>†</sup>
32+
33+
To use the SAGP, apply it alongside the Sentry Android SDK:
3334

34-
```groovy
35+
```groovy {filename:build.gradle}
3536
plugins {
36-
id "io.sentry.android.gradle" version "{{@inject packages.version('sentry.java.android.gradle-plugin', '3.0.0') }}"
37+
id "io.sentry.android.gradle" version "{{@inject packages.version('sentry.java.android.gradle-plugin', '6.13.0') }}"
3738
}
3839
3940
dependencies {
40-
implementation 'io.sentry:sentry-android:{{@inject packages.version('sentry.java.android', '5.0.0') }}'
41+
implementation 'io.sentry:sentry-android:{{@inject packages.version('sentry.java.android', '8.45.0') }}'
4142
}
4243
```
4344

44-
```kotlin
45+
```kotlin {filename:build.gradle.kts}
4546
plugins {
46-
id("io.sentry.android.gradle") version "{{@inject packages.version('sentry.java.android.gradle-plugin', '3.0.0') }}"
47+
id("io.sentry.android.gradle") version "{{@inject packages.version('sentry.java.android.gradle-plugin', '6.13.0') }}"
4748
}
4849

4950
dependencies {
50-
implementation("io.sentry:sentry-android:{{@inject packages.version('sentry.java.android', '5.0.0') }}")
51+
implementation("io.sentry:sentry-android:{{@inject packages.version('sentry.java.android', '8.45.0') }}")
5152
}
5253
```
5354

54-
<Alert>
55+
<small>
56+
† For now the SAGP only auto-wraps uses of `SQLiteDriver` with Room. Any
57+
non-Room uses of the driver must be wrapped manually. SAGP auto-wraps open
58+
helpers in all contexts.
59+
</small>
5560

56-
Make sure, that [tracing](/platforms/android/tracing/#configure-the-sample-rate) is enabled.
61+
The type or call site wrapped depends on the SAGP version:
5762

58-
</Alert>
63+
| API | Minimum SAGP version | Coverage |
64+
| ------------------------- | -------------------- | -------------------------------------------------------------------------- |
65+
| `SQLiteDriver` | `≥6.13.0` | `Room.databaseBuilder().setDriver(...)` call sites |
66+
| `SupportSQLiteOpenHelper` | `≥3.0.0` | `FrameworkSQLiteOpenHelperFactory` |
67+
| `SupportSQLiteOpenHelper` | `≥3.11.0` | Any `SupportSQLiteOpenHelper.Factory` (SQLDelight, custom factories, etc.) |
5968

6069
### Configure
6170

62-
In general, no further configuration is required as the auto-instrumentation is enabled by default. If you would like to disable the database instrumentation feature, we expose a configuration option for that:
71+
No further configuration is required as database auto-instrumentation is enabled by default. To disable it – including `SQLiteDriver` wrapping, `SupportSQLiteOpenHelper` wrapping, and Room DAO spans – use the `DATABASE` instrumentation feature:
6372

64-
```groovy
73+
```groovy {filename:build.gradle}
6574
import io.sentry.android.gradle.extensions.InstrumentationFeature
6675
6776
sentry {
@@ -72,7 +81,7 @@ sentry {
7281
}
7382
```
7483

75-
```kotlin
84+
```kotlin {filename:build.gradle.kts}
7685
import java.util.EnumSet
7786
import io.sentry.android.gradle.extensions.InstrumentationFeature
7887

@@ -84,45 +93,54 @@ sentry {
8493
}
8594
```
8695

87-
## Manual Installation
96+
See our [Gradle](/platforms/android/configuration/gradle/) page for other SAGP configuration options.
8897

89-
<Alert>
90-
91-
Supported in Sentry's Android SDK, version `6.21.0` and above.
92-
93-
</Alert>
98+
## Manual Instrumentation
9499

95100
### Install
96101

97-
Sentry captures data by wrapping a `SupportSQLiteOpenHelper.Factory`. To add the SQLite integration, initialize the [Android SDK](/platforms/android/), then add the `sentry-android-sqlite` dependency using Gradle:
102+
If you don't use the SAGP, you can always wrap your driver or open helper by hand. Add the Sentry Android SDK and the `sentry-android-sqlite` artifact:
98103

99-
```groovy
100-
implementation 'io.sentry:sentry-android:{{@inject packages.version('sentry.java.android', '6.21.0') }}'
101-
implementation 'io.sentry:sentry-android-sqlite:{{@inject packages.version('sentry.java.android.sqlite', '6.21.0') }}'
104+
```groovy {filename:build.gradle}
105+
dependencies {
106+
implementation 'io.sentry:sentry-android:{{@inject packages.version('sentry.java.android', '8.45.0') }}'
107+
implementation 'io.sentry:sentry-android-sqlite:{{@inject packages.version('sentry.java.android.sqlite', '8.45.0') }}'
108+
}
102109
```
103110

104-
### Configure
111+
```kotlin {filename:build.gradle.kts}
112+
dependencies {
113+
implementation("io.sentry:sentry-android:{{@inject packages.version('sentry.java.android', '8.45.0') }}")
114+
implementation("io.sentry:sentry-android-sqlite:{{@inject packages.version('sentry.java.android.sqlite', '8.45.0') }}")
115+
}
116+
```
105117

106-
No configuration is required. Just wrap your `SupportSQLiteOpenHelper` instance in `SentrySupportSQLiteOpenHelper`.
118+
The `sentry-android-sqlite` artifact ships both wrappers; the minimum version depends on which API you need to wrap:
107119

108-
```kotlin
109-
import io.sentry.android.sqlite.SentrySupportSQLiteOpenHelper
120+
| API | Minimum `sentry-android-sqlite` version |
121+
| ------------------------- | --------------------------------------- |
122+
| `SQLiteDriver` | `≥8.45.0` |
123+
| `SupportSQLiteOpenHelper` | `≥6.21.0` |
110124

111-
private val myOpenHelper = MyOpenHelper()
112-
private val instrumentedOpenHelper = SentrySupportSQLiteOpenHelper.create(myOpenHelper)
113-
```
125+
### Configure
114126

115-
```java
116-
import io.sentry.android.sqlite.SentrySupportSQLiteOpenHelper;
127+
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:
117128

118-
private final SupportSQLiteOpenHelper myOpenHelper = new MyOpenHelper();
119-
private final SupportSQLiteOpenHelper instrumentedOpenHelper = SentrySupportSQLiteOpenHelper.create(myOpenHelper);
120-
```
129+
#### Room
130+
131+
```kotlin {tabTitle:SQLiteDriver} {mdExpandTabs}
132+
import androidx.room.Room
133+
import androidx.sqlite.driver.AndroidSQLiteDriver
134+
import io.sentry.sqlite.SentrySQLiteDriver
121135

122-
Room is supported when using the default `FrameworkSQLiteOpenHelperFactory` provided by the `androidx.sqlite` package, but any custom `SupportSQLiteOpenHelper` can be used.
136+
val database = Room.databaseBuilder(context, MyDatabase::class.java, "dbName")
137+
.setDriver(SentrySQLiteDriver.create(AndroidSQLiteDriver()))
138+
.build()
139+
```
123140

124-
```kotlin
141+
```kotlin {tabTitle:SupportSQLiteOpenHelper}
125142
import androidx.room.Room
143+
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
126144
import io.sentry.android.sqlite.SentrySupportSQLiteOpenHelper
127145

128146
val database = Room.databaseBuilder(context, MyDatabase::class.java, "dbName")
@@ -132,92 +150,128 @@ val database = Room.databaseBuilder(context, MyDatabase::class.java, "dbName")
132150
.build()
133151
```
134152

153+
**Note**: If you're using the AndroidX [`SupportSQLiteDriver`](https://developer.android.com/reference/kotlin/androidx/sqlite/driver/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](#migrating-from-supportsqliteopenhelper-to-sqlitedriver) for more details.
154+
155+
#### SQLDelight
156+
157+
```kotlin
158+
import androidx.sqlite.db.SupportSQLiteOpenHelper
159+
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
160+
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
161+
import io.sentry.android.sqlite.SentrySupportSQLiteOpenHelper
162+
163+
val driver = AndroidSqliteDriver(
164+
schema = MyDatabase.Schema,
165+
context = context,
166+
name = "myapp.db",
167+
factory = SupportSQLiteOpenHelper.Factory { configuration ->
168+
SentrySupportSQLiteOpenHelper.create(FrameworkSQLiteOpenHelperFactory().create(configuration))
169+
},
170+
)
171+
```
172+
135173
```java
136-
import androidx.room.Room;
174+
import androidx.sqlite.db.SupportSQLiteOpenHelper;
175+
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory;
176+
import app.cash.sqldelight.driver.android.AndroidSqliteDriver;
137177
import io.sentry.android.sqlite.SentrySupportSQLiteOpenHelper;
138178

139-
final MyDatabase database = Room.databaseBuilder(context, MyDatabase.class, "dbName")
140-
.openHelperFactory (configuration ->
141-
SentrySupportSQLiteOpenHelper.create(new FrameworkSQLiteOpenHelperFactory().create(configuration))
142-
)
143-
.build();
179+
SupportSQLiteOpenHelper.Factory factory = configuration ->
180+
SentrySupportSQLiteOpenHelper.create(new FrameworkSQLiteOpenHelperFactory().create(configuration));
181+
182+
AndroidSqliteDriver driver = new AndroidSqliteDriver(
183+
MyDatabase.Companion.getSchema(),
184+
context,
185+
"myapp.db",
186+
factory
187+
);
144188
```
145189

190+
**Note**: SQLDelight doesn't currently support `SQLiteDriver`.
191+
192+
## Migrating from SupportSQLiteOpenHelper to SQLiteDriver
193+
194+
<Alert>
195+
196+
This section doesn't apply to migrations to AndroidX's [`SupportSQLiteDriver`](https://developer.android.com/reference/kotlin/androidx/sqlite/driver/SupportSQLiteDriver) (the bridge adapter for Room `[2.7, 3.0)`).
197+
198+
The support driver consumes your existing `SupportSQLiteOpenHelper`, so you should continue to use `SentrySupportSQLiteOpenHelper` to wrap your helper as before.
199+
200+
</Alert>
201+
202+
If you're switching your app's SQLite API from `SupportSQLiteOpenHelper` to `SQLiteDriver`, you may need to update your Sentry dependencies:
203+
204+
| Path | Previous minimum | New minimum |
205+
| -------------------------------- | ------------------------------------ | ------------------------------------- |
206+
| Manual instrumentation | `sentry-android-sqlite` `6.21.0` | `sentry-android-sqlite` `8.45.0` |
207+
| Auto-instrumentation<sup>†</sup> | Sentry Android Gradle Plugin `3.11.0` | Sentry Android Gradle Plugin `6.13.0` |
208+
209+
<small>
210+
† SAGP installs `sentry-android-sqlite` transitively when your project depends
211+
on `androidx.sqlite:sqlite`, so you typically don't need to add it. But if your
212+
project explicitly pins `sentry-android-sqlite`, be sure to bump it to `8.45.0+`.
213+
</small>
214+
215+
Replace `SentrySupportSQLiteOpenHelper.create(openHelper)` with `SentrySQLiteDriver.create(driver)`– and you're all set! (See [Manual Instrumentation → Room](#room) for the full call-site context.)
216+
217+
**Note**: Due to underlying API differences, `SQLiteDriver` and `SupportSQLiteOpenHelper` spans can sometimes differ. The driver's spans are tailored to work performed by the database itself during statement execution, while the open helper may also track time spent on statement preparation and work your app performs consuming native SQLite output.
218+
146219
## Verify
147220

148-
Assuming you have the following (reduced) code snippet performing a database query on a Room Dao:
221+
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](https://sentry.io).
149222

150-
```kotlin
151-
import android.os.Bundle
152-
import android.widget.Button
153-
import androidx.activity.ComponentActivity
154-
import androidx.room.Database
155-
import androidx.room.Dao
156-
import androidx.room.Insert
157-
import androidx.room.OnConflictStrategy
158-
import androidx.room.RoomDatabase
223+
```kotlin {tabTitle:SQLiteDriver} {mdExpandTabs}
159224
import io.sentry.Sentry
160225
import io.sentry.SpanStatus
161-
import kotlinx.coroutines.withContext
226+
import io.sentry.TransactionOptions
162227

163-
@Dao
164-
abstract class TracksDao {
165-
@Insert(onConflict = OnConflictStrategy.REPLACE)
166-
abstract suspend fun insert(track: Track): Long
167-
}
168-
169-
@Database(
170-
entities = [Track::class],
171-
version = 1,
172-
exportSchema = false
228+
// `driver` is your SentrySQLiteDriver-wrapped instance (see Configure section above).
229+
val transaction = Sentry.startTransaction(
230+
"DB Smoke Test",
231+
"db.query",
232+
TransactionOptions().apply { isBindToScope = true },
173233
)
174-
abstract class TracksDatabase : RoomDatabase() {
175-
abstract fun tracksDao(): TracksDao
234+
235+
driver.open(":memory:").use { connection ->
236+
connection.prepare("SELECT 1").use { it.step() }
176237
}
177238

178-
class EditActivity : ComponentActivity() {
179-
private lateinit var database: TracksDatabase
239+
transaction.finish(SpanStatus.OK)
240+
```
180241

181-
override fun onCreate(savedInstanceState: Bundle?) {
182-
super.onCreate(savedInstanceState)
183-
database = TODO("initialize database...")
242+
```kotlin {tabTitle:SupportSQLiteOpenHelper}
243+
import io.sentry.Sentry
244+
import io.sentry.SpanStatus
245+
import io.sentry.TransactionOptions
184246

185-
findViewById<Button>(R.id.editTrack).setOnClickListener {
186-
val transaction = Sentry.startTransaction(
187-
name = "Track Interaction",
188-
operation = "ui.action.edit",
189-
bindToScope = true
190-
)
247+
// `openHelper` is your SentrySupportSQLiteOpenHelper-wrapped instance (see Configure section above).
248+
val transaction = Sentry.startTransaction(
249+
"DB Smoke Test",
250+
"db.query",
251+
TransactionOptions().apply { isBindToScope = true },
252+
)
191253

192-
val newTrack = Track(/* fill in track values */)
254+
openHelper.writableDatabase.query("SELECT 1").use { it.moveToFirst() }
193255

194-
withContext(Dispatchers.IO) {
195-
database.tracksDao().insert(newTrack)
196-
transaction.finish(SpanStatus.OK)
197-
}
198-
}
199-
}
200-
}
256+
transaction.finish(SpanStatus.OK)
201257
```
202258

203-
To view the recorded transaction, log into [sentry.io](https://sentry.io) and open your project. Clicking on **Performance** will open a page with transactions, where you can select the just recorded transaction with the name `Track Interaction`. The event will look similar to this:
204-
205-
![Room and AndroidX SQLite performance instrumentation](./img/room-sqlite-instrumentation.png)
259+
To view the recorded transaction, log into [sentry.io](https://sentry.io) and open the [Traces](https://sentry.io/orgredirect/organizations/:orgslug/traces/) page. Filter by `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.
206260

207261
<Alert>
208262

209-
The Sentry Android Gradle plugin will report SQL queries for any `SupportSQLiteOpenHelper.Factory`, starting with version `3.11.0`.
263+
Sentry captures SQL strings as span descriptions. If you execute SQL with values interpolated directly into the query string (for example, `db.query("SELECT * FROM users WHERE id = $id")`), those values will be sent to Sentry. Prefer parameterized queries – `query(sql, bindArgs)`, `execSQL(sql, bindArgs)`, or Room `@Query` placeholders – so only the SQL skeleton is captured. See [Scrubbing Sensitive Data](/platforms/android/data-management/sensitive-data/) for additional controls.
210264

211-
Earlier versions will only support standard `androidx.room` usage and won't report SQL queries for any `SupportSQLiteOpenHelper.Factory` other than [androidx.sqlite](https://github.com/androidx/androidx/tree/androidx-main/sqlite).
265+
</Alert>
212266

213-
If you're having trouble with this SDK, we want to hear about it. Create an [issue on GitHub](https://github.com/getsentry/sentry-android-gradle-plugin/issues) and describe your experience.
267+
## Room DAO Methods
214268

215-
</Alert>
269+
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.
216270

217-
<Alert>
271+
DAO methods are instrumented via bytecode manipulation. We don't currently support manual instrumentation.
218272

219-
If you are directly using [SupportSQLiteDatabase#query](<https://developer.android.com/reference/androidx/sqlite/db/SupportSQLiteDatabase#query(java.lang.String)>) or [SupportSQLiteDatabase#execSQL](<https://developer.android.com/reference/androidx/sqlite/db/SupportSQLiteDatabase#execSQL(java.lang.String)>) methods through the Room's SQLiteOpenHelper, consider switching to their alternatives that accept `bindArgs` as a second parameter.
273+
<Alert>
220274

221-
Because Sentry captures SQL queries as Span description, there is a risk of leaking sensitive data when not using an SQL string with placeholders.
275+
**Known limitation:** DAO auto-instrumentation doesn't work on Room `≥2.7.0` (including Room `≥3.0.0`) due to internal API changes, although you'll still see SQL-level spans from the underlying `SQLiteDriver` or `SupportSQLiteOpenHelper` wrapper. (See [#1304](https://github.com/getsentry/sentry-android-gradle-plugin/issues/1304).)
222276

223277
</Alert>

0 commit comments

Comments
 (0)