Skip to content

Commit

Permalink
Merge branch 'develop' into yl/merge-develop-into-release-2-17-0
Browse files Browse the repository at this point in the history
  • Loading branch information
ambushwork committed Jan 2, 2025
2 parents 39f41f1 + c29bc1f commit ba2a443
Show file tree
Hide file tree
Showing 18 changed files with 647 additions and 112 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ object AndroidConfig {
const val MIN_SDK_FOR_WEAR = 23
const val BUILD_TOOLS_VERSION = "35.0.0"

val VERSION = Version(2, 17, 0, Version.Type.Release)
val VERSION = Version(2, 18, 0, Version.Type.Snapshot)
}

// TODO RUM-628 Switch to Java 17 bytecode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,13 @@ internal class RumViewManagerScope(
event: RumRawEvent,
writer: DataWriter<Any>
) {
val hasNoView = childrenScopes.isEmpty()
val iterator = childrenScopes.iterator()
var hasActiveView = false
@Suppress("UnsafeThirdPartyFunctionCall") // next/remove can't fail: we checked hasNext
while (iterator.hasNext()) {
val childScope = iterator.next()
hasActiveView = hasActiveView or childScope.isActive()
if (event is RumRawEvent.StopView) {
if (childScope.isActive() && (childScope as? RumViewScope)?.key?.id == event.key.id) {
lastStoppedViewTime = event.eventTime
Expand All @@ -149,6 +152,21 @@ internal class RumViewManagerScope(
iterator.remove()
}
}

if (event is RumRawEvent.AddViewLoadingTime && !hasActiveView) {
sdkCore.internalLogger.log(
InternalLogger.Level.WARN,
InternalLogger.Target.USER,
{ NO_ACTIVE_VIEW_FOR_LOADING_TIME_WARNING_MESSAGE }
)
sdkCore.internalLogger.logApiUsage {
InternalTelemetryEvent.ApiUsage.AddViewLoadingTime(
overwrite = event.overwrite,
noView = hasNoView,
noActiveView = !hasNoView
)
}
}
}

@WorkerThread
Expand All @@ -158,19 +176,11 @@ internal class RumViewManagerScope(
val isForegroundProcess = processFlag == importanceForeground

if (event is RumRawEvent.AddViewLoadingTime) {
val internalLogger = sdkCore.internalLogger
internalLogger.log(
sdkCore.internalLogger.log(
InternalLogger.Level.WARN,
InternalLogger.Target.USER,
{ MESSAGE_MISSING_VIEW }
)
internalLogger.logApiUsage {
InternalTelemetryEvent.ApiUsage.AddViewLoadingTime(
overwrite = event.overwrite,
noView = true,
noActiveView = false
)
}
// we should return here and not add the event to the session ended metric missed events as we already
// send the API usage telemetry
return
Expand Down Expand Up @@ -359,6 +369,9 @@ internal class RumViewManagerScope(
internal const val MESSAGE_UNKNOWN_MISSED_TYPE = "An RUM event was detected, but no view is active, " +
"its missed type is unknown"

internal const val NO_ACTIVE_VIEW_FOR_LOADING_TIME_WARNING_MESSAGE =
"No active view found to add the loading time."

internal val THREE_SECONDS_GAP_NS = TimeUnit.SECONDS.toNanos(3)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ internal open class RumViewScope(

private val oldViewIds = mutableSetOf<String>()
private val startedNanos: Long = eventTime.nanoTime
internal var stoppedNanos: Long = eventTime.nanoTime
internal var viewLoadingTime: Long? = null

internal val serverTimeOffsetInMs = sdkCore.time.serverTimeOffsetMs
Expand Down Expand Up @@ -250,33 +251,18 @@ internal open class RumViewScope(

@WorkerThread
private fun onAddViewLoadingTime(event: RumRawEvent.AddViewLoadingTime, writer: DataWriter<Any>) {
val internalLogger = sdkCore.internalLogger
val canUpdateViewLoadingTime = !stopped && (viewLoadingTime == null || event.overwrite)
if (stopped) {
internalLogger.log(
InternalLogger.Level.WARN,
InternalLogger.Target.USER,
{ NO_ACTIVE_VIEW_FOR_LOADING_TIME_WARNING_MESSAGE }
)
internalLogger.logApiUsage {
InternalTelemetryEvent.ApiUsage.AddViewLoadingTime(
overwrite = event.overwrite,
noView = false,
noActiveView = true
)
}
}

if (canUpdateViewLoadingTime) {
updateViewLoadingTime(event, internalLogger, writer)
updateViewLoadingTime(event, writer)
}
}

private fun updateViewLoadingTime(
event: RumRawEvent.AddViewLoadingTime,
internalLogger: InternalLogger,
writer: DataWriter<Any>
) {
val internalLogger = sdkCore.internalLogger
val viewName = key.name
val previousViewLoadingTime = viewLoadingTime
val newLoadingTime = event.eventTime.nanoTime - startedNanos
Expand Down Expand Up @@ -833,6 +819,7 @@ internal open class RumViewScope(
sideEffect()

stopped = true
resolveViewDuration(event)
sendViewUpdate(event, writer)
delegateEventToChildren(event, writer)
sendViewChanged()
Expand Down Expand Up @@ -872,7 +859,10 @@ internal open class RumViewScope(
val eventJsRefreshRate = performanceMetrics[RumPerformanceMetric.JS_FRAME_TIME]
?.toInversePerformanceMetric()

val updatedDurationNs = resolveViewDuration(event)
if (!stopped) {
resolveViewDuration(event)
}
val durationNs = stoppedNanos - startedNanos
val rumContext = getRumContext()

val timings = resolveCustomTimings()
Expand Down Expand Up @@ -922,7 +912,7 @@ internal open class RumViewScope(
id = currentViewId,
name = rumContext.viewName,
url = rumContext.viewUrl.orEmpty(),
timeSpent = updatedDurationNs,
timeSpent = durationNs,
action = ViewEvent.Action(eventActionCount),
resource = ViewEvent.Resource(eventResourceCount),
error = ViewEvent.Error(eventErrorCount),
Expand All @@ -932,8 +922,8 @@ internal open class RumViewScope(
customTimings = timings,
isActive = !viewComplete,
cpuTicksCount = eventCpuTicks,
cpuTicksPerSecond = if (updatedDurationNs >= ONE_SECOND_NS) {
eventCpuTicks?.let { (it * ONE_SECOND_NS) / updatedDurationNs }
cpuTicksPerSecond = if (durationNs >= ONE_SECOND_NS) {
eventCpuTicks?.let { (it * ONE_SECOND_NS) / durationNs }
} else {
null
},
Expand Down Expand Up @@ -1012,20 +1002,43 @@ internal open class RumViewScope(
return GlobalRumMonitor.get(sdkCore).getAttributes().toMap()
}

private fun resolveViewDuration(event: RumRawEvent): Long {
val duration = event.eventTime.nanoTime - startedNanos
return if (duration <= 0) {
private fun resolveViewDuration(event: RumRawEvent) {
stoppedNanos = event.eventTime.nanoTime
val duration = stoppedNanos - startedNanos
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)
)
}
stoppedNanos = startedNanos + 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 {
duration
stoppedNanos = startedNanos + 1
}
}

Expand Down Expand Up @@ -1350,10 +1363,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."
internal const val NO_ACTIVE_VIEW_FOR_LOADING_TIME_WARNING_MESSAGE =
"No active view found to add the loading time."
"view: %s was negative. In order to keep the view we forced it to 1ns."
internal const val ADDING_VIEW_LOADING_TIME_DEBUG_MESSAGE_FORMAT =
"View loading time %dns added to the view %s"
internal const val OVERWRITING_VIEW_LOADING_TIME_WARNING_MESSAGE_FORMAT =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,3 +251,37 @@ internal fun Forge.silentOrphanEvent(): RumRawEvent {
)
)
}

internal fun Forge.eventSent(
viewId: String,
eventTime: Time = Time()
): RumRawEvent {
return this.anElementFrom(
listOf(
RumRawEvent.ActionSent(
viewId = viewId,
frustrationCount = aPositiveInt(),
type = aValueFrom(ActionEvent.ActionEventActionType::class.java),
eventEndTimestampInNanos = aPositiveLong(),
eventTime = eventTime
),
RumRawEvent.ErrorSent(
viewId = viewId,
resourceId = aNullable { getForgery<UUID>().toString() },
resourceEndTimestampInNanos = aNullable { aLong() },
eventTime = eventTime
),
RumRawEvent.LongTaskSent(
viewId = viewId,
isFrozenFrame = aBool(),
eventTime = eventTime
),
RumRawEvent.ResourceSent(
viewId = viewId,
resourceId = getForgery<UUID>().toString(),
resourceEndTimestampInNanos = aPositiveLong(),
eventTime = eventTime
)
)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoInteractions
import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
import java.util.concurrent.TimeUnit
Expand Down Expand Up @@ -845,7 +844,7 @@ internal class RumViewManagerScopeTest {
// region AddViewLoadingTime

@Test
fun `M send a warning log and api usage telemetry W handleEvent { AddViewLoadingTime, no active view }`(
fun `M send a warning log and api usage telemetry W handleEvent { AddViewLoadingTime, no view }`(
forge: Forge
) {
// Given
Expand All @@ -856,11 +855,6 @@ internal class RumViewManagerScopeTest {
testedScope.handleEvent(fakeEvent, mockWriter)

// Then
mockInternalLogger.verifyLog(
InternalLogger.Level.WARN,
InternalLogger.Target.USER,
RumViewManagerScope.MESSAGE_MISSING_VIEW
)
mockInternalLogger.verifyApiUsage(
InternalTelemetryEvent.ApiUsage.AddViewLoadingTime(
overwrite = fakeEvent.overwrite,
Expand All @@ -869,8 +863,30 @@ internal class RumViewManagerScopeTest {
),
15f
)
verifyNoMoreInteractions(mockInternalLogger)
verifyNoInteractions(mockWriter)
}

@Test
fun `M send a warning log and api usage telemetry W handleEvent { AddViewLoadingTime, no active view }`(
forge: Forge
) {
// Given
val fakeEvent = forge.addViewLoadingTimeEvent()
whenever(mockChildScope.isActive()) doReturn false
testedScope.applicationDisplayed = true
testedScope.childrenScopes.add(mockChildScope)

// When
testedScope.handleEvent(fakeEvent, mockWriter)

// Then
mockInternalLogger.verifyApiUsage(
InternalTelemetryEvent.ApiUsage.AddViewLoadingTime(
overwrite = fakeEvent.overwrite,
noView = false,
noActiveView = true
),
15f
)
}

// endregion
Expand Down
Loading

0 comments on commit ba2a443

Please sign in to comment.