diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e0aff05910..a0519385fd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,6 +45,7 @@ jobs: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} + ELEMENT_ANDROID_SENTRY_DSN: ${{ secrets.ELEMENT_ANDROID_SENTRY_DSN }} run: ./gradlew :app:assembleGplayDebug app:assembleFDroidDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES - name: Upload debug APKs if: ${{ matrix.variant == 'debug' }} diff --git a/.github/workflows/build_enterprise.yml b/.github/workflows/build_enterprise.yml index 4370c75d0c..9182a9af3c 100644 --- a/.github/workflows/build_enterprise.yml +++ b/.github/workflows/build_enterprise.yml @@ -53,6 +53,7 @@ jobs: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} + ELEMENT_ANDROID_SENTRY_DSN: ${{ secrets.ELEMENT_ANDROID_SENTRY_DSN }} run: ./gradlew :app:assembleGplayDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES - name: Upload debug Enterprise APKs if: ${{ matrix.variant == 'debug' }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 3ad597e658..465a95bd08 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -29,6 +29,7 @@ jobs: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} + ELEMENT_ANDROID_SENTRY_DSN: ${{ secrets.ELEMENT_ANDROID_SENTRY_DSN }} ELEMENT_ANDROID_NIGHTLY_KEYID: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_KEYID }} ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD }} ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD }} diff --git a/.github/workflows/nightly_enterprise.yml b/.github/workflows/nightly_enterprise.yml index 7d12ac66a8..c7700ed56b 100644 --- a/.github/workflows/nightly_enterprise.yml +++ b/.github/workflows/nightly_enterprise.yml @@ -35,6 +35,7 @@ jobs: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} + ELEMENT_ANDROID_SENTRY_DSN: ${{ secrets.ELEMENT_ANDROID_SENTRY_DSN }} ELEMENT_ANDROID_NIGHTLY_KEYID: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_KEYID }} ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD }} ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 99ee377c00..e9de267d3f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,6 +31,7 @@ jobs: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} + ELEMENT_ANDROID_SENTRY_DSN: ${{ secrets.ELEMENT_ANDROID_SENTRY_DSN }} run: ./gradlew bundleGplayRelease $CI_GRADLE_ARG_PROPERTIES - name: Upload bundle as artifact uses: actions/upload-artifact@v4 diff --git a/docs/analytics.md b/docs/analytics.md index b3f592c227..3113b94650 100644 --- a/docs/analytics.md +++ b/docs/analytics.md @@ -2,10 +2,16 @@ -* [TODO](#todo) +* [Sentry](#sentry) -## TODO +## Sentry -There is no analytics in the project yet. +To make Sentry analytics and bug reporting work, you need to provide a Sentry DSN in the `local.properties` file, or set the `ELEMENT_ANDROID_SENTRY_DSN` environment variable. + +The format used to add the DSN to your `local.properties` file is the following: + +```properties +services.analyticsproviders.sentry.dsn=https://your-sentry-dsn/project-id +``` diff --git a/features/location/api/build.gradle.kts b/features/location/api/build.gradle.kts index 2384632ec3..998529088a 100644 --- a/features/location/api/build.gradle.kts +++ b/features/location/api/build.gradle.kts @@ -5,20 +5,13 @@ * Please see LICENSE files in the repository root for full details. */ -import java.util.Properties +import extension.readLocalProperty plugins { id("io.element.android-compose-library") id("kotlin-parcelize") } -fun readLocalProperty(name: String): String? = Properties().apply { - try { - load(rootProject.file("local.properties").reader()) - } catch (ignored: java.io.IOException) { - } -}.getProperty(name) - android { namespace = "io.element.android.features.location.api" diff --git a/plugins/src/main/kotlin/extension/Utils.kt b/plugins/src/main/kotlin/extension/Utils.kt index 61be232f6c..4d19505704 100644 --- a/plugins/src/main/kotlin/extension/Utils.kt +++ b/plugins/src/main/kotlin/extension/Utils.kt @@ -13,6 +13,7 @@ import org.gradle.api.provider.ValueSourceParameters import org.gradle.process.ExecOperations import java.io.ByteArrayOutputStream import java.io.IOException +import java.util.Properties import javax.inject.Inject abstract class GitRevisionValueSource : ValueSource { @@ -47,3 +48,10 @@ private fun ExecOperations.runCommand(cmd: String): String { } return String(outputStream.toByteArray()).trim() } + +fun Project.readLocalProperty(name: String): String? = Properties().apply { + try { + load(rootProject.file("local.properties").reader()) + } catch (ignored: IOException) { + } +}.getProperty(name) diff --git a/services/analyticsproviders/sentry/build.gradle.kts b/services/analyticsproviders/sentry/build.gradle.kts index c8deb34a7a..e144cb67ea 100644 --- a/services/analyticsproviders/sentry/build.gradle.kts +++ b/services/analyticsproviders/sentry/build.gradle.kts @@ -1,3 +1,4 @@ +import extension.readLocalProperty import extension.setupAnvil /* @@ -12,6 +13,21 @@ plugins { android { namespace = "io.element.android.services.analyticsproviders.sentry" + + buildFeatures { + buildConfig = true + } + + defaultConfig { + buildConfigField( + type = "String", + name = "SENTRY_DSN", + value = (System.getenv("ELEMENT_ANDROID_SENTRY_DSN") + ?: readLocalProperty("services.analyticsproviders.sentry.dsn") + ?: "" + ).let { "\"$it\"" } + ) + } } setupAnvil() diff --git a/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryAnalyticsProvider.kt b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryAnalyticsProvider.kt index ecf399416f..3445eba219 100644 --- a/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryAnalyticsProvider.kt +++ b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryAnalyticsProvider.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.di.ApplicationContext import io.element.android.services.analyticsproviders.api.AnalyticsProvider import io.element.android.services.analyticsproviders.sentry.log.analyticsTag import io.sentry.Sentry +import io.sentry.SentryLevel import io.sentry.SentryOptions import io.sentry.android.core.SentryAndroid import timber.log.Timber @@ -35,13 +36,20 @@ class SentryAnalyticsProvider @Inject constructor( override fun init() { Timber.tag(analyticsTag.value).d("Initializing Sentry") if (Sentry.isEnabled()) return + + val dsn = if (SentryConfig.DSN.isNotBlank()) { + SentryConfig.DSN + } else { + Timber.w("No Sentry DSN provided, Sentry will not be initialized") + return + } + SentryAndroid.init(context) { options -> - options.dsn = SentryConfig.DNS + options.dsn = dsn options.beforeSend = SentryOptions.BeforeSendCallback { event, _ -> event } options.tracesSampleRate = 1.0 options.isEnableUserInteractionTracing = true options.environment = buildMeta.buildType.toSentryEnv() - options.diagnosticLevel } } @@ -51,9 +59,11 @@ class SentryAnalyticsProvider @Inject constructor( } override fun capture(event: VectorAnalyticsEvent) { + Sentry.captureMessage("Event: ${event.getName()}", SentryLevel.INFO) } override fun screen(screen: VectorAnalyticsScreen) { + Sentry.captureMessage("Screen: ${screen.getName()}", SentryLevel.INFO) } override fun updateUserProperties(userProperties: UserProperties) { diff --git a/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt index a8002a67a6..39ade3f9ff 100644 --- a/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt +++ b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt @@ -9,7 +9,7 @@ package io.element.android.services.analyticsproviders.sentry object SentryConfig { const val NAME = "Sentry" - const val DNS = "https://32f7ff6a6e724f90838b7654042b2e81@sentry.tools.element.io/59" + const val DSN = BuildConfig.SENTRY_DSN const val ENV_DEBUG = "DEBUG" const val ENV_RELEASE = "RELEASE" }