From 6637313c6982f753d5ec391cac8ec73c91c6d95c Mon Sep 17 00:00:00 2001 From: Daniel Alome Date: Tue, 7 Jul 2026 22:32:05 +0100 Subject: [PATCH 1/2] ADFA-4397: Migrate crash and log reporting from Sentry to GlitchTip Point the Sentry Android SDK at GlitchTip instead of Sentry. The SDK, DSN format, and tooling stay the same; only the endpoint moves. The DSN comes from local.properties for local builds and from CI secrets for release builds, so nothing is hardcoded. - Forward the app's logback output to GlitchTip. WARN and above go to the Logs view; INFO and above ride along as breadcrumbs (breadcrumbs are free). - Tag each event with the device as the user so crashes can be filtered per user. - Drop Sentry features GlitchTip does not support: session replay and profiling (dependency plus manifest flags removed). - Keep event volume inside the 100k plan: error logs no longer become issues (real crashes are still captured), and performance transactions are disabled. Logs bill at 0.1 and share the same event pool. --- app/build.gradle.kts | 2 +- app/src/main/AndroidManifest.xml | 18 +---------- .../app/DeviceProtectedApplicationLoader.kt | 30 +++++++++++++++---- gradle/libs.versions.toml | 2 +- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 363b5d9720..2965d78d30 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -357,7 +357,7 @@ dependencies { // Sentry Android SDK (core + replay for quality configuration) implementation(libs.sentry.core) implementation(libs.sentry.android.core) - implementation(libs.sentry.android.replay) + implementation(libs.sentry.logback) // Firebase Analytics implementation(platform(libs.firebase.bom)) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6d6b64d88a..f733790057 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -143,30 +143,14 @@ - - + android:value="0.0" /> - - - - - - - // Reduce replay quality to LOW to prevent OOM - // This reduces screenshot compression to 10 and bitrate to 50kbps - // (defaults to MEDIUM quality) - options.sessionReplay.quality = SentryReplayQuality.LOW options.environment = if (BuildConfig.DEBUG) IDEApplication.SENTRY_ENV_DEV else IDEApplication.SENTRY_ENV_PROD } + val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext + val sentryLogAppender = + SentryAppender().apply { + context = loggerContext + setMinimumEventLevel(Level.OFF) + setMinimumBreadcrumbLevel(Level.INFO) + setMinimumLevel(Level.WARN) + start() + } + loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).addAppender(sentryLogAppender) + + Sentry.setUser( + User().apply { + id = Settings.Secure.getString(app.contentResolver, Settings.Secure.ANDROID_ID) + username = "${Build.MANUFACTURER} ${Build.MODEL}" + }, + ) + ShizukuSettings.initialize() EventBus diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d8e60c15da..0450dc5b28 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -147,7 +147,7 @@ firebase-analytics = { module = "com.google.firebase:firebase-analytics-ktx" } # Sentry sentry-core = { module = "io.sentry:sentry", version.ref = "sentry" } sentry-android-core = { module = "io.sentry:sentry-android-core", version.ref = "sentry" } -sentry-android-replay = { module = "io.sentry:sentry-android-replay", version.ref = "sentry" } +sentry-logback = { module = "io.sentry:sentry-logback", version.ref = "sentry" } # AndroidIDE androidide-ts = { module = "com.itsaky.androidide.treesitter:android-tree-sitter", version.ref = "tree-sitter" } From 25f35adb3c28617688ec4c4d40717c080ab1b6f5 Mon Sep 17 00:00:00 2001 From: Daniel Alome Date: Wed, 8 Jul 2026 20:42:06 +0100 Subject: [PATCH 2/2] ADFA-4397: Consolidate to a single GLITCHTIP_DSN and guard reporting init - Read one glitchtipDsn / GLITCHTIP_DSN for both debug and release builds and drop the unused SENTRY_ORG, SENTRY_PROJECT, and SENTRY_AUTH_TOKEN. - Wrap SentryAndroid.init, the logback appender setup, and Sentry.setUser in runCatching so a failure is logged and direct-boot startup still runs ShizukuSettings and the EventBus. --- .github/workflows/analyze.yml | 4 +- .github/workflows/debug.yml | 2 +- .github/workflows/release.yml | 5 +-- app/build.gradle.kts | 8 ++-- .../app/DeviceProtectedApplicationLoader.kt | 44 ++++++++++--------- 5 files changed, 32 insertions(+), 31 deletions(-) diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml index 31a89cf4c3..0e87601811 100644 --- a/.github/workflows/analyze.yml +++ b/.github/workflows/analyze.yml @@ -79,7 +79,7 @@ jobs: # Firebase config). Scoped to this step so SonarCloud and other # third-party actions never see them. FIREBASE_CONSOLE_URL: ${{ secrets.FIREBASE_CONSOLE_URL }} - SENTRY_DSN_DEBUG: ${{ secrets.SENTRY_DSN_DEBUG }} + GLITCHTIP_DSN: ${{ secrets.GLITCHTIP_DSN }} run: | echo "gradle_time_start=$(date +%s)" >> $GITHUB_ENV flox activate -d flox/base -- ./gradlew :app:assembleV8Debug --no-daemon @@ -104,7 +104,7 @@ jobs: # The Gradle build also drives Sentry/Firebase configuration during # the unit-test compile path. FIREBASE_CONSOLE_URL: ${{ secrets.FIREBASE_CONSOLE_URL }} - SENTRY_DSN_DEBUG: ${{ secrets.SENTRY_DSN_DEBUG }} + GLITCHTIP_DSN: ${{ secrets.GLITCHTIP_DSN }} run: flox activate -d flox/base -- ./gradlew :testing:tooling:assemble :testing:common:assemble sonarqube --info --no-build-cache -x lint --continue - name: Upload JaCoCo report diff --git a/.github/workflows/debug.yml b/.github/workflows/debug.yml index 533e19b597..79e5cbd78f 100644 --- a/.github/workflows/debug.yml +++ b/.github/workflows/debug.yml @@ -27,7 +27,7 @@ env: ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.MVN_SIGNING_KEY_ID }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MVN_SIGNING_KEY_PASSWORD }} FIREBASE_CONSOLE_URL: ${{ secrets.FIREBASE_CONSOLE_URL }} - SENTRY_DSN_DEBUG: ${{ secrets.SENTRY_DSN_DEBUG }} + GLITCHTIP_DSN: ${{ secrets.GLITCHTIP_DSN }} jobs: check_changes: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d4879e51f5..73d8770afa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,10 +25,7 @@ env: ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.MVN_SIGNING_KEY_ID }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MVN_SIGNING_KEY_PASSWORD }} FIREBASE_CONSOLE_URL: ${{ secrets.FIREBASE_CONSOLE_URL }} - SENTRY_DSN_RELEASE: ${{ secrets.SENTRY_DSN_RELEASE }} - SENTRY_ORG: ${{ secrets.SENTRY_ORG }} - SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + GLITCHTIP_DSN: ${{ secrets.GLITCHTIP_DSN }} jobs: merge_stage_to_main: diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2965d78d30..bb53bd9b5a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -83,6 +83,8 @@ val props = if (file.exists()) load(file.inputStream()) } +val glitchtipDsn = props.getProperty("glitchtipDsn") ?: propOrEnv("GLITCHTIP_DSN") + apply { plugin(AndroidIDEAssetsPlugin::class.java) } @@ -114,12 +116,10 @@ android { buildTypes { debug { signingConfig = signingConfigs.getByName("debug") - manifestPlaceholders["sentryDsn"] = - props.getProperty("sentryDsnDebug") ?: propOrEnv("SENTRY_DSN_DEBUG") + manifestPlaceholders["sentryDsn"] = glitchtipDsn } release { - manifestPlaceholders["sentryDsn"] = - props.getProperty("sentryDsnRelease") ?: propOrEnv("SENTRY_DSN_RELEASE") + manifestPlaceholders["sentryDsn"] = glitchtipDsn } } diff --git a/app/src/main/java/com/itsaky/androidide/app/DeviceProtectedApplicationLoader.kt b/app/src/main/java/com/itsaky/androidide/app/DeviceProtectedApplicationLoader.kt index b31c11d02b..231748902f 100644 --- a/app/src/main/java/com/itsaky/androidide/app/DeviceProtectedApplicationLoader.kt +++ b/app/src/main/java/com/itsaky/androidide/app/DeviceProtectedApplicationLoader.kt @@ -72,28 +72,32 @@ internal object DeviceProtectedApplicationLoader : ), ) - SentryAndroid.init(app) { options -> - options.environment = - if (BuildConfig.DEBUG) IDEApplication.SENTRY_ENV_DEV else IDEApplication.SENTRY_ENV_PROD - } - - val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext - val sentryLogAppender = - SentryAppender().apply { - context = loggerContext - setMinimumEventLevel(Level.OFF) - setMinimumBreadcrumbLevel(Level.INFO) - setMinimumLevel(Level.WARN) - start() + runCatching { + SentryAndroid.init(app) { options -> + options.environment = + if (BuildConfig.DEBUG) IDEApplication.SENTRY_ENV_DEV else IDEApplication.SENTRY_ENV_PROD } - loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).addAppender(sentryLogAppender) - Sentry.setUser( - User().apply { - id = Settings.Secure.getString(app.contentResolver, Settings.Secure.ANDROID_ID) - username = "${Build.MANUFACTURER} ${Build.MODEL}" - }, - ) + val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext + val sentryLogAppender = + SentryAppender().apply { + context = loggerContext + setMinimumEventLevel(Level.OFF) + setMinimumBreadcrumbLevel(Level.INFO) + setMinimumLevel(Level.WARN) + start() + } + loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).addAppender(sentryLogAppender) + + Sentry.setUser( + User().apply { + id = Settings.Secure.getString(app.contentResolver, Settings.Secure.ANDROID_ID) + username = "${Build.MANUFACTURER} ${Build.MODEL}" + }, + ) + }.onFailure { + logger.error("Failed to initialize crash and log reporting", it) + } ShizukuSettings.initialize()