Jetpack Compose
Learn more about the Sentry Compose integration.
The sentry-compose-android
library provides Jetpack Compose support in the following areas:
@Composable
performance metrics- Transaction and breadcrumb support for user interactions (clicks, swipes and scroll gestures)
- Transaction and breadcrumb support for navigation events
- Support for View Hierarchies
(New in version 6.16.0)
With Jetpack Compose performance metrics the Sentry Android SDK can automatically measure the initial composition
and rendering
time as performance spans of your @Composable
UI elements.
This feature requires an underlying transaction to attach it's spans to. Transactions can be created manually or automatically by configuring
Once performance metrics have been enabled, you'll see two spans inside your transactions:
ui.compose.composition
, describing the time it took to execute your@Composable
ui.compose.rendering
, describing the time it took to render your@Composable
to the canvas
The SentryTraced
function also provides an enableUserInteractionTracing
argument, which automatically applies a Modifier.sentryTag()
and enables the User Interactions feature mentioned below.
To add the Jetpack Compose integration, install the Android SDK, then add the sentry-compose-android
dependency using Gradle:
implementation 'io.sentry:sentry-android:7.19.1'
implementation 'io.sentry:sentry-compose-android:7.19.1'
Once installed, you need to wrap your @Composable
body with the SentryTraced
function.
import io.sentry.compose.SentryTraced
@Composable
fun LoginScreen() {
SentryTraced("login_screen") {
Column {
// ...
Button(
onClick = { TODO() }) {
Text(text = "Login")
}
}
}
}
(New in version 6.10.0)
The Sentry User Interactions feature for Jetpack Compose can detect click, scroll, and swipe gestures. For every gesture, the SDK can automatically collect breadcrumbs and launch transactions.
To add the Jetpack Compose integration, install the Android SDK, then add the sentry-compose-android
dependency using Gradle:
implementation 'io.sentry:sentry-android:7.19.1'
implementation 'io.sentry:sentry-compose-android:7.19.1'
This feature is disabled by default, but you can enable it the following ways.
AndroidManifest.xml
<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>
If you initialize the SDK manually as mentioned here, you can enable user interactions like this:
MyApplication.kt
SentryAndroid.init(this) { options ->
// ...
options.isEnableUserInteractionTracing = true
options.isEnableUserInteractionBreadcrumbs = true
}
The Sentry SDK requires you to have either Modifier.sentryTag("<tag>")
or Modifier.testTag("<tag>")
applied on each @Composable
you want to capture to determine its identifier. Here's an example:
import io.sentry.compose.sentryTag
@Composable
fun LoginScreen() {
Column {
// ...
Button(
modifier = Modifier.sentryTag("button_login"),
onClick = { TODO() }) {
Text(text = "Login")
}
}
}
If a @Composable
doesn't have a .sentryTag
or a .testTag
modifier applied, both breadcrumbs and transactions won't be captured because they can't be uniquely identified. To automatically generate tags, refer to the docs about our Sentry Kotlin Compiler Plugin.
If you want to customize the recorded breadcrumbs/transactions, you can skip to the section below.
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.
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
:
plugins {
id "io.sentry.android.gradle" version "4.14.1"
}
Learn more about the Sentry Android Gradle plugin in our Gradle documentation. Details about auto-installation can be found in the Auto Instrumentation section.
If you want to disable the Jetpack Compose instrumentation feature, you can adapt your build.gradle(.kts)
file like so:
import io.sentry.android.gradle.extensions.InstrumentationFeature
sentry {
tracingInstrumentation {
enabled = true
features = EnumSet.allOf(InstrumentationFeature) - InstrumentationFeature.COMPOSE
}
}
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:
implementation 'io.sentry:sentry-android:7.19.1'
implementation 'io.sentry:sentry-compose-android:7.19.1'
Configuration should happen in the respective @Composable
function, once you obtain an instance of NavHostController
:
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.
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:
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")
}
}
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:
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
}
})
}
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").