Skip to content

Commit b4446a0

Browse files
authored
Merge pull request #3060 from DataDog/aleksandr-gringauz/feature/app-launch-vitals
Merge App launch vitals feature branch into develop
2 parents cc0f647 + 548d04f commit b4446a0

File tree

58 files changed

+2575
-693
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2575
-693
lines changed

detekt_custom_safe_calls.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,7 @@ datadog:
930930
- "kotlin.collections.listOf(com.datadog.android.rum.model.ErrorEvent.Interface)"
931931
- "kotlin.collections.listOf(com.datadog.android.rum.model.LongTaskEvent.Interface)"
932932
- "kotlin.collections.listOf(com.datadog.android.rum.model.ResourceEvent.Interface)"
933+
- "kotlin.collections.listOf(com.datadog.android.rum.model.RumVitalAppLaunchEvent.Interface)"
933934
- "kotlin.collections.listOf(com.datadog.android.rum.model.RumVitalOperationStepEvent.Interface)"
934935
- "kotlin.collections.listOf(com.datadog.android.rum.model.ViewEvent.Interface)"
935936
- "kotlin.collections.listOf(com.datadog.android.sessionreplay.MapperTypeWrapper)"

features/dd-sdk-android-rum/api/apiSurface

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ interface com.datadog.android.rum.RumMonitor
122122
fun startFeatureOperation(String, String? = null, Map<String, Any?> = emptyMap())
123123
fun succeedFeatureOperation(String, String? = null, Map<String, Any?> = emptyMap())
124124
fun failFeatureOperation(String, String? = null, com.datadog.android.rum.featureoperations.FailureReason, Map<String, Any?> = emptyMap())
125+
fun reportAppFullyDisplayed()
125126
var debug: Boolean
126127
fun _getInternal(): _RumInternalProxy?
127128
enum com.datadog.android.rum.RumPerformanceMetric

features/dd-sdk-android-rum/api/dd-sdk-android-rum.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ public abstract interface class com/datadog/android/rum/RumMonitor {
162162
public abstract fun getDebug ()Z
163163
public abstract fun removeAttribute (Ljava/lang/String;)V
164164
public abstract fun removeViewAttributes (Ljava/util/Collection;)V
165+
public abstract fun reportAppFullyDisplayed ()V
165166
public abstract fun setDebug (Z)V
166167
public abstract fun startAction (Lcom/datadog/android/rum/RumActionType;Ljava/lang/String;Ljava/util/Map;)V
167168
public abstract fun startFeatureOperation (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/Rum.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ import com.datadog.android.core.InternalSdkCore
1818
import com.datadog.android.core.sampling.RateBasedSampler
1919
import com.datadog.android.rum.internal.RumAnonymousIdentifierManager
2020
import com.datadog.android.rum.internal.RumFeature
21+
import com.datadog.android.rum.internal.domain.scope.RumVitalAppLaunchEventHelper
2122
import com.datadog.android.rum.internal.metric.SessionEndedMetricDispatcher
2223
import com.datadog.android.rum.internal.monitor.DatadogRumMonitor
2324
import com.datadog.android.rum.internal.startup.RumAppStartupTelemetryReporter
25+
import com.datadog.android.rum.internal.startup.RumSessionScopeStartupManager
2426
import com.datadog.android.telemetry.internal.TelemetryEventHandler
2527

2628
/**
@@ -115,6 +117,16 @@ object Rum {
115117
sessionSamplingRate = rumFeature.configuration.sampleRate
116118
)
117119

120+
val rumVitalAppLaunchEventHelper = RumVitalAppLaunchEventHelper(
121+
rumSessionTypeOverride = rumFeature.configuration.rumSessionTypeOverride,
122+
batteryInfoProvider = rumFeature.batteryInfoProvider,
123+
displayInfoProvider = rumFeature.displayInfoProvider,
124+
sampleRate = rumFeature.sampleRate,
125+
internalLogger = sdkCore.internalLogger
126+
)
127+
128+
val rumAppStartupTelemetryReporter = RumAppStartupTelemetryReporter.create(sdkCore = sdkCore)
129+
118130
return DatadogRumMonitor(
119131
applicationId = rumFeature.applicationId,
120132
sdkCore = sdkCore,
@@ -145,7 +157,13 @@ object Rum {
145157
accessibilitySnapshotManager = rumFeature.accessibilitySnapshotManager,
146158
batteryInfoProvider = rumFeature.batteryInfoProvider,
147159
displayInfoProvider = rumFeature.displayInfoProvider,
148-
rumAppStartupTelemetryReporter = RumAppStartupTelemetryReporter.create(sdkCore)
160+
rumSessionScopeStartupManagerFactory = {
161+
RumSessionScopeStartupManager.create(
162+
rumVitalAppLaunchEventHelper = rumVitalAppLaunchEventHelper,
163+
sdkCore = sdkCore,
164+
rumAppStartupTelemetryReporter = rumAppStartupTelemetryReporter
165+
)
166+
}
149167
)
150168
}
151169

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/RumMonitor.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,14 @@ interface RumMonitor {
370370
attributes: Map<String, Any?> = emptyMap()
371371
)
372372

373+
/**
374+
* This method can be used to mark the moment in time when the UI of the app is considered fully displayed.
375+
* The duration between the application launch and this moment of time will be shown as TTFD (time to full display)
376+
* in the RUM session explorer. Only the first call to this method will have any effect for a given RUM session.
377+
*/
378+
@ExperimentalRumApi
379+
fun reportAppFullyDisplayed()
380+
373381
/**
374382
* Utility setting to inspect the active RUM View.
375383
* If set, a debugging outline will be displayed on top of the application, describing the name

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/RumFeature.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -692,15 +692,17 @@ internal class RumFeature(
692692

693693
override fun onAppStartupDetected(scenario: RumStartupScenario) {
694694
val activity = scenario.activity.get() ?: return
695+
val rumMonitor = (GlobalRumMonitor.get(sdkCore) as? AdvancedRumMonitor) ?: return
696+
697+
rumMonitor.sendAppStartEvent(scenario)
695698

696699
val callback = object : RumFirstDrawTimeReporter.Callback {
697700
override fun onFirstFrameDrawn(timestampNs: Long) {
698701
val info = RumTTIDInfo(
699702
scenario = scenario,
700-
durationNs = timestampNs - scenario.initialTimeNs
703+
durationNs = timestampNs - scenario.initialTime.nanoTime
701704
)
702-
(GlobalRumMonitor.get(sdkCore) as? AdvancedRumMonitor)
703-
?.sendTTIDEvent(info)
705+
rumMonitor.sendTTIDEvent(info)
704706
}
705707
}
706708

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/RumSessionTypeExt.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.datadog.android.rum.model.ActionEvent
1111
import com.datadog.android.rum.model.ErrorEvent
1212
import com.datadog.android.rum.model.LongTaskEvent
1313
import com.datadog.android.rum.model.ResourceEvent
14+
import com.datadog.android.rum.model.RumVitalAppLaunchEvent
1415
import com.datadog.android.rum.model.RumVitalOperationStepEvent
1516
import com.datadog.android.rum.model.ViewEvent
1617

@@ -54,3 +55,10 @@ internal fun RumSessionType.toVital(): RumVitalOperationStepEvent.RumVitalOperat
5455
RumSessionType.USER -> RumVitalOperationStepEvent.RumVitalOperationStepEventSessionType.USER
5556
}
5657
}
58+
59+
internal fun RumSessionType.toVitalAppLaunch(): RumVitalAppLaunchEvent.RumVitalAppLaunchEventSessionType {
60+
return when (this) {
61+
RumSessionType.SYNTHETICS -> RumVitalAppLaunchEvent.RumVitalAppLaunchEventSessionType.SYNTHETICS
62+
RumSessionType.USER -> RumVitalAppLaunchEvent.RumVitalAppLaunchEventSessionType.USER
63+
}
64+
}

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/Time.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ internal data class Time(
1313
val nanoTime: Long = System.nanoTime()
1414
)
1515

16+
/**
17+
* Convert a value obtained from [System.currentTimeMillis] to [Time].
18+
*/
1619
internal fun Long.asTime(): Time {
1720
// Because nanoTime only measures the nanoseconds since the beginning
1821
// of the current JVM lifetime, we need to approximate the nanotime we want.
@@ -22,3 +25,12 @@ internal fun Long.asTime(): Time {
2225
val offset = this - now.timestamp
2326
return Time(this, TimeUnit.MILLISECONDS.toNanos(offset) + now.nanoTime)
2427
}
28+
29+
/**
30+
* Convert a value obtained from [System.nanoTime] to [Time].
31+
*/
32+
internal fun Long.asTimeNs(): Time {
33+
val now = Time()
34+
val offset = this - now.nanoTime
35+
return Time(TimeUnit.NANOSECONDS.toMillis(offset) + now.timestamp, this)
36+
}

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/event/RumEventMapper.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import com.datadog.android.rum.model.ActionEvent
1313
import com.datadog.android.rum.model.ErrorEvent
1414
import com.datadog.android.rum.model.LongTaskEvent
1515
import com.datadog.android.rum.model.ResourceEvent
16+
import com.datadog.android.rum.model.RumVitalAppLaunchEvent
1617
import com.datadog.android.rum.model.RumVitalOperationStepEvent
1718
import com.datadog.android.rum.model.ViewEvent
1819
import com.datadog.android.telemetry.model.TelemetryConfigurationEvent
@@ -64,6 +65,8 @@ internal data class RumEventMapper(
6465
is LongTaskEvent -> longTaskEventMapper.map(event)
6566
is RumVitalOperationStepEvent -> vitalOperationStepEventMapper.map(event)
6667
is TelemetryConfigurationEvent -> telemetryConfigurationMapper.map(event)
68+
// TODO RUM-13103: add vitalAppLaunchEventMapper.
69+
is RumVitalAppLaunchEvent,
6770
is TelemetryDebugEvent,
6871
is TelemetryUsageEvent,
6972
is TelemetryErrorEvent -> event

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/event/RumEventSerializer.kt

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import com.datadog.android.rum.model.ActionEvent
1616
import com.datadog.android.rum.model.ErrorEvent
1717
import com.datadog.android.rum.model.LongTaskEvent
1818
import com.datadog.android.rum.model.ResourceEvent
19+
import com.datadog.android.rum.model.RumVitalAppLaunchEvent
1920
import com.datadog.android.rum.model.RumVitalOperationStepEvent
2021
import com.datadog.android.rum.model.ViewEvent
2122
import com.datadog.android.telemetry.model.TelemetryConfigurationEvent
@@ -24,6 +25,7 @@ import com.datadog.android.telemetry.model.TelemetryErrorEvent
2425
import com.datadog.android.telemetry.model.TelemetryUsageEvent
2526
import com.google.gson.JsonObject
2627

28+
@Suppress("TooManyFunctions")
2729
internal class RumEventSerializer(
2830
private val internalLogger: InternalLogger,
2931
private val dataConstraints: DataConstraints = DatadogDataConstraints(internalLogger)
@@ -49,7 +51,10 @@ internal class RumEventSerializer(
4951
serializeLongTaskEvent(model)
5052
}
5153
is RumVitalOperationStepEvent -> {
52-
serializeVitalEvent(model)
54+
serializeVitalOperationStepEvent(model)
55+
}
56+
is RumVitalAppLaunchEvent -> {
57+
serializeVitalAppLaunchEvent(model)
5358
}
5459
is TelemetryDebugEvent -> {
5560
model.toJson().toString()
@@ -205,7 +210,31 @@ internal class RumEventSerializer(
205210
return extractKnownAttributes(sanitizedModel.toJson().asJsonObject).toString()
206211
}
207212

208-
private fun serializeVitalEvent(model: RumVitalOperationStepEvent): String {
213+
private fun serializeVitalOperationStepEvent(model: RumVitalOperationStepEvent): String {
214+
val sanitizedUser = model.usr?.copy(
215+
additionalProperties = validateUserAttributes(model.usr.additionalProperties)
216+
.safeMapValuesToJson(internalLogger)
217+
.toMutableMap()
218+
)
219+
val sanitizedAccount = model.account?.copy(
220+
additionalProperties = validateAccountAttributes(model.account.additionalProperties)
221+
.safeMapValuesToJson(internalLogger)
222+
.toMutableMap()
223+
)
224+
val sanitizedContext = model.context?.copy(
225+
additionalProperties = validateContextAttributes(model.context.additionalProperties)
226+
.safeMapValuesToJson(internalLogger)
227+
.toMutableMap()
228+
)
229+
val sanitizedModel = model.copy(
230+
usr = sanitizedUser,
231+
account = sanitizedAccount,
232+
context = sanitizedContext
233+
)
234+
return extractKnownAttributes(sanitizedModel.toJson().asJsonObject).toString()
235+
}
236+
237+
private fun serializeVitalAppLaunchEvent(model: RumVitalAppLaunchEvent): String {
209238
val sanitizedUser = model.usr?.copy(
210239
additionalProperties = validateUserAttributes(model.usr.additionalProperties)
211240
.safeMapValuesToJson(internalLogger)

0 commit comments

Comments
 (0)