Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve telemetry on invalid view duration #2466

Merged
merged 1 commit into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1014,14 +1014,38 @@ internal open class RumViewScope(

private fun resolveViewDuration(event: RumRawEvent): Long {
val duration = event.eventTime.nanoTime - startedNanos
return if (duration <= 0) {
return if (duration == 0L) {
if (type == RumViewType.BACKGROUND && event is RumRawEvent.AddError && event.isFatal) {
// This is a legitimate empty duration, no-op
} else {
sdkCore.internalLogger.log(
InternalLogger.Level.WARN,
listOf(
InternalLogger.Target.USER,
InternalLogger.Target.TELEMETRY
),
{ ZERO_DURATION_WARNING_MESSAGE.format(Locale.US, key.name) },
null,
false,
mapOf("view.name" to key.name)
)
}
1
} else if (duration < 0) {
sdkCore.internalLogger.log(
InternalLogger.Level.WARN,
listOf(
InternalLogger.Target.USER,
InternalLogger.Target.TELEMETRY
),
{ NEGATIVE_DURATION_WARNING_MESSAGE.format(Locale.US, key.name) }
{ NEGATIVE_DURATION_WARNING_MESSAGE.format(Locale.US, key.name) },
null,
false,
mapOf(
"view.start_ns" to startedNanos,
"view.end_ns" to event.eventTime.nanoTime,
"view.name" to key.name
)
)
1
} else {
Expand Down Expand Up @@ -1350,8 +1374,10 @@ internal open class RumViewScope(

internal val FROZEN_FRAME_THRESHOLD_NS = TimeUnit.MILLISECONDS.toNanos(700)
internal const val SLOW_RENDERED_THRESHOLD_FPS = 55
internal const val ZERO_DURATION_WARNING_MESSAGE = "The computed duration for the " +
"view: %s was 0. In order to keep the view we forced it to 1ns."
internal const val NEGATIVE_DURATION_WARNING_MESSAGE = "The computed duration for the " +
"view: %s was 0 or negative. In order to keep the view we forced it to 1ns."
"view: %s was negative. In order to keep the view we forced it to 1ns."
internal const val NO_ACTIVE_VIEW_FOR_LOADING_TIME_WARNING_MESSAGE =
"No active view found to add the loading time."
internal const val ADDING_VIEW_LOADING_TIME_DEBUG_MESSAGE_FORMAT =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7536,7 +7536,7 @@ internal class RumViewScopeTest {

// endregion

// region InteractionToNextViewTime
// region InteractionToNextViewTime

@Test
fun `M notify the interactionToNextViewMetricResolver W view was created`() {
Expand Down Expand Up @@ -7584,9 +7584,9 @@ internal class RumViewScopeTest {
verify(mockInteractionToNextViewMetricResolver, never()).onActionSent(any())
}

// endregion
// endregion

// region Vitals
// region Vitals

@Test
fun `M send View update W onVitalUpdate()+handleEvent(KeepAlive) {CPU}`(
Expand Down Expand Up @@ -8200,9 +8200,9 @@ internal class RumViewScopeTest {
verify(mockFrameRateVitalMonitor).unregister(testedScope.frameRateVitalListener)
}

// endregion
// endregion

// region Cross-platform performance metrics
// region Cross-platform performance metrics

@Test
fun `M send update W handleEvent(UpdatePerformanceMetric+KeepAlive) { FlutterBuildTime }`(
Expand Down Expand Up @@ -8387,9 +8387,9 @@ internal class RumViewScopeTest {
assertThat(result).isSameAs(testedScope)
}

// endregion
// endregion

// region Feature Flags
// region Feature Flags

@Test
fun `M send event W handleEvent(AddFeatureFlagEvaluation) on active view`(
Expand Down Expand Up @@ -8529,9 +8529,9 @@ internal class RumViewScopeTest {
}
}

// endregion
// endregion

// region Feature Flags Batch
// region Feature Flags Batch

@Test
fun `M send event W handleEvent(AddFeatureFlagEvaluations) on active view`(
Expand Down Expand Up @@ -8688,9 +8688,9 @@ internal class RumViewScopeTest {
}
}

// endregion
// endregion

// region Stopping Sessions
// region Stopping Sessions

@Test
fun `M set view to inactive and send update W handleEvent { StopSession }`() {
Expand All @@ -8716,9 +8716,9 @@ internal class RumViewScopeTest {
}
}

// endregion
// endregion

// region write notification
// region write notification

@Test
fun `M notify about success W handleEvent(AddError+non-fatal) { write succeeded }`(
Expand Down Expand Up @@ -9088,13 +9088,59 @@ internal class RumViewScopeTest {
.eventDropped(testedScope.viewId, StorageEvent.FrozenFrame)
}

// endregion
// endregion

// region Misc
// region Misc

@ParameterizedTest
@MethodSource("brokenTimeRawEventData")
fun `M update the duration to 1ns W handleEvent { computed duration less or equal to 0 }`(
fun `M update the duration to 1ns W handleEvent { computed duration equal to 0 }`(
rawEventData: RumRawEventData
) {
// Given
testedScope = RumViewScope(
mockParentScope,
rumMonitor.mockSdkCore,
mockSessionEndedMetricDispatcher,
rawEventData.viewKey,
rawEventData.event.eventTime,
fakeAttributes,
mockViewChangedListener,
mockResolver,
mockCpuVitalMonitor,
mockMemoryVitalMonitor,
mockFrameRateVitalMonitor,
mockFeaturesContextResolver,
trackFrustrations = fakeTrackFrustrations,
sampleRate = fakeSampleRate,
interactionToNextViewMetricResolver = mockInteractionToNextViewMetricResolver,
networkSettledMetricResolver = mockNetworkSettledMetricResolver
)

// When
testedScope.handleEvent(rawEventData.event, mockWriter)

// Then
argumentCaptor<ViewEvent> {
verify(mockWriter).write(eq(mockEventBatchWriter), capture(), eq(EventType.DEFAULT))
assertThat(firstValue)
.apply {
hasDuration(1)
}
}
mockInternalLogger.verifyLog(
InternalLogger.Level.WARN,
listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY),
RumViewScope.ZERO_DURATION_WARNING_MESSAGE.format(Locale.US, testedScope.key.name),
additionalProperties = mapOf(
"view.name" to rawEventData.viewKey.name
)
)
}

@ParameterizedTest
@MethodSource("brokenTimeRawEventData")
fun `M update the duration to 1ns W handleEvent { computed duration less than 0 }`(
rawEventData: RumRawEventData
) {
// Given
Expand Down Expand Up @@ -9131,13 +9177,18 @@ internal class RumViewScopeTest {
mockInternalLogger.verifyLog(
InternalLogger.Level.WARN,
listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY),
RumViewScope.NEGATIVE_DURATION_WARNING_MESSAGE.format(Locale.US, testedScope.key.name)
RumViewScope.NEGATIVE_DURATION_WARNING_MESSAGE.format(Locale.US, testedScope.key.name),
additionalProperties = mapOf(
"view.start_ns" to fakeEventTime.nanoTime,
"view.end_ns" to rawEventData.event.eventTime.nanoTime,
"view.name" to rawEventData.viewKey.name
)
)
}

// endregion
// endregion

// region Global Attributes
// region Global Attributes

@Test
fun `M update the global attributes W handleEvent(StopView)`(
Expand Down Expand Up @@ -9602,7 +9653,7 @@ internal class RumViewScopeTest {
assertThat(result).isNull()
}

// endregion
// endregion

@Test
fun `M produce event safe for serialization W handleEvent()`(
Expand Down Expand Up @@ -9680,7 +9731,7 @@ internal class RumViewScopeTest {
}
}

// region Internal
// region Internal

private fun mockEvent(): RumRawEvent {
val event: RumRawEvent = mock()
Expand Down Expand Up @@ -9717,7 +9768,7 @@ internal class RumViewScopeTest {
).thenReturn(fakeReplayRecordsCount)
}

// endregion
// endregion

data class RumRawEventData(val event: RumRawEvent, val viewKey: RumScopeKey)

Expand Down Expand Up @@ -9747,16 +9798,23 @@ internal class RumViewScopeTest {
),
RumRawEventData(
RumRawEvent.AddCustomTiming(
fakeName,
name = fakeName,
eventTime = eventTime
),
fakeKey
),
RumRawEventData(
RumRawEvent.StopView(
key = fakeKey,
attributes = emptyMap(),
eventTime = eventTime
),
fakeKey
),
RumRawEventData(RumRawEvent.StopView(fakeKey, emptyMap(), eventTime), fakeKey),
RumRawEventData(
RumRawEvent.StartView(
fakeKey,
emptyMap(),
key = fakeKey,
attributes = emptyMap(),
eventTime = eventTime
),
fakeKey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ class SampleApplication : Application() {
event.context?.additionalProperties?.put(ATTR_IS_MAPPED, true)
event
}
.trackBackgroundEvents(true)
.build()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,17 @@ class LogsForegroundService : Service() {
sendRumErrorPendingIntent
)
.addAction(
android.R.drawable.ic_menu_call,
R.drawable.ic_baseline_action_24,
"Send RUM Action",
sendRumActionPendingIntent
)
.addAction(
android.R.drawable.ic_media_play,
R.drawable.ic_baseline_start_res_24,
"Start RUM Resource",
startRumResourcePendingIntent
)
.addAction(
android.R.drawable.ic_media_pause,
R.drawable.ic_baseline_end_res_24,
"Stop RUM Resource",
stopRumResourcePendingIntent
)
Expand Down
16 changes: 16 additions & 0 deletions sample/kotlin/src/main/res/drawable/ic_baseline_action_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!--
~ Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
~ This product includes software developed at Datadog (https://www.datadoghq.com/).
~ Copyright 2016-Present Datadog, Inc.
-->

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M13 5C15.21 5 17 6.79 17 9C17 10.5 16.2 11.77 15 12.46V11.24C15.61 10.69 16 9.89 16 9C16 7.34 14.66 6 13 6S10 7.34 10 9C10 9.89 10.39 10.69 11 11.24V12.46C9.8 11.77 9 10.5 9 9C9 6.79 10.79 5 13 5M20 20.5C19.97 21.32 19.32 21.97 18.5 22H13C12.62 22 12.26 21.85 12 21.57L8 17.37L8.74 16.6C8.93 16.39 9.2 16.28 9.5 16.28H9.7L12 18V9C12 8.45 12.45 8 13 8S14 8.45 14 9V13.47L15.21 13.6L19.15 15.79C19.68 16.03 20 16.56 20 17.14V20.5M20 2H4C2.9 2 2 2.9 2 4V12C2 13.11 2.9 14 4 14H8V12L4 12L4 4H20L20 12H18V14H20V13.96L20.04 14C21.13 14 22 13.09 22 12V4C22 2.9 21.11 2 20 2Z"/>
</vector>
16 changes: 16 additions & 0 deletions sample/kotlin/src/main/res/drawable/ic_baseline_end_res_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!--
~ Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
~ This product includes software developed at Datadog (https://www.datadoghq.com/).
~ Copyright 2016-Present Datadog, Inc.
-->

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M14.59,6L12,8.59L9.41,6L8,7.41L10.59,10L8,12.59L9.41,14L12,11.41L14.59,14L16,12.59L13.41,10L16,7.41L14.59,6M17,3A2,2 0 0,1 19,5V15A2,2 0 0,1 17,17H13V19H14A1,1 0 0,1 15,20H22V22H15A1,1 0 0,1 14,23H10A1,1 0 0,1 9,22H2V20H9A1,1 0 0,1 10,19H11V17H7C5.89,17 5,16.1 5,15V5A2,2 0 0,1 7,3H17Z"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!--
~ Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
~ This product includes software developed at Datadog (https://www.datadoghq.com/).
~ Copyright 2016-Present Datadog, Inc.
-->

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M16,11V9H13V6H11V9H8V11H11V14H13V11H16M17,3A2,2 0 0,1 19,5V15A2,2 0 0,1 17,17H13V19H14A1,1 0 0,1 15,20H22V22H15A1,1 0 0,1 14,23H10A1,1 0 0,1 9,22H2V20H9A1,1 0 0,1 10,19H11V17H7C5.89,17 5,16.1 5,15V5A2,2 0 0,1 7,3H17Z"/>
</vector>