Skip to content

Commit f6192aa

Browse files
authored
chore(android-sqlite): Add SQLite samples to sentry-samples-android (#5504)
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 10a0bc2 commit f6192aa

15 files changed

Lines changed: 1542 additions & 13 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.4"
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
jackson = "2.18.3"
1315
jetbrainsCompose = "1.6.11"
1416
kotlin = "2.2.0"
1517
kotlinSpring7 = "2.2.0"
1618
kotlin-compatible-version = "1.9"
19+
ksp = "2.3.9"
1720
ktorClient = "3.0.0"
1821
logback = "1.2.9"
1922
log4j2 = "2.20.0"
2023
nopen = "1.0.1"
2124
# see https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-compatibility-and-versioning.html#kotlin-compatibility
2225
# see https://developer.android.com/jetpack/androidx/releases/compose-kotlin
2326
okhttp = "4.9.2"
27+
openfeature = "1.18.2"
2428
otel = "1.60.1"
2529
otelInstrumentation = "2.26.0"
2630
otelInstrumentationAlpha = "2.26.0-alpha"
2731
# 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
2832
otelSemanticConventions = "1.40.0"
2933
otelSemanticConventionsAlpha = "1.40.0-alpha"
3034
retrofit = "2.9.0"
35+
room2 = "2.8.4"
36+
room3 = "3.0.0-alpha06"
3137
sagp = "6.10.0"
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" }
@@ -62,6 +70,7 @@ vanniktech-maven-publish = { id = "com.vanniktech.maven.publish", version = "0.3
6270
springboot3 = { id = "org.springframework.boot", version.ref = "springboot3" }
6371
springboot4 = { id = "org.springframework.boot", version.ref = "springboot4" }
6472
spring-dependency-management = { id = "io.spring.dependency-management", version = "1.1.7" }
73+
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
6574
gretty = { id = "org.gretty", version = "4.0.0" }
6675
animalsniffer = { id = "ru.vyarus.animalsniffer", version = "2.0.1" }
6776
sentry = { id = "io.sentry.android.gradle", version.ref = "sagp"}
@@ -92,7 +101,14 @@ androidx-lifecycle-common-java8 = { module = "androidx.lifecycle:lifecycle-commo
92101
androidx-lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "androidxLifecycle" }
93102
androidx-navigation-runtime = { module = "androidx.navigation:navigation-runtime", version.ref = "androidxNavigation" }
94103
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidxNavigation" }
95-
androidx-sqlite = { module = "androidx.sqlite:sqlite", version = "2.6.2" }
104+
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room2" }
105+
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room2" }
106+
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room2" }
107+
androidx-room3-compiler = { module = "androidx.room3:room3-compiler", version.ref = "room3" }
108+
androidx-room3-runtime = { module = "androidx.room3:room3-runtime", version.ref = "room3" }
109+
androidx-sqlite = { module = "androidx.sqlite:sqlite", version.ref = "sqlite" }
110+
androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sqliteAlpha" }
111+
androidx-sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqliteAlpha" }
96112
androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version = "1.2.1" }
97113
androidx-browser = { module = "androidx.browser:browser", version = "1.8.0" }
98114
async-profiler = { module = "tools.profiler:async-profiler", version.ref = "asyncProfiler" }
@@ -205,6 +221,7 @@ springboot4-starter-jdbc = { module = "org.springframework.boot:spring-boot-star
205221
springboot4-starter-actuator = { module = "org.springframework.boot:spring-boot-starter-actuator", version.ref = "springboot4" }
206222
springboot4-starter-cache = { module = "org.springframework.boot:spring-boot-starter-cache", version.ref = "springboot4" }
207223
springboot4-starter-kafka = { module = "org.springframework.boot:spring-boot-starter-kafka", version.ref = "springboot4" }
224+
sqldelight-android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
208225
timber = { module = "com.jakewharton.timber:timber", version = "4.7.1" }
209226

210227
# Animalsniffer signature
@@ -248,3 +265,7 @@ msgpack = { module = "org.msgpack:msgpack-core", version = "0.9.8" }
248265
okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" }
249266
okio = { module = "com.squareup.okio:okio", version = "1.13.0" }
250267
roboelectric = { module = "org.robolectric:robolectric", version = "4.15" }
268+
269+
[bundles]
270+
androidx-room2 = ["androidx-room-runtime", "androidx-room-ktx"]
271+
androidx-sqlite-drivers = ["androidx-sqlite-bundled", "androidx-sqlite-framework"]

sentry-samples/sentry-samples-android/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Sentry Sample Android App
22

33
Sample application demonstrating how to use the Sentry Android SDK, including core functionality (error reporting, tracing, session replay,
4-
profiling) and integrations (Compose, OkHttp, etc.).
4+
profiling) and integrations (Compose, OkHttp, SQLite, etc.).
55

66
## How to run it?
77

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

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ plugins {
99
id("com.android.application")
1010
alias(libs.plugins.kotlin.android)
1111
alias(libs.plugins.kotlin.compose)
12+
alias(libs.plugins.ksp)
1213
alias(libs.plugins.sentry) apply false
14+
alias(libs.plugins.sqldelight)
1315
}
1416

1517
if (providers.gradleProperty("useSagp").isPresent) {
@@ -26,9 +28,9 @@ plugins.withId("io.sentry.android.gradle") {
2628
tracingInstrumentation {
2729
features.set(
2830
setOf(
31+
// FILE_IO is disabled for non-SAGP builds.
2932
InstrumentationFeature.COMPOSE,
3033
InstrumentationFeature.DATABASE,
31-
InstrumentationFeature.FILE_IO,
3234
InstrumentationFeature.OKHTTP,
3335
)
3436
)
@@ -44,7 +46,8 @@ android {
4446

4547
defaultConfig {
4648
applicationId = "io.sentry.samples.android"
47-
minSdk = libs.versions.minSdk.get().toInt()
49+
// androidx.sqlite 2.6+ require minSdk 23; the Sentry SDK still supports 21.
50+
minSdk = 23
4851
targetSdk = libs.versions.targetSdk.get().toInt()
4952
versionCode = 2
5053
versionName = project.version.toString()
@@ -119,7 +122,13 @@ android {
119122
}
120123
}
121124

122-
kotlin { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 }
125+
// Java 11 b/c androidx.room3 requires it.
126+
compileOptions {
127+
sourceCompatibility = JavaVersion.VERSION_11
128+
targetCompatibility = JavaVersion.VERSION_11
129+
}
130+
131+
kotlin { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 }
123132

124133
androidComponents.beforeVariants {
125134
it.enable = !Config.Android.shouldSkipDebugVariant(it.buildType)
@@ -145,13 +154,25 @@ android {
145154
@Suppress("UnstableApiUsage") packagingOptions { jniLibs { useLegacyPackaging = true } }
146155
}
147156

157+
sqldelight {
158+
databases {
159+
create("SampleSQLDelightDatabase") {
160+
packageName.set("io.sentry.samples.android.sqlite")
161+
// Keep .sq files next to the hand-written Kotlin (src/main/java/.../sqlite) instead of the
162+
// default src/main/sqldelight source root.
163+
srcDirs("src/main/java")
164+
}
165+
}
166+
}
167+
148168
dependencies {
149169
implementation(
150170
kotlin(Config.kotlinStdLib, org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION)
151171
)
152172

153173
implementation(projects.sentryAndroid)
154174
implementation(projects.sentryAndroidFragment)
175+
implementation(projects.sentryAndroidSqlite)
155176
implementation(projects.sentryAndroidTimber)
156177
implementation(projects.sentryCompose)
157178
implementation(projects.sentryKotlinExtensions)
@@ -177,17 +198,24 @@ dependencies {
177198
implementation(libs.androidx.navigation.compose)
178199
implementation(libs.androidx.recyclerview)
179200
implementation(libs.androidx.browser)
201+
implementation(libs.androidx.room3.runtime)
202+
implementation(libs.bundles.androidx.room2)
203+
implementation(libs.bundles.androidx.sqlite.drivers)
204+
implementation(libs.camerax.camera2)
205+
implementation(libs.camerax.core)
206+
implementation(libs.camerax.lifecycle)
207+
implementation(libs.camerax.view)
180208
implementation(libs.coil.compose)
181209
implementation(libs.kotlinx.coroutines.android)
182210
implementation(libs.lottie.compose)
183211
implementation(libs.retrofit)
184212
implementation(libs.retrofit.gson)
185213
implementation(libs.sentry.native.ndk)
214+
implementation(libs.sqldelight.android.driver)
186215
implementation(libs.timber)
187-
implementation(libs.camerax.core)
188-
implementation(libs.camerax.camera2)
189-
implementation(libs.camerax.lifecycle)
190-
implementation(libs.camerax.view)
216+
217+
ksp(libs.androidx.room.compiler)
218+
ksp(libs.androidx.room3.compiler)
191219

192220
debugImplementation(projects.sentryAndroidDistribution)
193221
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
@@ -101,6 +101,14 @@
101101
android:name=".TriggerHttpRequestActivity"
102102
android:exported="false" />
103103

104+
<activity
105+
android:name=".sqlite.SQLiteActivity"
106+
android:exported="false" />
107+
108+
<activity
109+
android:name=".sqlite.UiLoadActivity"
110+
android:exported="false" />
111+
104112
<!-- NOTE: Replace the test DSN below with YOUR OWN DSN to see the events from this app in your Sentry project/dashboard-->
105113
<meta-data
106114
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 {
@@ -18,6 +19,8 @@ public void onCreate() {
1819
strictMode();
1920
super.onCreate();
2021

22+
SampleDatabases.INSTANCE.warmUp(this);
23+
2124
// Example how to initialize the SDK manually which allows access to SentryOptions callbacks.
2225
// Make sure you disable the auto init via manifest meta-data: io.sentry.auto-init=false
2326
// 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)