Skip to content

Commit

Permalink
Merge 83e5a6f into 184d40a
Browse files Browse the repository at this point in the history
  • Loading branch information
markushi authored Feb 27, 2023
2 parents 184d40a + 83e5a6f commit f0108ba
Show file tree
Hide file tree
Showing 18 changed files with 805 additions and 249 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- If set to `false` performance is disabled, regardless of `tracesSampleRate` and `tracesSampler` options.
- Detect dependencies by listing MANIFEST.MF files at runtime ([#2538](https://github.com/getsentry/sentry-java/pull/2538))
- Report integrations in use, report packages in use more consistently ([#2179](https://github.com/getsentry/sentry-java/pull/2179))
- Add capabilities to track Jetpack Compose composition/rendering time ([#2507](https://github.com/getsentry/sentry-java/pull/2507))

### Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ class ActivityLifecycleIntegrationTest {

fun getSut(apiVersion: Int = 29, importance: Int = RunningAppProcessInfo.IMPORTANCE_FOREGROUND): ActivityLifecycleIntegration {
whenever(hub.options).thenReturn(options)
transaction = SentryTracer(context, hub, true, transactionFinishedCallback)
val transactionOptions = TransactionOptions().apply {
isWaitForChildren = true
}
transaction = SentryTracer(context, hub, transactionOptions, transactionFinishedCallback)
whenever(hub.startTransaction(any(), any<TransactionOptions>())).thenReturn(transaction)
whenever(buildInfo.sdkInfoVersion).thenReturn(apiVersion)

Expand Down
4 changes: 4 additions & 0 deletions sentry-compose/api/android/sentry-compose.api
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ public final class io/sentry/compose/BuildConfig {
public fun <init> ()V
}

public final class io/sentry/compose/SentryComposeTracingKt {
public static final fun SentryTraced (Ljava/lang/String;Landroidx/compose/ui/Modifier;ZLkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V
}

public final class io/sentry/compose/SentryNavigationIntegrationKt {
public static final fun withSentryObservableEffect (Landroidx/navigation/NavHostController;ZZLandroidx/compose/runtime/Composer;II)Landroidx/navigation/NavHostController;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package io.sentry.compose

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.platform.testTag
import io.sentry.ISpan
import io.sentry.Sentry
import io.sentry.SpanOptions

private const val OP_PARENT_COMPOSITION = "ui.compose.composition"
private const val OP_COMPOSE = "ui.compose"

private const val OP_PARENT_RENDER = "ui.compose.rendering"
private const val OP_RENDER = "ui.render"

@Immutable
private class ImmutableHolder<T>(var item: T)

private fun getRootSpan(): ISpan? {
var rootSpan: ISpan? = null
Sentry.configureScope {
rootSpan = it.transaction
}
return rootSpan
}

private val localSentryCompositionParentSpan = compositionLocalOf {
ImmutableHolder(
getRootSpan()
?.startChild(
OP_PARENT_COMPOSITION,
null,
SpanOptions().apply {
isTrimStart = true
isTrimEnd = true
isIdle = true
}
)
)
}

private val localSentryRenderingParentSpan = compositionLocalOf {
ImmutableHolder(
getRootSpan()
?.startChild(
OP_PARENT_RENDER,
null,
SpanOptions().apply {
isTrimStart = true
isTrimEnd = true
isIdle = true
}
)
)
}

@ExperimentalComposeUiApi
@Composable
public fun SentryTraced(
tag: String,
modifier: Modifier = Modifier,
enableUserInteractionTracing: Boolean = true,
content: @Composable BoxScope.() -> Unit
) {
val parentCompositionSpan = localSentryCompositionParentSpan.current
val parentRenderingSpan = localSentryRenderingParentSpan.current
val compositionSpan = parentCompositionSpan.item?.startChild(OP_COMPOSE, tag)
val firstRendered = remember { ImmutableHolder(false) }

val baseModifier = if (enableUserInteractionTracing) modifier.testTag(tag) else modifier

Box(
modifier = baseModifier
.drawWithContent {
val renderSpan = if (!firstRendered.item) {
parentRenderingSpan.item?.startChild(
OP_RENDER,
tag
)
} else {
null
}
drawContent()
firstRendered.item = true
renderSpan?.finish()
}
) {
content()
}
compositionSpan?.finish()
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:OptIn(ExperimentalComposeUiApi::class)

package io.sentry.samples.android.compose

import android.os.Bundle
Expand All @@ -18,6 +20,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.input.TextFieldValue
Expand All @@ -28,7 +31,7 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import io.sentry.Sentry
import io.sentry.compose.SentryTraced
import io.sentry.compose.withSentryObservableEffect
import io.sentry.samples.android.GithubAPI
import kotlinx.coroutines.launch
Expand All @@ -43,48 +46,45 @@ class ComposeActivity : ComponentActivity() {
SampleNavigation(navController)
}
}

override fun onResume() {
super.onResume()
Sentry.getSpan()?.finish()
}
}

@Composable
fun Landing(
navigateGithub: () -> Unit,
navigateGithubWithArgs: () -> Unit
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
) {
Button(
onClick = {
navigateGithub()
},
modifier = Modifier
.testTag("button_nav_github")
.padding(top = 32.dp)
SentryTraced(tag = "buttons_page") {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
) {
Text("Navigate to Github Page")
}
Button(
onClick = { navigateGithubWithArgs() },
modifier = Modifier
.testTag("button_nav_github_args")
.padding(top = 32.dp)
) {
Text("Navigate to Github Page With Args")
}
Button(
onClick = { throw RuntimeException("Crash from Compose") },
modifier = Modifier
.testTag("button_crash")
.padding(top = 32.dp)
) {
Text("Crash from Compose")
SentryTraced(tag = "button_nav_github") {
Button(
onClick = {
navigateGithub()
},
modifier = Modifier.padding(top = 32.dp)
) {
Text("Navigate to Github")
}
}
SentryTraced(tag = "button_nav_github_args") {
Button(
onClick = { navigateGithubWithArgs() },
modifier = Modifier.padding(top = 32.dp)
) {
Text("Navigate to Github Page With Args")
}
}
SentryTraced(tag = "button_crash") {
Button(
onClick = { throw RuntimeException("Crash from Compose") },
modifier = Modifier.padding(top = 32.dp)
) {
Text("Crash from Compose")
}
}
}
}
}
Expand All @@ -102,59 +102,67 @@ fun Github(
result = GithubAPI.service.listReposAsync(user.text, perPage).random().full_name
}

Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
) {
TextField(
value = user,
onValueChange = { newText ->
user = newText
}
)
Text("Random repo $result")
Button(
onClick = {
scope.launch {
result = GithubAPI.service.listReposAsync(user.text, perPage).random().full_name
}
},
SentryTraced("github-$user") {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.testTag("button_list_repos_async")
.padding(top = 32.dp)
.fillMaxSize()
) {
Text("Make Request")
TextField(
value = user,
onValueChange = { newText ->
user = newText
}
)
Text("Random repo $result")
Button(
onClick = {
scope.launch {
result =
GithubAPI.service.listReposAsync(user.text, perPage).random().full_name
}
},
modifier = Modifier
.testTag("button_list_repos_async")
.padding(top = 32.dp)
) {
Text("Make Request")
}
}
}
}

@Composable
fun SampleNavigation(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = Destination.Landing.route
) {
composable(Destination.Landing.route) {
Landing(
navigateGithub = { navController.navigate("github") },
navigateGithubWithArgs = { navController.navigate("github/spotify?per_page=10") }
)
}
composable(Destination.Github.route) {
Github()
}
composable(
Destination.GithubWithArgs.route,
arguments = listOf(
navArgument(Destination.USER_ARG) { type = NavType.StringType },
navArgument(Destination.PER_PAGE_ARG) { type = NavType.IntType; defaultValue = 10 }
)
SentryTraced(tag = "navhost") {
NavHost(
navController = navController,
startDestination = Destination.Landing.route
) {
Github(
it.arguments?.getString(Destination.USER_ARG) ?: "getsentry",
it.arguments?.getInt(Destination.PER_PAGE_ARG) ?: 10
)
composable(Destination.Landing.route) {
Landing(
navigateGithub = { navController.navigate("github") },
navigateGithubWithArgs = { navController.navigate("github/spotify?per_page=10") }
)
}
composable(Destination.Github.route) {
Github()
}
composable(
Destination.GithubWithArgs.route,
arguments = listOf(
navArgument(Destination.USER_ARG) { type = NavType.StringType },
navArgument(Destination.PER_PAGE_ARG) {
type = NavType.IntType; defaultValue = 10
}
)
) {
Github(
it.arguments?.getString(Destination.USER_ARG) ?: "getsentry",
it.arguments?.getInt(Destination.PER_PAGE_ARG) ?: 10
)
}
}
}
}
Expand Down
Loading

0 comments on commit f0108ba

Please sign in to comment.