Add transaction trees for opening a room so we can have a nice trace view
This commit is contained in:
committed by
Jorge Martin Espinosa
parent
2a011bf072
commit
93feed38bf
@@ -12,6 +12,7 @@ import dev.zacsweers.metro.ContributesBinding
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.ColdStartUntilCachedRoomList
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.catch
|
||||
@@ -44,7 +45,7 @@ class DefaultAnalyticsColdStartWatcher(
|
||||
if (hasConsent) {
|
||||
if (isColdStart.get()) {
|
||||
Timber.d("Starting cold start check")
|
||||
analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStartUntilCachedRoomList)
|
||||
analyticsService.startLongRunningTransaction(ColdStartUntilCachedRoomList)
|
||||
} else {
|
||||
error("The app is no longer in a cold start state")
|
||||
}
|
||||
@@ -56,6 +57,7 @@ class DefaultAnalyticsColdStartWatcher(
|
||||
|
||||
override fun whenLoggingIn() {
|
||||
if (isColdStart.getAndSet(false)) {
|
||||
analyticsService.removeLongRunningTransaction(ColdStartUntilCachedRoomList)
|
||||
Timber.d("Canceled cold start check: user is logging in")
|
||||
}
|
||||
}
|
||||
@@ -63,7 +65,7 @@ class DefaultAnalyticsColdStartWatcher(
|
||||
override fun onRoomListVisible() {
|
||||
if (isColdStart.getAndSet(false)) {
|
||||
Timber.d("Room list is visible, finishing cold start check")
|
||||
analyticsService.removeLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStartUntilCachedRoomList)?.finish()
|
||||
analyticsService.removeLongRunningTransaction(ColdStartUntilCachedRoomList)?.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,9 @@ import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
|
||||
import io.element.android.libraries.matrix.ui.room.LoadingRoomState
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.LoadJoinedRoomFlow
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.NotificationTapOpensTimeline
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.OpenRoom
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
@@ -111,7 +113,9 @@ class RoomFlowNode(
|
||||
|
||||
override fun onBuilt() {
|
||||
super.onBuilt()
|
||||
analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.OpenRoom)
|
||||
val parentTransaction = analyticsService.getLongRunningTransaction(NotificationTapOpensTimeline)
|
||||
val openRoomTransaction = analyticsService.startLongRunningTransaction(OpenRoom, parentTransaction)
|
||||
analyticsService.startLongRunningTransaction(LoadJoinedRoomFlow, openRoomTransaction)
|
||||
resolveRoomId()
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,10 @@ import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.LoadJoinedRoomFlow
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.LoadMessagesUi
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.OpenRoom
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import io.element.android.services.appnavstate.api.ActiveRoomsHolder
|
||||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -66,6 +70,7 @@ class JoinedRoomLoadedFlowNode(
|
||||
private val sessionCoroutineScope: CoroutineScope,
|
||||
private val matrixClient: MatrixClient,
|
||||
private val activeRoomsHolder: ActiveRoomsHolder,
|
||||
private val analyticsService: AnalyticsService,
|
||||
roomGraphFactory: RoomGraphFactory,
|
||||
) : BaseFlowNode<JoinedRoomLoadedFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
@@ -93,6 +98,8 @@ class JoinedRoomLoadedFlowNode(
|
||||
init {
|
||||
lifecycle.subscribe(
|
||||
onCreate = {
|
||||
val parent = analyticsService.getLongRunningTransaction(OpenRoom)
|
||||
analyticsService.startLongRunningTransaction(LoadMessagesUi, parent)
|
||||
Timber.v("OnCreate => ${inputs.room.roomId}")
|
||||
appNavigationStateService.onNavigateToRoom(id, inputs.room.roomId)
|
||||
activeRoomsHolder.addRoom(inputs.room)
|
||||
@@ -100,6 +107,7 @@ class JoinedRoomLoadedFlowNode(
|
||||
trackVisitedRoom()
|
||||
},
|
||||
onResume = {
|
||||
analyticsService.removeLongRunningTransaction(LoadJoinedRoomFlow)?.finish()
|
||||
sessionCoroutineScope.launch {
|
||||
inputs.room.subscribeToSync()
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
|
||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.services.appnavstate.api.ActiveRoomsHolder
|
||||
import io.element.android.services.appnavstate.impl.DefaultActiveRoomsHolder
|
||||
import io.element.android.services.appnavstate.test.FakeAppNavigationStateService
|
||||
@@ -123,6 +124,7 @@ class JoinedRoomLoadedFlowNodeTest {
|
||||
roomGraphFactory = FakeRoomGraphFactory(),
|
||||
matrixClient = matrixClient,
|
||||
activeRoomsHolder = activeRoomsHolder,
|
||||
analyticsService = FakeAnalyticsService(),
|
||||
)
|
||||
|
||||
@Test
|
||||
|
||||
@@ -68,6 +68,7 @@ import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
||||
import io.element.android.libraries.mediaplayer.api.MediaPlayer
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.LoadMessagesUi
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
@@ -136,6 +137,9 @@ class MessagesNode(
|
||||
onCreate = {
|
||||
sessionCoroutineScope.launch { analyticsService.capture(room.toAnalyticsViewRoom()) }
|
||||
},
|
||||
onResume = {
|
||||
analyticsService.removeLongRunningTransaction(LoadMessagesUi)?.finish()
|
||||
},
|
||||
onDestroy = {
|
||||
mediaPlayer.close()
|
||||
}
|
||||
|
||||
@@ -55,7 +55,9 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin
|
||||
import io.element.android.libraries.matrix.ui.room.canSendMessageAsState
|
||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.DisplayFirstTimelineItems
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.NotificationTapOpensTimeline
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.OpenRoom
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
@@ -111,6 +113,11 @@ class TimelinePresenter(
|
||||
|
||||
@Composable
|
||||
override fun present(): TimelineState {
|
||||
LaunchedEffect(Unit) {
|
||||
val parent = analyticsService.getLongRunningTransaction(OpenRoom)
|
||||
analyticsService.startLongRunningTransaction(DisplayFirstTimelineItems, parent)
|
||||
}
|
||||
|
||||
val localScope = rememberCoroutineScope()
|
||||
|
||||
val timelineMode = remember { timelineController.mainTimelineMode() }
|
||||
@@ -228,18 +235,26 @@ class TimelinePresenter(
|
||||
LaunchedEffect(Unit) {
|
||||
timelineItemsFactory.timelineItems
|
||||
.onEach { newTimelineItems ->
|
||||
analyticsService.removeLongRunningTransaction(AnalyticsLongRunningTransaction.NotificationTapOpensTimeline)?.finish()
|
||||
analyticsService.removeLongRunningTransaction(AnalyticsLongRunningTransaction.OpenRoom)?.finish()
|
||||
timelineItemIndexer.process(newTimelineItems)
|
||||
timelineItems = newTimelineItems
|
||||
|
||||
analyticsService.run {
|
||||
removeLongRunningTransaction(DisplayFirstTimelineItems)?.finish()
|
||||
removeLongRunningTransaction(OpenRoom)?.finish()
|
||||
removeLongRunningTransaction(NotificationTapOpensTimeline)?.finish()
|
||||
}
|
||||
}
|
||||
.launchIn(this)
|
||||
|
||||
combine(timelineController.timelineItems(), room.membersStateFlow) { items, membersState ->
|
||||
val parent = analyticsService.getLongRunningTransaction(DisplayFirstTimelineItems)
|
||||
val transaction = parent?.startChild("timelineItemsFactory.replaceWith", "Processing timeline items")
|
||||
transaction?.setData("items", items.count())
|
||||
timelineItemsFactory.replaceWith(
|
||||
timelineItems = items,
|
||||
roomMembers = membersState.roomMembers().orEmpty()
|
||||
)
|
||||
transaction?.finish()
|
||||
items
|
||||
}
|
||||
.onEach(redactedVoiceMessageManager::onEachMatrixTimelineItem)
|
||||
|
||||
@@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.roomlist.awaitLoaded
|
||||
import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewInfoMapper
|
||||
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import io.element.android.services.analytics.api.recordTransaction
|
||||
import io.element.android.services.analyticsproviders.api.recordChildTransaction
|
||||
@@ -113,10 +114,13 @@ class RustRoomFactory(
|
||||
|
||||
val sdkRoom = awaitRoomInRoomList(roomId) ?: return@withLock null
|
||||
|
||||
val parentTransaction = analyticsService.getLongRunningTransaction(AnalyticsLongRunningTransaction.OpenRoom)
|
||||
|
||||
if (sdkRoom.membership() == Membership.JOINED) {
|
||||
analyticsService.recordTransaction(
|
||||
name = "Get joined room",
|
||||
operation = "RustRoomFactory.getJoinedRoomOrPreview",
|
||||
parentTransaction = parentTransaction,
|
||||
) { transaction ->
|
||||
val hideThreadedEvents = featureFlagService.isFeatureEnabled(FeatureFlags.Threads)
|
||||
// Init the live timeline in the SDK from the Room
|
||||
@@ -136,9 +140,10 @@ class RustRoomFactory(
|
||||
)
|
||||
}
|
||||
|
||||
val baseRoom = transaction.recordChildTransaction(operation = "getBaseRoom", description = "Get room from SDK") { getBaseRoom(sdkRoom) }
|
||||
GetRoomResult.Joined(
|
||||
JoinedRustRoom(
|
||||
baseRoom = getBaseRoom(sdkRoom),
|
||||
baseRoom = baseRoom,
|
||||
notificationSettingsService = notificationSettingsService,
|
||||
roomContentForwarder = roomContentForwarder,
|
||||
liveInnerTimeline = timeline,
|
||||
@@ -152,6 +157,7 @@ class RustRoomFactory(
|
||||
analyticsService.recordTransaction(
|
||||
name = "Get preview of room",
|
||||
operation = "RustRoomFactory.getJoinedRoomOrPreview",
|
||||
parentTransaction = parentTransaction,
|
||||
) {
|
||||
val preview = try {
|
||||
sdkRoom.previewRoom(via = serverNames)
|
||||
|
||||
@@ -16,4 +16,7 @@ sealed class AnalyticsLongRunningTransaction(
|
||||
data object ResumeAppUntilNewRoomsReceived : AnalyticsLongRunningTransaction("App was resumed and new room list items arrived", null)
|
||||
data object NotificationTapOpensTimeline : AnalyticsLongRunningTransaction("A notification was tapped and it opened a timeline", null)
|
||||
data object OpenRoom : AnalyticsLongRunningTransaction("Open a room and see loaded items in the timeline", null)
|
||||
data object LoadJoinedRoomFlow : AnalyticsLongRunningTransaction("Load joined room UI", "ui.load")
|
||||
data object LoadMessagesUi : AnalyticsLongRunningTransaction("Load messages UI", "ui.load")
|
||||
data object DisplayFirstTimelineItems : AnalyticsLongRunningTransaction("Get and display first timeline items", null)
|
||||
}
|
||||
|
||||
@@ -58,7 +58,10 @@ interface AnalyticsService : AnalyticsTracker, ErrorTracker {
|
||||
/**
|
||||
* Starts an [AnalyticsLongRunningTransaction], that can be shared with other components.
|
||||
*/
|
||||
fun startLongRunningTransaction(longRunningTransaction: AnalyticsLongRunningTransaction): AnalyticsTransaction
|
||||
fun startLongRunningTransaction(
|
||||
longRunningTransaction: AnalyticsLongRunningTransaction,
|
||||
parentTransaction: AnalyticsTransaction? = null
|
||||
): AnalyticsTransaction
|
||||
|
||||
/**
|
||||
* Gets an ongoing [AnalyticsLongRunningTransaction], if it exists.
|
||||
@@ -71,8 +74,14 @@ interface AnalyticsService : AnalyticsTracker, ErrorTracker {
|
||||
fun removeLongRunningTransaction(longRunningTransaction: AnalyticsLongRunningTransaction): AnalyticsTransaction?
|
||||
}
|
||||
|
||||
inline fun <T> AnalyticsService.recordTransaction(name: String, operation: String, block: (AnalyticsTransaction) -> T): T {
|
||||
val transaction = startTransaction(name, operation)
|
||||
inline fun <T> AnalyticsService.recordTransaction(
|
||||
name: String,
|
||||
operation: String,
|
||||
parentTransaction: AnalyticsTransaction? = null,
|
||||
block: (AnalyticsTransaction) -> T
|
||||
): T {
|
||||
val transaction = parentTransaction?.startChild(name, operation)
|
||||
?: startTransaction(name, operation)
|
||||
try {
|
||||
val result = block(transaction)
|
||||
return result
|
||||
|
||||
@@ -153,8 +153,13 @@ class DefaultAnalyticsService(
|
||||
} ?: NoopAnalyticsTransaction
|
||||
}
|
||||
|
||||
override fun startLongRunningTransaction(longRunningTransaction: AnalyticsLongRunningTransaction): AnalyticsTransaction {
|
||||
val transaction = startTransaction(longRunningTransaction.name, longRunningTransaction.operation)
|
||||
override fun startLongRunningTransaction(
|
||||
longRunningTransaction: AnalyticsLongRunningTransaction,
|
||||
parentTransaction: AnalyticsTransaction?,
|
||||
): AnalyticsTransaction {
|
||||
val transaction = parentTransaction?.startChild(longRunningTransaction.name, longRunningTransaction.operation)
|
||||
?: startTransaction(longRunningTransaction.name, longRunningTransaction.operation)
|
||||
|
||||
pendingLongRunningTransactions[longRunningTransaction] = transaction
|
||||
return transaction
|
||||
}
|
||||
|
||||
@@ -39,7 +39,10 @@ class NoopAnalyticsService : AnalyticsService {
|
||||
override fun trackError(throwable: Throwable) = Unit
|
||||
override fun updateSuperProperties(updatedProperties: SuperProperties) = Unit
|
||||
override fun startTransaction(name: String, operation: String?): AnalyticsTransaction = NoopAnalyticsTransaction
|
||||
override fun startLongRunningTransaction(longRunningTransaction: AnalyticsLongRunningTransaction): AnalyticsTransaction = NoopAnalyticsTransaction
|
||||
override fun startLongRunningTransaction(
|
||||
longRunningTransaction: AnalyticsLongRunningTransaction,
|
||||
parentTransaction: AnalyticsTransaction?,
|
||||
): AnalyticsTransaction = NoopAnalyticsTransaction
|
||||
override fun getLongRunningTransaction(longRunningTransaction: AnalyticsLongRunningTransaction): AnalyticsTransaction? = null
|
||||
override fun removeLongRunningTransaction(longRunningTransaction: AnalyticsLongRunningTransaction) = NoopAnalyticsTransaction
|
||||
}
|
||||
|
||||
@@ -71,7 +71,10 @@ class FakeAnalyticsService(
|
||||
}
|
||||
|
||||
override fun startTransaction(name: String, operation: String?): AnalyticsTransaction = NoopAnalyticsTransaction
|
||||
override fun startLongRunningTransaction(longRunningTransaction: AnalyticsLongRunningTransaction): AnalyticsTransaction {
|
||||
override fun startLongRunningTransaction(
|
||||
longRunningTransaction: AnalyticsLongRunningTransaction,
|
||||
parentTransaction: AnalyticsTransaction?
|
||||
): AnalyticsTransaction {
|
||||
longRunningTransactions[longRunningTransaction] = NoopAnalyticsTransaction
|
||||
return NoopAnalyticsTransaction
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ package io.element.android.services.analyticsproviders.sentry
|
||||
|
||||
import io.element.android.services.analyticsproviders.api.AnalyticsTransaction
|
||||
import io.sentry.ISpan
|
||||
import io.sentry.ITransaction
|
||||
import io.sentry.Sentry
|
||||
import timber.log.Timber
|
||||
|
||||
class SentryAnalyticsTransaction private constructor(span: ISpan) : AnalyticsTransaction {
|
||||
constructor(name: String, operation: String?) : this(Sentry.startTransaction(name, operation.orEmpty()))
|
||||
@@ -20,5 +22,9 @@ class SentryAnalyticsTransaction private constructor(span: ISpan) : AnalyticsTra
|
||||
)
|
||||
override fun setData(key: String, value: Any) = inner.setData(key, value)
|
||||
override fun isFinished(): Boolean = inner.isFinished
|
||||
override fun finish() = inner.finish()
|
||||
override fun finish() {
|
||||
val name = if (inner is ITransaction) inner.name else inner.operation
|
||||
Timber.d("Finishing transaction: $name")
|
||||
inner.finish()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user