Skip to content

Commit d4ffab7

Browse files
committed
chore(android-sqlite): Add SQLite samples to sentry-samples-android
Adds our SQLite integrations to sentry-android-samples (`SentrySQLiteDriver` and `SentrySupportOpenSQLiteHelper`). The entry point is `SQLiteActivity`. Example SQL statements are identical across integrations so we can observe similarities / differences in how they handle spans. Users can exercise the integrations directly or via Room or SQLDelight.
1 parent f2207a5 commit d4ffab7

15 files changed

Lines changed: 1501 additions & 11 deletions

File tree

gradle/libs.versions.toml

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,42 +5,49 @@ androidxNavigation = "2.4.2"
55
androidxTestCore = "1.7.0"
66
androidxCompose = "1.6.3"
77
asyncProfiler = "4.2"
8+
camerax = "1.4.0"
89
composeCompiler = "1.5.14"
910
coroutines = "1.6.1"
1011
espresso = "3.7.0"
1112
feign = "11.6"
13+
gummyBears = "0.12.0"
1214
jacoco = "0.8.7"
1315
jackson = "2.18.3"
1416
jetbrainsCompose = "1.6.11"
1517
kotlin = "2.2.0"
1618
kotlinSpring7 = "2.2.0"
1719
kotlin-compatible-version = "1.9"
20+
ksp = "2.2.0-2.0.2"
1821
ktorClient = "3.0.0"
1922
logback = "1.2.9"
2023
log4j2 = "2.20.0"
2124
nopen = "1.0.1"
2225
# see https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-compatibility-and-versioning.html#kotlin-compatibility
2326
# see https://developer.android.com/jetpack/androidx/releases/compose-kotlin
2427
okhttp = "4.9.2"
28+
openfeature = "1.18.2"
2529
otel = "1.60.1"
2630
otelInstrumentation = "2.26.0"
2731
otelInstrumentationAlpha = "2.26.0-alpha"
2832
# check https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/dependencyManagement/build.gradle.kts#L49 for release version above to find a compatible version
2933
otelSemanticConventions = "1.40.0"
3034
otelSemanticConventionsAlpha = "1.40.0-alpha"
3135
retrofit = "2.9.0"
36+
room2 = "2.8.4"
37+
room3 = "3.0.0-alpha06"
38+
sqlite = "2.6.2"
39+
sqliteAlpha = "2.7.0-alpha06" # Required by Room3 3.0.0-alpha*
3240
slf4j = "1.7.30"
41+
spotless = "8.4.0"
3342
springboot2 = "2.7.18"
3443
springboot3 = "3.5.0"
3544
springboot4 = "4.0.0"
45+
sqldelight = "2.3.2"
46+
3647
# Android
3748
targetSdk = "36"
3849
compileSdk = "36"
3950
minSdk = "21"
40-
spotless = "8.4.0"
41-
gummyBears = "0.12.0"
42-
camerax = "1.4.0"
43-
openfeature = "1.18.2"
4451

4552
[plugins]
4653
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
@@ -50,6 +57,7 @@ kotlin-jvm-spring7 = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlinSpr
5057
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
5158
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
5259
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
60+
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
5361
buildconfig = { id = "com.github.gmazzo.buildconfig", version = "5.6.5" }
5462
dokka = { id = "org.jetbrains.dokka", version = "2.0.0" }
5563
dokka-javadoc = { id = "org.jetbrains.dokka-javadoc", version = "2.0.0" }
@@ -64,6 +72,7 @@ vanniktech-maven-publish = { id = "com.vanniktech.maven.publish", version = "0.3
6472
springboot3 = { id = "org.springframework.boot", version.ref = "springboot3" }
6573
springboot4 = { id = "org.springframework.boot", version.ref = "springboot4" }
6674
spring-dependency-management = { id = "io.spring.dependency-management", version = "1.1.7" }
75+
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
6776
gretty = { id = "org.gretty", version = "4.0.0" }
6877
animalsniffer = { id = "ru.vyarus.animalsniffer", version = "2.0.1" }
6978
sentry = { id = "io.sentry.android.gradle", version = "6.6.0"}
@@ -94,7 +103,14 @@ androidx-lifecycle-common-java8 = { module = "androidx.lifecycle:lifecycle-commo
94103
androidx-lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "androidxLifecycle" }
95104
androidx-navigation-runtime = { module = "androidx.navigation:navigation-runtime", version.ref = "androidxNavigation" }
96105
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidxNavigation" }
97-
androidx-sqlite = { module = "androidx.sqlite:sqlite", version = "2.6.2" }
106+
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room2" }
107+
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room2" }
108+
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room2" }
109+
androidx-room3-compiler = { module = "androidx.room3:room3-compiler", version.ref = "room3" }
110+
androidx-room3-runtime = { module = "androidx.room3:room3-runtime", version.ref = "room3" }
111+
androidx-sqlite = { module = "androidx.sqlite:sqlite", version.ref = "sqlite" }
112+
androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sqliteAlpha" }
113+
androidx-sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqliteAlpha" }
98114
androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version = "1.2.1" }
99115
androidx-browser = { module = "androidx.browser:browser", version = "1.8.0" }
100116
async-profiler = { module = "tools.profiler:async-profiler", version.ref = "asyncProfiler" }
@@ -207,6 +223,7 @@ springboot4-starter-jdbc = { module = "org.springframework.boot:spring-boot-star
207223
springboot4-starter-actuator = { module = "org.springframework.boot:spring-boot-starter-actuator", version.ref = "springboot4" }
208224
springboot4-starter-cache = { module = "org.springframework.boot:spring-boot-starter-cache", version.ref = "springboot4" }
209225
springboot4-starter-kafka = { module = "org.springframework.boot:spring-boot-starter-kafka", version.ref = "springboot4" }
226+
sqldelight-android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
210227
timber = { module = "com.jakewharton.timber:timber", version = "4.7.1" }
211228

212229
# Animalsniffer signature
@@ -249,3 +266,7 @@ msgpack = { module = "org.msgpack:msgpack-core", version = "0.9.8" }
249266
okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" }
250267
okio = { module = "com.squareup.okio:okio", version = "1.13.0" }
251268
roboelectric = { module = "org.robolectric:robolectric", version = "4.15" }
269+
270+
[bundles]
271+
androidx-room2 = ["androidx-room-runtime", "androidx-room-ktx"]
272+
androidx-sqlite-drivers = ["androidx-sqlite-bundled", "androidx-sqlite-framework"]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Sentry Sample Android App
2+
3+
Sample application demonstrating how to use the Sentry Android SDK, including core functionality (error reporting, tracing, session replay, profiling) and integrations (Compose, OkHttp, SQLDelight, etc.).
4+
5+
## How to run it?
6+
7+
Install the app on your device or emulator:
8+
9+
```
10+
./gradlew :sentry-samples:sentry-samples-android:installDebug
11+
```
12+
13+
or simply open the project in Android Studio and run the `sentry-samples-android` configuration.
14+
15+
## Viewing events locally
16+
17+
Debug builds enable SDK debug logging, so captured envelopes are printed to logcat (tag `Sentry`):
18+
19+
```
20+
adb logcat -s Sentry
21+
```
22+
23+
## Viewing events on Sentry UI
24+
25+
By default, events appear under the [sentry-sdk test project](https://sentry-sdks.sentry.io/issues/?project=5428559).
26+
To redirect them to your own project, replace the test DSN (i.e., the `io.sentry.dsn` `meta-data` value)
27+
in `src/main/AndroidManifest.xml` with your own.

sentry-samples/sentry-samples-android/build.gradle.kts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ plugins {
77
id("com.android.application")
88
alias(libs.plugins.kotlin.android)
99
alias(libs.plugins.kotlin.compose)
10+
alias(libs.plugins.ksp)
11+
alias(libs.plugins.sqldelight)
1012
}
1113

1214
android {
@@ -15,7 +17,8 @@ android {
1517

1618
defaultConfig {
1719
applicationId = "io.sentry.samples.android"
18-
minSdk = libs.versions.minSdk.get().toInt()
20+
// androidx.sqlite 2.6+ require minSdk 23; the Sentry SDK still supports 21.
21+
minSdk = 23
1922
targetSdk = libs.versions.targetSdk.get().toInt()
2023
versionCode = 2
2124
versionName = project.version.toString()
@@ -90,7 +93,13 @@ android {
9093
}
9194
}
9295

93-
kotlin { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 }
96+
// Java 11 b/c androidx.room3 requires it.
97+
compileOptions {
98+
sourceCompatibility = JavaVersion.VERSION_11
99+
targetCompatibility = JavaVersion.VERSION_11
100+
}
101+
102+
kotlin { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 }
94103

95104
androidComponents.beforeVariants {
96105
it.enable = !Config.Android.shouldSkipDebugVariant(it.buildType)
@@ -116,13 +125,25 @@ android {
116125
@Suppress("UnstableApiUsage") packagingOptions { jniLibs { useLegacyPackaging = true } }
117126
}
118127

128+
sqldelight {
129+
databases {
130+
create("SampleSQLDelightDatabase") {
131+
packageName.set("io.sentry.samples.android.sqlite")
132+
// Keep .sq files next to the hand-written Kotlin (src/main/java/.../sqlite) instead of the
133+
// default src/main/sqldelight source root.
134+
srcDirs("src/main/java")
135+
}
136+
}
137+
}
138+
119139
dependencies {
120140
implementation(
121141
kotlin(Config.kotlinStdLib, org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION)
122142
)
123143

124144
implementation(projects.sentryAndroid)
125145
implementation(projects.sentryAndroidFragment)
146+
implementation(projects.sentryAndroidSqlite)
126147
implementation(projects.sentryAndroidTimber)
127148
implementation(projects.sentryCompose)
128149
implementation(projects.sentryKotlinExtensions)
@@ -148,17 +169,24 @@ dependencies {
148169
implementation(libs.androidx.navigation.compose)
149170
implementation(libs.androidx.recyclerview)
150171
implementation(libs.androidx.browser)
172+
implementation(libs.androidx.room3.runtime)
173+
implementation(libs.bundles.androidx.room2)
174+
implementation(libs.bundles.androidx.sqlite.drivers)
175+
implementation(libs.camerax.camera2)
176+
implementation(libs.camerax.core)
177+
implementation(libs.camerax.lifecycle)
178+
implementation(libs.camerax.view)
151179
implementation(libs.coil.compose)
152180
implementation(libs.kotlinx.coroutines.android)
153181
implementation(libs.lottie.compose)
154182
implementation(libs.retrofit)
155183
implementation(libs.retrofit.gson)
156184
implementation(libs.sentry.native.ndk)
185+
implementation(libs.sqldelight.android.driver)
157186
implementation(libs.timber)
158-
implementation(libs.camerax.core)
159-
implementation(libs.camerax.camera2)
160-
implementation(libs.camerax.lifecycle)
161-
implementation(libs.camerax.view)
187+
188+
ksp(libs.androidx.room.compiler)
189+
ksp(libs.androidx.room3.compiler)
162190

163191
debugImplementation(projects.sentryAndroidDistribution)
164192
debugImplementation(libs.leakcanary)

sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@
9292
android:name=".TriggerHttpRequestActivity"
9393
android:exported="false" />
9494

95+
<activity
96+
android:name=".sqlite.SQLiteActivity"
97+
android:exported="false" />
98+
99+
<activity
100+
android:name=".sqlite.UiLoadActivity"
101+
android:exported="false" />
102+
95103
<!-- NOTE: Replace the test DSN below with YOUR OWN DSN to see the events from this app in your Sentry project/dashboard-->
96104
<meta-data
97105
android:name="io.sentry.dsn"

sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,20 @@ fun TracingScreen() {
473473
}
474474
}
475475
}
476+
item {
477+
SentryTraced("open_sqlite") {
478+
OutlinedButton(
479+
onClick = {
480+
activity.startActivity(
481+
Intent(activity, io.sentry.samples.android.sqlite.SQLiteActivity::class.java)
482+
)
483+
},
484+
modifier = Modifier,
485+
) {
486+
Text("Open SQLite Activity", maxLines = 2, overflow = TextOverflow.Ellipsis)
487+
}
488+
}
489+
}
476490
}
477491
}
478492

sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MyApplication.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import android.app.Application;
44
import android.os.StrictMode;
55
import io.sentry.Sentry;
6+
import io.sentry.samples.android.sqlite.SampleDatabases;
67

78
/** Apps. main Application. */
89
public class MyApplication extends Application {
@@ -13,6 +14,8 @@ public void onCreate() {
1314
strictMode();
1415
super.onCreate();
1516

17+
SampleDatabases.INSTANCE.warmUp(this);
18+
1619
// Example how to initialize the SDK manually which allows access to SentryOptions callbacks.
1720
// Make sure you disable the auto init via manifest meta-data: io.sentry.auto-init=false
1821
// SentryAndroid.init(
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package io.sentry.samples.android.sqlite
2+
3+
/**
4+
* Display text for each "SQL run" summary shown in the [SQLiteActivity] screen UI. Documentation
5+
* only / never executed. The real statements live in [SqlStatements].
6+
*/
7+
internal data class DisplayInfo(val sql: String, val sqlHeavy: String = sql)
8+
9+
internal val DRIVER_DIRECT =
10+
DisplayInfo(
11+
sql =
12+
"""
13+
CREATE TABLE IF NOT EXISTS song(…)
14+
INSERT INTO song(title, artist) VALUES (?, ?)
15+
SELECT count(*) FROM song
16+
"""
17+
.trimIndent(),
18+
sqlHeavy =
19+
"""
20+
CREATE TABLE IF NOT EXISTS song(…)
21+
INSERT INTO song(title, artist) VALUES (?, ?)
22+
INSERT INTO song(title, artist) VALUES (?, ?), (?, ?), … (?, ?)
23+
SELECT id, title, artist FROM song
24+
SELECT count(*) FROM song
25+
-- then, per row: appWork() = 500x SHA-256, in the app (not in any span)
26+
"""
27+
.trimIndent(),
28+
)
29+
30+
internal val DRIVER_ROOM2 =
31+
DisplayInfo(
32+
sql =
33+
"""
34+
INSERT OR ABORT INTO `song` (…) VALUES (nullif(?, 0), ?, ?)
35+
SELECT count(*) FROM song
36+
"""
37+
.trimIndent(),
38+
sqlHeavy =
39+
"""
40+
INSERT OR ABORT INTO `song` (…) VALUES (nullif(?, 0), ?, ?)
41+
SELECT * FROM song
42+
SELECT count(*) FROM song
43+
-- then, per row: appWork() = 500x SHA-256, outside the step()-timed spans
44+
"""
45+
.trimIndent(),
46+
)
47+
48+
// Room 3 issues the same statements as Room 2 (see SqlStatements.driverWithRoom3).
49+
internal val DRIVER_ROOM3 = DRIVER_ROOM2
50+
51+
internal val OPENHELPER_DIRECT =
52+
DisplayInfo(
53+
sql =
54+
"""
55+
CREATE TABLE IF NOT EXISTS song(…)
56+
INSERT INTO song(title, artist) VALUES (?, ?)
57+
SELECT count(*) FROM song
58+
"""
59+
.trimIndent(),
60+
sqlHeavy =
61+
"""
62+
CREATE TABLE IF NOT EXISTS song(…)
63+
INSERT INTO song(title, artist) VALUES (?, ?)
64+
INSERT INTO song(title, artist) VALUES (?, ?), (?, ?), … (?, ?)
65+
SELECT id, title, artist FROM song
66+
SELECT count(*) FROM song
67+
-- then, per row: appWork() = 500x SHA-256, in the app
68+
"""
69+
.trimIndent(),
70+
)
71+
72+
internal val OPENHELPER_ROOM =
73+
DisplayInfo(
74+
sql =
75+
"""
76+
INSERT OR ABORT INTO `song` (…) VALUES (nullif(?, 0), ?, ?)
77+
SELECT count(*) FROM song
78+
"""
79+
.trimIndent(),
80+
sqlHeavy =
81+
"""
82+
INSERT OR ABORT INTO `song` (…) VALUES (nullif(?, 0), ?, ?)
83+
SELECT * FROM song
84+
SELECT count(*) FROM song
85+
-- then, per row: appWork() = 500x SHA-256, in the app
86+
"""
87+
.trimIndent(),
88+
)
89+
90+
internal val OPENHELPER_SQLDELIGHT =
91+
DisplayInfo(
92+
sql =
93+
"""
94+
INSERT INTO song(title, artist) VALUES (?, ?)
95+
SELECT count(*) FROM song
96+
"""
97+
.trimIndent(),
98+
sqlHeavy =
99+
"""
100+
INSERT INTO song(title, artist) VALUES (?, ?)
101+
SELECT * FROM song
102+
SELECT count(*) FROM song
103+
-- then, per row: appWork() = 500x SHA-256, in the app
104+
"""
105+
.trimIndent(),
106+
)

0 commit comments

Comments
 (0)