Skip to content

Commit

Permalink
RUMM-2076 Add the Configuration#setVitalsMonitorUpdateFrequency API
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusc83 committed May 6, 2022
1 parent 0b30629 commit a835ed6
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 23 deletions.
7 changes: 7 additions & 0 deletions dd-sdk-android/apiSurface
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ data class com.datadog.android.core.configuration.Configuration
DEPRECATED fun setInternalLogsEnabled(String, String): Builder
fun setAdditionalConfiguration(Map<String, Any>): Builder
fun setProxy(java.net.Proxy, okhttp3.Authenticator?): Builder
fun setVitalsUpdateFrequency(VitalsUpdateFrequency): Builder
companion object
data class com.datadog.android.core.configuration.Credentials
constructor(String, String, String, String?, String? = null)
Expand All @@ -118,6 +119,12 @@ enum com.datadog.android.core.configuration.UploadFrequency
- FREQUENT
- AVERAGE
- RARE
enum com.datadog.android.core.configuration.VitalsUpdateFrequency
constructor(Long)
- FREQUENT
- AVERAGE
- RARE
- NEVER
data class com.datadog.android.core.model.NetworkInfo
constructor(Connectivity = Connectivity.NETWORK_NOT_CONNECTED, kotlin.String? = null, kotlin.Long? = null, kotlin.Long? = null, kotlin.Long? = null, kotlin.Long? = null, kotlin.String? = null)
fun toJson(): com.google.gson.JsonElement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ internal constructor(
val viewTrackingStrategy: ViewTrackingStrategy?,
val longTaskTrackingStrategy: TrackingStrategy?,
val rumEventMapper: EventMapper<Any>,
val backgroundEventTracking: Boolean
val backgroundEventTracking: Boolean,
val vitalsMonitorUpdateFrequency: VitalsUpdateFrequency
) : Feature()
}

Expand Down Expand Up @@ -691,6 +692,17 @@ internal constructor(
devLogger.e(ERROR_FEATURE_DISABLED.format(Locale.US, feature.featureName, method))
}
}

/**
* Allows to specify the frequency at which to update the mobile vitals
* data provided in the RUM [ViewEvent].
* @param frequency as [VitalsUpdateFrequency]
* @see [VitalsUpdateFrequency]
*/
fun setVitalsUpdateFrequency(frequency: VitalsUpdateFrequency): Builder {
rumConfig = rumConfig.copy(vitalsMonitorUpdateFrequency = frequency)
return this
}
}

// endregion
Expand Down Expand Up @@ -737,7 +749,8 @@ internal constructor(
viewTrackingStrategy = ActivityViewTrackingStrategy(false),
longTaskTrackingStrategy = MainLooperLongTaskStrategy(DEFAULT_LONG_TASK_THRESHOLD_MS),
rumEventMapper = NoOpEventMapper(),
backgroundEventTracking = false
backgroundEventTracking = false,
vitalsMonitorUpdateFrequency = VitalsUpdateFrequency.AVERAGE
)

internal const val ERROR_FEATURE_DISABLED = "The %s feature has been disabled in your " +
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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.core.configuration

/**
* Defines the frequency at which mobile vitals monitor updates the data.
*/
enum class VitalsUpdateFrequency(
internal val periodInMs: Long
) {

/** Every 100 milliseconds. */
FREQUENT(100L),

/** Every 500 milliseconds. This is the default frequency. */
AVERAGE(500L),

/** Every 1000 milliseconds. */
RARE(1000L),

/** No data will be sent for mobile vitals. */
NEVER(0)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* 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.core.internal.thread

import java.util.concurrent.Callable
import java.util.concurrent.Future
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit

internal class NoOpScheduledExecutorService : ScheduledExecutorService {
override fun execute(command: Runnable?) {
// No-op
}

override fun shutdown() {
// No-op
}

override fun shutdownNow(): MutableList<Runnable>? {
return null
}

override fun isShutdown(): Boolean {
return false
}

override fun isTerminated(): Boolean {
return false
}

override fun awaitTermination(timeout: Long, unit: TimeUnit?): Boolean {
return false
}

override fun <T : Any?> submit(task: Callable<T>?): Future<T>? {
return null
}

override fun <T : Any?> submit(task: Runnable?, result: T): Future<T>? {
return null
}

override fun submit(task: Runnable?): Future<*>? {
return null
}

override fun <T : Any?> invokeAll(tasks: MutableCollection<out Callable<T>>?):
MutableList<Future<T>>? {
return null
}

override fun <T : Any?> invokeAll(
tasks: MutableCollection<out Callable<T>>?,
timeout: Long,
unit: TimeUnit?
): MutableList<Future<T>>? {
return null
}

override fun <T : Any?> invokeAny(tasks: MutableCollection<out Callable<T>>?): T? {
return null
}

override fun <T : Any?> invokeAny(
tasks: MutableCollection<out Callable<T>>?,
timeout: Long,
unit: TimeUnit?
): T? {
return null
}

override fun schedule(command: Runnable?, delay: Long, unit: TimeUnit?): ScheduledFuture<*>? {
return null
}

override fun <V : Any?> schedule(
callable: Callable<V>?,
delay: Long,
unit: TimeUnit?
): ScheduledFuture<V>? {
return null
}

override fun scheduleAtFixedRate(
command: Runnable?,
initialDelay: Long,
period: Long,
unit: TimeUnit?
): ScheduledFuture<*>? {
return null
}

override fun scheduleWithFixedDelay(
command: Runnable?,
initialDelay: Long,
delay: Long,
unit: TimeUnit?
): ScheduledFuture<*>? {
return null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import android.os.Handler
import android.os.Looper
import android.view.Choreographer
import com.datadog.android.core.configuration.Configuration
import com.datadog.android.core.configuration.VitalsUpdateFrequency
import com.datadog.android.core.internal.CoreFeature
import com.datadog.android.core.internal.SdkFeature
import com.datadog.android.core.internal.event.NoOpEventMapper
import com.datadog.android.core.internal.net.DataUploader
import com.datadog.android.core.internal.persistence.PersistenceStrategy
import com.datadog.android.core.internal.system.StaticAndroidInfoProvider
import com.datadog.android.core.internal.thread.NoOpScheduledExecutorService
import com.datadog.android.core.internal.utils.devLogger
import com.datadog.android.core.internal.utils.executeSafe
import com.datadog.android.core.internal.utils.scheduleSafe
Expand Down Expand Up @@ -45,6 +47,7 @@ import com.datadog.android.rum.tracking.TrackingStrategy
import com.datadog.android.rum.tracking.ViewTrackingStrategy
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.TimeUnit

Expand All @@ -68,7 +71,7 @@ internal object RumFeature : SdkFeature<Any, Configuration.Feature.RUM>() {

internal var debugActivityLifecycleListener: Application.ActivityLifecycleCallbacks? = null

internal lateinit var vitalExecutorService: ScheduledThreadPoolExecutor
internal var vitalExecutorService: ScheduledExecutorService = NoOpScheduledExecutorService()
internal lateinit var anrDetectorExecutorService: ExecutorService
internal lateinit var anrDetectorRunnable: ANRDetectorRunnable
internal lateinit var anrDetectorHandler: Handler
Expand All @@ -86,7 +89,8 @@ internal object RumFeature : SdkFeature<Any, Configuration.Feature.RUM>() {
configuration.userActionTrackingStrategy?.let { actionTrackingStrategy = it }
configuration.longTaskTrackingStrategy?.let { longTaskTrackingStrategy = it }

initializeVitalMonitors()
initializeVitalMonitors(configuration.vitalsMonitorUpdateFrequency)

initializeANRDetector()

registerTrackingStrategies(context)
Expand All @@ -109,6 +113,7 @@ internal object RumFeature : SdkFeature<Any, Configuration.Feature.RUM>() {
vitalExecutorService.shutdownNow()
anrDetectorExecutorService.shutdownNow()
anrDetectorRunnable.stop()
vitalExecutorService = NoOpScheduledExecutorService()
}

override fun createPersistenceStrategy(
Expand Down Expand Up @@ -173,16 +178,22 @@ internal object RumFeature : SdkFeature<Any, Configuration.Feature.RUM>() {
longTaskTrackingStrategy.unregister(appContext)
}

private fun initializeVitalMonitors() {
private fun initializeVitalMonitors(frequency: VitalsUpdateFrequency) {
if (frequency == VitalsUpdateFrequency.NEVER) {
return
}
cpuVitalMonitor = AggregatingVitalMonitor()
memoryVitalMonitor = AggregatingVitalMonitor()
frameRateVitalMonitor = AggregatingVitalMonitor()
initializeVitalReaders(frequency.periodInMs)
}

private fun initializeVitalReaders(periodInMs: Long) {
@Suppress("UnsafeThirdPartyFunctionCall") // pool size can't be <= 0
vitalExecutorService = ScheduledThreadPoolExecutor(1)

initializeVitalMonitor(CPUVitalReader(), cpuVitalMonitor)
initializeVitalMonitor(MemoryVitalReader(), memoryVitalMonitor)
initializeVitalMonitor(CPUVitalReader(), cpuVitalMonitor, periodInMs)
initializeVitalMonitor(MemoryVitalReader(), memoryVitalMonitor, periodInMs)

val vitalFrameCallback = VitalFrameCallback(frameRateVitalMonitor) { isInitialized() }
try {
Expand All @@ -199,17 +210,18 @@ internal object RumFeature : SdkFeature<Any, Configuration.Feature.RUM>() {

private fun initializeVitalMonitor(
vitalReader: VitalReader,
vitalObserver: VitalObserver
vitalObserver: VitalObserver,
updateFrequencyInMs: Long
) {
val readerRunnable = VitalReaderRunnable(
vitalReader,
vitalObserver,
vitalExecutorService,
VITAL_UPDATE_PERIOD_MS
updateFrequencyInMs
)
vitalExecutorService.scheduleSafe(
"Vitals monitoring",
VITAL_UPDATE_PERIOD_MS,
updateFrequencyInMs,
TimeUnit.MILLISECONDS,
readerRunnable
)
Expand All @@ -222,8 +234,5 @@ internal object RumFeature : SdkFeature<Any, Configuration.Feature.RUM>() {
anrDetectorExecutorService.executeSafe("ANR detection", anrDetectorRunnable)
}

// Update Vitals every second
private const val VITAL_UPDATE_PERIOD_MS = 100L

// endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ internal class ConfigurationBuilderTest {
viewTrackingStrategy = ActivityViewTrackingStrategy(false),
rumEventMapper = NoOpEventMapper(),
longTaskTrackingStrategy = MainLooperLongTaskStrategy(100L),
backgroundEventTracking = false
backgroundEventTracking = false,
vitalsMonitorUpdateFrequency = VitalsUpdateFrequency.AVERAGE
)
)
assertThat(config.additionalConfig).isEmpty()
Expand Down Expand Up @@ -1643,6 +1644,21 @@ internal class ConfigurationBuilderTest {
assertThat(config.logsConfig).isEqualTo(Configuration.DEFAULT_LOGS_CONFIG)
}

@Test
fun `M use the given frequency W setVitalsMonitorUpdateFrequency`(
@Forgery fakeFrequency: VitalsUpdateFrequency
) {
// When
val config = testedBuilder
.setVitalsUpdateFrequency(fakeFrequency)
.build()

// Then
assertThat(config.rumConfig).isEqualTo(
Configuration.DEFAULT_RUM_CONFIG.copy(vitalsMonitorUpdateFrequency = fakeFrequency)
)
}

private fun Configuration.Builder.setSecurityConfig(
securityConfig: SecurityConfig
): Configuration.Builder {
Expand Down
Loading

0 comments on commit a835ed6

Please sign in to comment.