Jetpack Compose

The sentry-compose-android library provides Jetpack Compose support for the following interactions:

  • User interactions like clicks, swipes and scroll gestures
  • Jetpack Compose navigation

User Interactions

(New in version 6.10.0)

Installation

To add the Jetpack Compose integration, install the Android SDK, then add the sentry-compose-android dependency using Gradle:

Copied
implementation 'io.sentry:sentry-android:6.13.0'
implementation 'io.sentry:sentry-compose-android:6.13.0'

Configuration

This feature is disabled by default, but you can enable it the following ways.

Using AndroidManifest.xml

AndroidManifest.xml
Copied
<application>
    <meta-data android:name="io.sentry.traces.user-interaction.enable" android:value="true" />
    <meta-data android:name="io.sentry.breadcrumbs.user-interaction" android:value="true" />
</application>

Using SentryOptions

If you initialize the SDK manually as mentioned here, you can enable user interactions like this:

MyApplication.kt
Copied
SentryAndroid.init(this) { options ->
    // ...
    options.isEnableUserInteractionTracing = true
    options.isEnableUserInteractionBreadcrumbs = true
}

The Sentry SDK utilizes the built-in Modifier.testTag("<tag>") of a Composable to determine its identifier. Here's an example:

Copied
import androidx.compose.ui.platform.testTag
@Composable
fun LoginScreen() {
  Column {
    // ...
    Button(
        modifier = Modifier.testTag("button_login"),
        onClick = { TODO() }) {
        Text(text = "Login")
    }
  }
}

Jetpack Compose Navigation

In this section, we get you up and running with Sentry's Compose Navigation Integration, so that it will automatically add a breadcrumb and start a transaction for each navigation event.

Auto-Installation With the Sentry Android Gradle Plugin

Install

If you're using the Sentry Android Gradle Plugin, the Jetpack Compose Navigation Integration is automatically applied, and both breadcrumbs and transactions are automatically created for every navigation event.

Add the Sentry Android Gradle plugin in build.gradle:

Copied
buildscript {
  repositories {
    mavenCentral()
  }
}

plugins {
  id "io.sentry.android.gradle" version "3.4.2"
}

Disable

If you want to disable the Jetpack Compose instrumentation feature, you can adapt your build.gradle(.kts) file like so:

Copied
import io.sentry.android.gradle.extensions.InstrumentationFeature

sentry {
  tracingInstrumentation {
    enabled = true
    features = EnumSet.allOf(InstrumentationFeature) - InstrumentationFeature.COMPOSE
  }
}

Manual Installation

Install

Sentry captures data by adding a withSentryObservableEffect extension function to NavHostController. To add the Navigation integration, install the Android SDK, then add the sentry-compose dependency using Gradle:

Copied
implementation 'io.sentry:sentry-android:6.13.0'
implementation 'io.sentry:sentry-compose-android:6.13.0'

Configure

Configuration should happen in the respective @Composable function, once you obtain an instance of NavHostController:

Copied
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import io.sentry.compose.withSentryObservableEffect

val navController = rememberNavController().withSentryObservableEffect(
  enableNavigationBreadcrumbs = true, // enabled by default
  enableNavigationTracing = true  // enabled by default
)
NavHost(
  navController = navController
) {
  ...
}

By default, the navigation transaction finishes automatically after it reaches the specified idleTimeout and all of its child spans are finished. You can customize the timeout to your needs.

Verify

This snippet includes a sample Activity with a couple of navigation events between composables and captures an intentional message, so you can test that everything is working as soon as you set it up:

Copied
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import io.sentry.Sentry
import io.sentry.compose.withSentryObservableEffect

class ComposeActivity : ComponentActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContent {
      val navController = rememberNavController().withSentryObservableEffect()
      SampleNavigation(navController)
    }
  }
}

@Composable
fun SampleNavigation(navController: NavHostController) {
  NavHost(
    navController = navController,
    startDestination = "first"
  ) {
    composable("first") {
      First(
        navigateToSecond = { navController.navigate("second") }
      )
    }
    composable("second") {
      Second()
    }
  }
}

@Composable
fun First(
  navigateToSecond: () -> Unit
) {
  Column(
    verticalArrangement = Arrangement.Center,
    horizontalAlignment = Alignment.CenterHorizontally,
    modifier = Modifier.fillMaxSize()
  ) {
    Button(
      onClick = { navigateToSecond() },
      modifier = Modifier.padding(top = 32.dp)
    ) {
      Text("Navigate to Second")
    }
    Button(
      onClick = { Sentry.captureMessage("Some message from Compose.") },
      modifier = Modifier.padding(top = 32.dp)
    ) {
      Text("Message from Compose")
    }
  }
}

@Composable
fun Second() {
  Column(
    verticalArrangement = Arrangement.Center,
    horizontalAlignment = Alignment.CenterHorizontally,
    modifier = Modifier.fillMaxSize()
  ) {
    Text("Second Screen")
  }
}

Customize the Recorded Breadcrumb/Transaction

By default, the Navigation integration captures route arguments as additional data on breadcrumbs and transactions. In case the arguments contain any PII data, you can strip it out by way of BeforeBreadcrumbCallback and EventProcessor respectively. To do that, manually initialize the SDK and add the following snippet:

Copied
import io.sentry.EventProcessor
import io.sentry.android.core.SentryAndroid
import io.sentry.SentryOptions.BeforeBreadcrumbCallback
import io.sentry.android.navigation.SentryNavigationListener

SentryAndroid.init(this) { options ->
  options.beforeBreadcrumb = BeforeBreadcrumbCallback { breadcrumb, hint ->
    if (SentryNavigationListener.NAVIGATION_OP == breadcrumb.category) {
      breadcrumb.data.remove("from_arguments")
      breadcrumb.data.remove("to_arguments")
    }
    breadcrumb
  }
  options.addEventProcessor(object : EventProcessor {
    override fun process(transaction: SentryTransaction, hint: Hint): SentryTransaction? {
      if (SentryNavigationListener.NAVIGATION_OP == transaction.contexts.trace.operation) {
        transaction.removeExtra("arguments")
      }
      return transaction
    }
  })
}
Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) to suggesting an update ("yeah, this would be better").