Skip to content

Commit

Permalink
RUM-6375: Force single core for session replay
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanmos committed Oct 23, 2024
1 parent 992020c commit 1c739e1
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 1 deletion.
1 change: 1 addition & 0 deletions detekt_custom.yml
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,7 @@ datadog:
- "java.lang.ref.WeakReference.constructor(android.content.Context?)"
- "java.lang.ref.WeakReference.constructor(android.view.View?)"
- "java.lang.ref.WeakReference.constructor(android.view.Window?)"
- "java.lang.ref.WeakReference.constructor(com.datadog.android.api.SdkCore?)"
- "java.lang.ref.WeakReference.constructor(kotlin.Any?)"
- "java.lang.ref.WeakReference.constructor(kotlin.Nothing?)"
- "java.lang.ref.WeakReference.constructor(kotlin.String?)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,27 @@

package com.datadog.android.sessionreplay

import androidx.annotation.VisibleForTesting
import com.datadog.android.Datadog
import com.datadog.android.api.InternalLogger
import com.datadog.android.api.SdkCore
import com.datadog.android.api.feature.Feature.Companion.SESSION_REPLAY_FEATURE_NAME
import com.datadog.android.api.feature.FeatureSdkCore
import com.datadog.android.sessionreplay.internal.SessionReplayFeature
import com.datadog.android.sessionreplay.internal.TouchPrivacyManager
import java.lang.ref.WeakReference

/**
* An entry point to Datadog Session Replay feature.
*/
object SessionReplay {

@VisibleForTesting internal var currentRegisteredCore: WeakReference<SdkCore>? = null

internal const val IS_ALREADY_REGISTERED_WARNING =
"Session Replay is already enabled and does not support multiple instances. " +
"The existing instance will continue to be used."

/**
* Enables a SessionReplay feature based on the configuration provided.
* It is recommended to invoke this function as early as possible in the app's lifecycle,
Expand Down Expand Up @@ -51,7 +60,13 @@ object SessionReplay {
startRecordingImmediately = sessionReplayConfiguration.startRecordingImmediately,
dynamicOptimizationEnabled = sessionReplayConfiguration.dynamicOptimizationEnabled
)
sdkCore.registerFeature(sessionReplayFeature)

if (isAlreadyRegistered()) {
logAlreadyRegisteredWarning(sdkCore.internalLogger)
} else {
currentRegisteredCore = WeakReference(sdkCore)
sdkCore.registerFeature(sessionReplayFeature)
}
}
}

Expand Down Expand Up @@ -86,4 +101,14 @@ object SessionReplay {

sessionReplayFeature?.manuallyStopRecording()
}

private fun isAlreadyRegistered() =
currentRegisteredCore?.get() != null

private fun logAlreadyRegisteredWarning(internalLogger: InternalLogger) =
internalLogger.log(
level = InternalLogger.Level.WARN,
targets = listOf(InternalLogger.Target.MAINTAINER, InternalLogger.Target.TELEMETRY),
messageBuilder = { IS_ALREADY_REGISTERED_WARNING }
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@

package com.datadog.android.sessionreplay

import com.datadog.android.api.InternalLogger
import com.datadog.android.api.feature.Feature.Companion.SESSION_REPLAY_FEATURE_NAME
import com.datadog.android.api.feature.FeatureScope
import com.datadog.android.api.feature.FeatureSdkCore
import com.datadog.android.sessionreplay.SessionReplay.IS_ALREADY_REGISTERED_WARNING
import com.datadog.android.sessionreplay.forge.ForgeConfigurator
import com.datadog.android.sessionreplay.internal.SessionReplayFeature
import com.datadog.android.sessionreplay.internal.net.SegmentRequestFactory
import com.datadog.android.sessionreplay.utils.verifyLog
import fr.xgouchet.elmyr.annotation.Forgery
import fr.xgouchet.elmyr.annotation.StringForgery
import fr.xgouchet.elmyr.junit5.ForgeConfiguration
Expand Down Expand Up @@ -50,6 +53,7 @@ internal class SessionReplayTest {
@BeforeEach
fun `set up`() {
whenever(mockSdkCore.internalLogger) doReturn mock()
SessionReplay.currentRegisteredCore = null
}

@Test
Expand Down Expand Up @@ -119,4 +123,41 @@ internal class SessionReplayTest {
// Then
verify(mockSessionReplayFeature).manuallyStopRecording()
}

@Test
fun `M warn and send telemetry W enable { session replay feature already registered with another core }`(
@Forgery fakeSessionReplayConfiguration: SessionReplayConfiguration,
@Mock mockCore1: FeatureSdkCore,
@Mock mockCore2: FeatureSdkCore,
@Mock mockInternalLogger: InternalLogger
) {
// Given
whenever(mockCore1.internalLogger).thenReturn(mockInternalLogger)
whenever(mockCore2.internalLogger).thenReturn(mockInternalLogger)
val fakeSessionReplayConfigurationWithMockRequirement = fakeSessionReplayConfiguration.copy(
systemRequirementsConfiguration = mockSystemRequirementsConfiguration
)
whenever(
mockSystemRequirementsConfiguration.runIfRequirementsMet(any(), any())
) doAnswer {
it.getArgument<() -> Unit>(1).invoke()
}
SessionReplay.enable(
sessionReplayConfiguration = fakeSessionReplayConfigurationWithMockRequirement,
sdkCore = mockCore1
)

// When
SessionReplay.enable(
sessionReplayConfiguration = fakeSessionReplayConfigurationWithMockRequirement,
sdkCore = mockCore2
)

// Then
mockInternalLogger.verifyLog(
level = InternalLogger.Level.WARN,
targets = listOf(InternalLogger.Target.MAINTAINER, InternalLogger.Target.TELEMETRY),
message = IS_ALREADY_REGISTERED_WARNING
)
}
}

0 comments on commit 1c739e1

Please sign in to comment.