Skip to content

Commit

Permalink
RUMM-2542 Send hasReplay property for RUM events
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusc83 committed Sep 23, 2022
1 parent e1db155 commit 9de000f
Show file tree
Hide file tree
Showing 28 changed files with 624 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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.
*/

package com.datadog.android.rum.internal

import com.datadog.android.sessionreplay.internal.SessionReplayFeature
import com.datadog.android.v2.api.context.DatadogContext

internal class FeaturesContextResolver {

fun resolveHasReplay(context: DatadogContext): Boolean {
val sessionReplayContext =
context.featuresContext[SessionReplayFeature.SESSION_REPLAY_FEATURE_NAME]
return (
sessionReplayContext?.get(SessionReplayFeature.IS_RECORDING_CONTEXT_KEY)
as? Boolean
) ?: false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.annotation.WorkerThread
import com.datadog.android.core.internal.persistence.DataWriter
import com.datadog.android.rum.GlobalRum
import com.datadog.android.rum.RumActionType
import com.datadog.android.rum.internal.FeaturesContextResolver
import com.datadog.android.rum.internal.domain.RumContext
import com.datadog.android.rum.internal.domain.Time
import com.datadog.android.rum.internal.domain.event.RumEventSourceProvider
Expand All @@ -31,7 +32,8 @@ internal class RumActionScope(
inactivityThresholdMs: Long = ACTION_INACTIVITY_MS,
maxDurationMs: Long = ACTION_MAX_DURATION_MS,
private val rumEventSourceProvider: RumEventSourceProvider,
private val contextProvider: ContextProvider
private val contextProvider: ContextProvider,
private val featuresContextResolver: FeaturesContextResolver = FeaturesContextResolver()
) : RumScope {

private val inactivityThresholdNs = TimeUnit.MILLISECONDS.toNanos(inactivityThresholdMs)
Expand Down Expand Up @@ -179,6 +181,7 @@ internal class RumActionScope(
longTaskCount++
}

@Suppress("LongMethod")
@WorkerThread
private fun sendAction(
endNanos: Long,
Expand All @@ -192,6 +195,7 @@ internal class RumActionScope(
val rumContext = getRumContext()
val sdkContext = contextProvider.context
val user = sdkContext.userInfo
val hasReplay = featuresContextResolver.resolveHasReplay(sdkContext)

val frustrations = mutableListOf<ActionEvent.Type>()
if (errorCount > 0 && actualType == RumActionType.TAP) {
Expand Down Expand Up @@ -219,7 +223,8 @@ internal class RumActionScope(
application = ActionEvent.Application(rumContext.applicationId),
session = ActionEvent.ActionEventSession(
id = rumContext.sessionId,
type = ActionEvent.ActionEventSessionType.USER
type = ActionEvent.ActionEventSessionType.USER,
hasReplay = hasReplay
),
source = rumEventSourceProvider.actionEventSource,
usr = ActionEvent.Usr(
Expand Down Expand Up @@ -260,7 +265,8 @@ internal class RumActionScope(
event: RumRawEvent.StartAction,
timestampOffset: Long,
eventSourceProvider: RumEventSourceProvider,
contextProvider: ContextProvider
contextProvider: ContextProvider,
featuresContextResolver: FeaturesContextResolver
): RumScope {
return RumActionScope(
parentScope,
Expand All @@ -271,7 +277,8 @@ internal class RumActionScope(
event.attributes,
timestampOffset,
rumEventSourceProvider = eventSourceProvider,
contextProvider = contextProvider
contextProvider = contextProvider,
featuresContextResolver = featuresContextResolver
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.datadog.android.rum.GlobalRum
import com.datadog.android.rum.RumAttributes
import com.datadog.android.rum.RumErrorSource
import com.datadog.android.rum.RumResourceKind
import com.datadog.android.rum.internal.FeaturesContextResolver
import com.datadog.android.rum.internal.domain.RumContext
import com.datadog.android.rum.internal.domain.Time
import com.datadog.android.rum.internal.domain.event.ResourceTiming
Expand All @@ -37,7 +38,8 @@ internal class RumResourceScope(
serverTimeOffsetInMs: Long,
internal val firstPartyHostDetector: FirstPartyHostDetector,
private val rumEventSourceProvider: RumEventSourceProvider,
private val contextProvider: ContextProvider
private val contextProvider: ContextProvider,
private val featuresContextResolver: FeaturesContextResolver
) : RumScope {

internal val resourceId: String = UUID.randomUUID().toString()
Expand Down Expand Up @@ -173,6 +175,7 @@ internal class RumResourceScope(
val sdkContext = contextProvider.context
val rumContext = getRumContext()
val user = sdkContext.userInfo
val hasReplay = featuresContextResolver.resolveHasReplay(sdkContext)

@Suppress("UNCHECKED_CAST")
val finalTiming = timing ?: extractResourceTiming(
Expand Down Expand Up @@ -212,7 +215,8 @@ internal class RumResourceScope(
application = ResourceEvent.Application(rumContext.applicationId),
session = ResourceEvent.ResourceEventSession(
id = rumContext.sessionId,
type = ResourceEvent.ResourceEventSessionType.USER
type = ResourceEvent.ResourceEventSessionType.USER,
hasReplay = hasReplay
),
source = rumEventSourceProvider.resourceEventSource,
os = ResourceEvent.Os(
Expand Down Expand Up @@ -259,7 +263,7 @@ internal class RumResourceScope(
}
}

@SuppressWarnings("LongParameterList")
@SuppressWarnings("LongParameterList", "LongMethod")
@WorkerThread
private fun sendError(
message: String,
Expand All @@ -274,6 +278,7 @@ internal class RumResourceScope(
val rumContext = getRumContext()
val sdkContext = contextProvider.context
val user = sdkContext.userInfo
val hasReplay = featuresContextResolver.resolveHasReplay(sdkContext)

val errorEvent = ErrorEvent(
date = eventTimestamp,
Expand Down Expand Up @@ -307,7 +312,8 @@ internal class RumResourceScope(
application = ErrorEvent.Application(rumContext.applicationId),
session = ErrorEvent.ErrorEventSession(
id = rumContext.sessionId,
type = ErrorEvent.ErrorEventSessionType.USER
type = ErrorEvent.ErrorEventSessionType.USER,
hasReplay = hasReplay
),
source = rumEventSourceProvider.errorEventSource,
os = ErrorEvent.Os(
Expand Down Expand Up @@ -363,7 +369,8 @@ internal class RumResourceScope(
firstPartyHostDetector: FirstPartyHostDetector,
timestampOffset: Long,
rumEventSourceProvider: RumEventSourceProvider,
contextProvider: ContextProvider
contextProvider: ContextProvider,
featuresContextResolver: FeaturesContextResolver
): RumScope {
return RumResourceScope(
parentScope,
Expand All @@ -375,7 +382,8 @@ internal class RumResourceScope(
timestampOffset,
firstPartyHostDetector,
rumEventSourceProvider,
contextProvider
contextProvider,
featuresContextResolver
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.datadog.android.log.internal.utils.debugWithTelemetry
import com.datadog.android.rum.GlobalRum
import com.datadog.android.rum.RumActionType
import com.datadog.android.rum.RumAttributes
import com.datadog.android.rum.internal.FeaturesContextResolver
import com.datadog.android.rum.internal.domain.RumContext
import com.datadog.android.rum.internal.domain.Time
import com.datadog.android.rum.internal.domain.event.RumEventSourceProvider
Expand Down Expand Up @@ -58,6 +59,7 @@ internal open class RumViewScope(
private val contextProvider: ContextProvider,
private val buildSdkVersionProvider: BuildSdkVersionProvider = DefaultBuildSdkVersionProvider(),
private val viewUpdatePredicate: ViewUpdatePredicate = DefaultViewUpdatePredicate(),
private val featuresContextResolver: FeaturesContextResolver = FeaturesContextResolver(),
internal val type: RumViewType = RumViewType.FOREGROUND
) : RumScope {

Expand Down Expand Up @@ -281,7 +283,8 @@ internal open class RumViewScope(
event,
serverTimeOffsetInMs,
rumEventSourceProvider,
contextProvider
contextProvider,
featuresContextResolver
)
pendingActionCount++
customActionScope.handleEvent(RumRawEvent.SendCustomActionNow(), writer)
Expand All @@ -298,7 +301,8 @@ internal open class RumViewScope(
event,
serverTimeOffsetInMs,
rumEventSourceProvider,
contextProvider
contextProvider,
featuresContextResolver
)
)
pendingActionCount++
Expand All @@ -321,7 +325,8 @@ internal open class RumViewScope(
firstPartyHostDetector,
serverTimeOffsetInMs,
rumEventSourceProvider,
contextProvider
contextProvider,
featuresContextResolver
)
pendingResourceCount++
}
Expand All @@ -344,6 +349,7 @@ internal open class RumViewScope(
val networkInfo = sdkContext.networkInfo
val errorType = event.type ?: event.throwable?.javaClass?.canonicalName
val throwableMessage = event.throwable?.message ?: ""
val hasReplay = featuresContextResolver.resolveHasReplay(sdkContext)
val message = if (throwableMessage.isNotBlank() && event.message != throwableMessage) {
"${event.message}: $throwableMessage"
} else {
Expand Down Expand Up @@ -375,7 +381,8 @@ internal open class RumViewScope(
application = ErrorEvent.Application(rumContext.applicationId),
session = ErrorEvent.ErrorEventSession(
id = rumContext.sessionId,
type = ErrorEvent.ErrorEventSessionType.USER
type = ErrorEvent.ErrorEventSessionType.USER,
hasReplay = hasReplay
),
source = rumEventSourceProvider.errorEventSource,
os = ErrorEvent.Os(
Expand Down Expand Up @@ -581,6 +588,7 @@ internal open class RumViewScope(
val timings = resolveCustomTimings()
val memoryInfo = lastMemoryInfo
val refreshRateInfo = lastFrameRateInfo
val hasReplay = featuresContextResolver.resolveHasReplay(sdkContext)
val isSlowRendered = resolveRefreshRateInfo(refreshRateInfo)
val viewEvent = ViewEvent(
date = eventTimestamp,
Expand Down Expand Up @@ -617,7 +625,8 @@ internal open class RumViewScope(
application = ViewEvent.Application(rumContext.applicationId),
session = ViewEvent.ViewEventSession(
id = rumContext.sessionId,
type = ViewEvent.ViewEventSessionType.USER
type = ViewEvent.ViewEventSessionType.USER,
hasReplay = hasReplay
),
source = rumEventSourceProvider.viewEventSource,
os = ViewEvent.Os(
Expand Down Expand Up @@ -718,7 +727,8 @@ internal open class RumViewScope(
application = ActionEvent.Application(rumContext.applicationId),
session = ActionEvent.ActionEventSession(
id = rumContext.sessionId,
type = ActionEvent.ActionEventSessionType.USER
type = ActionEvent.ActionEventSessionType.USER,
hasReplay = false
),
source = rumEventSourceProvider.actionEventSource,
os = ActionEvent.Os(
Expand Down Expand Up @@ -760,6 +770,7 @@ internal open class RumViewScope(
val networkInfo = sdkContext.networkInfo
val timestamp = event.eventTime.timestamp + serverTimeOffsetInMs
val isFrozenFrame = event.durationNs > FROZEN_FRAME_THRESHOLD_NS
val hasReplay = featuresContextResolver.resolveHasReplay(sdkContext)
val longTaskEvent = LongTaskEvent(
date = timestamp - TimeUnit.NANOSECONDS.toMillis(event.durationNs),
longTask = LongTaskEvent.LongTask(
Expand All @@ -782,7 +793,8 @@ internal open class RumViewScope(
application = LongTaskEvent.Application(rumContext.applicationId),
session = LongTaskEvent.LongTaskEventSession(
id = rumContext.sessionId,
type = LongTaskEvent.LongTaskEventSessionType.USER
type = LongTaskEvent.LongTaskEventSessionType.USER,
hasReplay = hasReplay
),
source = rumEventSourceProvider.longTaskEventSource,
os = LongTaskEvent.Os(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@ package com.datadog.android.sessionreplay.internal

import android.app.Application
import android.content.Context
import com.datadog.android.Datadog
import com.datadog.android.core.configuration.Configuration
import com.datadog.android.core.internal.CoreFeature
import com.datadog.android.core.internal.SdkFeature
import com.datadog.android.core.internal.net.DataUploader
import com.datadog.android.core.internal.net.NoOpDataUploader
import com.datadog.android.core.internal.persistence.PersistenceStrategy
import com.datadog.android.core.internal.utils.sdkLogger
import com.datadog.android.sessionreplay.LifecycleCallback
import com.datadog.android.sessionreplay.SessionReplayLifecycleCallback
import com.datadog.android.sessionreplay.internal.domain.SessionReplayRecordPersistenceStrategy
import com.datadog.android.sessionreplay.internal.domain.SessionReplayRequestFactory
import com.datadog.android.sessionreplay.internal.domain.SessionReplaySerializedRecordWriter
import com.datadog.android.v2.api.RequestFactory
import java.util.concurrent.atomic.AtomicBoolean

internal class SessionReplayFeature(
Expand All @@ -27,9 +28,10 @@ internal class SessionReplayFeature(
private val sessionReplayCallbackProvider: (PersistenceStrategy<String>) ->
LifecycleCallback = {
SessionReplayLifecycleCallback(
SessionReplayContextProvider(),
SessionReplayRumContextProvider(),
configuration.privacy,
SessionReplaySerializedRecordWriter(it.getWriter())
SessionReplaySerializedRecordWriter(it.getWriter()),
SessionReplayRecordCallback(Datadog.globalSDKCore)
)
}
) : SdkFeature<String, Configuration.Feature.SessionReplay>(coreFeature) {
Expand Down Expand Up @@ -61,6 +63,7 @@ internal class SessionReplayFeature(
configuration: Configuration.Feature.SessionReplay
): PersistenceStrategy<String> {
return SessionReplayRecordPersistenceStrategy(
coreFeature.contextProvider,
coreFeature.trackingConsentProvider,
coreFeature.storageDir,
coreFeature.persistenceExecutorService,
Expand All @@ -69,9 +72,9 @@ internal class SessionReplayFeature(
)
}

override fun createUploader(configuration: Configuration.Feature.SessionReplay): DataUploader {
// TODO: This will be added later in RUMM-2273
return NoOpDataUploader()
override fun createRequestFactory(configuration: Configuration.Feature.SessionReplay):
RequestFactory {
return SessionReplayRequestFactory()
}

// endregion
Expand Down Expand Up @@ -110,5 +113,6 @@ internal class SessionReplayFeature(

companion object {
const val SESSION_REPLAY_FEATURE_NAME = "session-replay"
const val IS_RECORDING_CONTEXT_KEY = "is_recording"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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.
*/

package com.datadog.android.sessionreplay.internal

import com.datadog.android.sessionreplay.RecordCallback
import com.datadog.android.v2.api.SDKCore

internal class SessionReplayRecordCallback(private val datadogCore: SDKCore) : RecordCallback {
override fun onStartRecording() {
updateRecording(true)
}

override fun onStopRecording() {
updateRecording(false)
}

private fun updateRecording(isRecording:Boolean){
val featureContext = mapOf(SessionReplayFeature.IS_RECORDING_CONTEXT_KEY to isRecording)
datadogCore.setFeatureContext(
SessionReplayFeature.SESSION_REPLAY_FEATURE_NAME,
featureContext
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import com.datadog.android.rum.internal.domain.RumContext.Companion.NULL_UUID
import com.datadog.android.sessionreplay.utils.RumContextProvider
import com.datadog.android.sessionreplay.utils.SessionReplayRumContext

internal class SessionReplayContextProvider : RumContextProvider {
internal class SessionReplayRumContextProvider : RumContextProvider {
override fun getRumContext(): SessionReplayRumContext {
return GlobalRum.getRumContext().let {
SessionReplayRumContext(
Expand Down
Loading

0 comments on commit 9de000f

Please sign in to comment.