Allow bridging Sentry spans to the SDK ones.

Add distributed tracing for `Room.timelineWithConfiguration`, so we can inspect the associated Rust trace.
This commit is contained in:
Jorge Martín 2025-11-21 17:20:12 +01:00 committed by Jorge Martin Espinosa
parent 6c404fda36
commit bd427735ff
15 changed files with 195 additions and 10 deletions

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.impl.analytics
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.services.analytics.api.AnalyticsSdkSpan
import io.element.android.services.analytics.api.AnalyticsSdkSpanFactory
@ContributesBinding(AppScope::class)
class DefaultAnalyticsSdkFactory : AnalyticsSdkSpanFactory {
override fun create(name: String, parentTraceId: String?): AnalyticsSdkSpan {
return RustAnalyticsSdkSpan(name = name, parentTraceId = parentTraceId)
}
override fun bridge(parentTraceId: String?): AnalyticsSdkSpan {
// A bridge span has no name
return RustAnalyticsSdkSpan(name = null, parentTraceId = parentTraceId)
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.impl.analytics
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.services.analytics.api.AnalyticsSdkSpan
import kotlinx.coroutines.DelicateCoroutinesApi
import org.matrix.rustcomponents.sdk.LogLevel
import org.matrix.rustcomponents.sdk.Span
import timber.log.Timber
class RustAnalyticsSdkSpan(
name: String? = null,
private val parentTraceId: String?,
) : AnalyticsSdkSpan {
private val inner = if (name != null) {
Span(
target = "elementx",
name = name,
file = "-",
line = null,
level = LogLevel.WARN,
bridgeTraceId = parentTraceId,
)
} else {
Span.newBridgeSpan(
target = "elementx",
parentTraceId = parentTraceId,
)
}
override fun enter() {
if (Span.current().isNone()) {
inner.enter()
} else {
Timber.w("Not entering span sentry.trace='$parentTraceId' because another span is already active")
}
}
@OptIn(DelicateCoroutinesApi::class)
override fun exit() {
inner.exit()
runCatchingExceptions { inner.destroy() }
Timber.d("Exited span sentry.trace='$parentTraceId'")
}
}

View file

@ -25,6 +25,7 @@ import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewInfoMapp
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.inBridgeSdkSpan
import io.element.android.services.analytics.api.recordTransaction
import io.element.android.services.analyticsproviders.api.recordChildTransaction
import io.element.android.services.toolbox.api.systemclock.SystemClock
@ -127,17 +128,19 @@ class RustRoomFactory(
val timeline = transaction.recordChildTransaction(
operation = "sdkRoom.timelineWithConfiguration",
description = "Get timeline from the SDK",
) {
sdkRoom.timelineWithConfiguration(
TimelineConfiguration(
focus = TimelineFocus.Live(hideThreadedEvents = hideThreadedEvents),
filter = eventFilters?.let(TimelineFilter::EventTypeFilter) ?: TimelineFilter.All,
internalIdPrefix = "live",
dateDividerMode = DateDividerMode.DAILY,
trackReadReceipts = TimelineReadReceiptTracking.ALL_EVENTS,
reportUtds = true,
) { timelineTransaction ->
analyticsService.inBridgeSdkSpan(parentTraceId = timelineTransaction.traceId()) {
sdkRoom.timelineWithConfiguration(
TimelineConfiguration(
focus = TimelineFocus.Live(hideThreadedEvents = hideThreadedEvents),
filter = eventFilters?.let(TimelineFilter::EventTypeFilter) ?: TimelineFilter.All,
internalIdPrefix = "live",
dateDividerMode = DateDividerMode.DAILY,
trackReadReceipts = TimelineReadReceiptTracking.ALL_EVENTS,
reportUtds = true,
)
)
)
}
}
GetRoomResult.Joined(