Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
758845b
feat(rum): let sampling rates be set remotely instead of only at init
Fiona2016 Aug 15, 2026
488e9f1
feat(rum): ask for the sampling settings again when the app returns
Fiona2016 Aug 16, 2026
915f0ef
feat(rum): report which settings version the app is running
Fiona2016 Aug 16, 2026
c65c532
fix(rum): only refresh on foreground when the server allows it
Fiona2016 Aug 17, 2026
e63ad9d
feat(rum): let the host application force a session to be collected
Fiona2016 Aug 19, 2026
307c427
feat(rum): deliver the console's custom values to the host application
Fiona2016 Aug 19, 2026
dc1ad6b
refactor(rum): rename the remote sampling channel to remote configura…
Fiona2016 Aug 24, 2026
1eee2d5
feat(rum): fetch remote configuration per session instead of polling
Fiona2016 Aug 24, 2026
5582bd4
feat(rum): revalidate the stored configuration instead of refetching it
Fiona2016 Aug 24, 2026
b620775
feat(rum): report the configuration a session was drawn under on its …
Fiona2016 Aug 24, 2026
6a04de3
chore(rum): record rcVersion in the API surface
Fiona2016 Aug 24, 2026
f21498c
refactor(rum): drop remote delivery of the Session Replay sample rate
Fiona2016 Aug 24, 2026
3f6e630
refactor(rum): return the console's custom values decoded
Fiona2016 Aug 24, 2026
a729603
refactor(rum): drop what the narrowed scope left behind
Fiona2016 Aug 25, 2026
beba6f7
fix(rum): refuse configuration responses this SDK cannot vouch for
Fiona2016 Aug 27, 2026
b3afd61
feat(rum): let the application have the last word on the session draw
Fiona2016 Aug 27, 2026
a0a744e
refactor(rum): drop the draw record nothing reads
Fiona2016 Aug 27, 2026
adce02c
fix(rum): read a configuration that carries no schema stamp
Fiona2016 Aug 27, 2026
2665e54
refactor(rum): keep only the part of the draw record anything reads
Fiona2016 Aug 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion features/dd-sdk-android-rum/api/apiSurface
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
data class com.datadog.android.rum.BeforeSamplingContext
constructor(Float, Map<String, Any?>?)
interface com.datadog.android.rum.BeforeSamplingCallback
fun sampleRate(BeforeSamplingContext): Float?
fun <T: java.io.Closeable, R> T.useMonitored(com.datadog.android.api.SdkCore = Datadog.getInstance(), (T) -> R): R
annotation com.datadog.android.rum.ExperimentalRumApi
object com.datadog.android.rum.GlobalRumMonitor
Expand Down Expand Up @@ -62,6 +66,8 @@ data class com.datadog.android.rum.RumConfiguration
class Builder
constructor(String)
fun setSessionSampleRate(Float): Builder
fun setRemoteConfigurationEnabled(Boolean): Builder
fun setBeforeSampling(BeforeSamplingCallback): Builder
fun collectAccessibility(Boolean): Builder
fun setTelemetrySampleRate(Float): Builder
fun trackUserInteractions(Array<com.datadog.android.rum.tracking.ViewAttributesProvider> = emptyArray(), com.datadog.android.rum.tracking.InteractionPredicate = NoOpInteractionPredicate()): Builder
Expand Down Expand Up @@ -116,6 +122,8 @@ interface com.datadog.android.rum.RumMonitor
fun getAttributes(): Map<String, Any?>
fun clearAttributes()
fun stopSession()
fun setForcedSession()
fun getRemoteConfig(): Map<String, Any?>?
fun addViewLoadingTime(Boolean)
fun addViewAttributes(Map<String, Any?>)
fun removeViewAttributes(Collection<String>)
Expand Down Expand Up @@ -1838,7 +1846,7 @@ data class com.datadog.android.rum.model.ViewEvent
fun fromJson(kotlin.String): DdSession
fun fromJsonObject(com.google.gson.JsonObject): DdSession
data class Configuration
constructor(kotlin.Number, kotlin.Number? = null, kotlin.Number? = null, kotlin.Boolean? = null)
constructor(kotlin.Number, kotlin.Number? = null, kotlin.Number? = null, kotlin.Boolean? = null, kotlin.Long? = null)
fun toJson(): com.google.gson.JsonElement
companion object
fun fromJson(kotlin.String): Configuration
Expand Down
31 changes: 27 additions & 4 deletions features/dd-sdk-android-rum/api/dd-sdk-android-rum.api
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
public abstract interface class com/datadog/android/rum/BeforeSamplingCallback {
public abstract fun sampleRate (Lcom/datadog/android/rum/BeforeSamplingContext;)Ljava/lang/Float;
}

public final class com/datadog/android/rum/BeforeSamplingContext {
public fun <init> (FLjava/util/Map;)V
public final fun component1 ()F
public final fun component2 ()Ljava/util/Map;
public final fun copy (FLjava/util/Map;)Lcom/datadog/android/rum/BeforeSamplingContext;
public static synthetic fun copy$default (Lcom/datadog/android/rum/BeforeSamplingContext;FLjava/util/Map;ILjava/lang/Object;)Lcom/datadog/android/rum/BeforeSamplingContext;
public fun equals (Ljava/lang/Object;)Z
public final fun getCustom ()Ljava/util/Map;
public final fun getSessionSampleRate ()F
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/datadog/android/rum/CloseableExtKt {
public static final fun useMonitored (Ljava/io/Closeable;Lcom/datadog/android/api/SdkCore;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static synthetic fun useMonitored$default (Ljava/io/Closeable;Lcom/datadog/android/api/SdkCore;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/Object;
Expand Down Expand Up @@ -103,10 +120,12 @@ public final class com/datadog/android/rum/RumConfiguration$Builder {
public final fun collectAccessibility (Z)Lcom/datadog/android/rum/RumConfiguration$Builder;
public final fun disableUserInteractionTracking ()Lcom/datadog/android/rum/RumConfiguration$Builder;
public final fun setActionEventMapper (Lcom/datadog/android/event/EventMapper;)Lcom/datadog/android/rum/RumConfiguration$Builder;
public final fun setBeforeSampling (Lcom/datadog/android/rum/BeforeSamplingCallback;)Lcom/datadog/android/rum/RumConfiguration$Builder;
public final fun setErrorEventMapper (Lcom/datadog/android/event/EventMapper;)Lcom/datadog/android/rum/RumConfiguration$Builder;
public final fun setInitialResourceIdentifier (Lcom/datadog/android/rum/metric/networksettled/InitialResourceIdentifier;)Lcom/datadog/android/rum/RumConfiguration$Builder;
public final fun setLastInteractionIdentifier (Lcom/datadog/android/rum/metric/interactiontonextview/LastInteractionIdentifier;)Lcom/datadog/android/rum/RumConfiguration$Builder;
public final fun setLongTaskEventMapper (Lcom/datadog/android/event/EventMapper;)Lcom/datadog/android/rum/RumConfiguration$Builder;
public final fun setRemoteConfigurationEnabled (Z)Lcom/datadog/android/rum/RumConfiguration$Builder;
public final fun setResourceEventMapper (Lcom/datadog/android/event/EventMapper;)Lcom/datadog/android/rum/RumConfiguration$Builder;
public final fun setSessionListener (Lcom/datadog/android/rum/RumSessionListener;)Lcom/datadog/android/rum/RumConfiguration$Builder;
public final fun setSessionSampleRate (F)Lcom/datadog/android/rum/RumConfiguration$Builder;
Expand Down Expand Up @@ -161,10 +180,12 @@ public abstract interface class com/datadog/android/rum/RumMonitor {
public abstract fun getAttributes ()Ljava/util/Map;
public abstract fun getCurrentSessionId (Lkotlin/jvm/functions/Function1;)V
public abstract fun getDebug ()Z
public abstract fun getRemoteConfig ()Ljava/util/Map;
public abstract fun removeAttribute (Ljava/lang/String;)V
public abstract fun removeViewAttributes (Ljava/util/Collection;)V
public abstract fun reportAppFullyDisplayed ()V
public abstract fun setDebug (Z)V
public abstract fun setForcedSession ()V
public abstract fun startAction (Lcom/datadog/android/rum/RumActionType;Ljava/lang/String;Ljava/util/Map;)V
public abstract fun startFeatureOperation (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V
public abstract fun startResource (Ljava/lang/String;Lcom/datadog/android/rum/RumResourceMethod;Ljava/lang/String;Ljava/util/Map;)V
Expand Down Expand Up @@ -4942,18 +4963,20 @@ public final class com/datadog/android/rum/model/ViewEvent$Companion {

public final class com/datadog/android/rum/model/ViewEvent$Configuration {
public static final field Companion Lcom/datadog/android/rum/model/ViewEvent$Configuration$Companion;
public fun <init> (Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Boolean;)V
public synthetic fun <init> (Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Boolean;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Boolean;Ljava/lang/Long;)V
public synthetic fun <init> (Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Boolean;Ljava/lang/Long;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/Number;
public final fun component2 ()Ljava/lang/Number;
public final fun component3 ()Ljava/lang/Number;
public final fun component4 ()Ljava/lang/Boolean;
public final fun copy (Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Boolean;)Lcom/datadog/android/rum/model/ViewEvent$Configuration;
public static synthetic fun copy$default (Lcom/datadog/android/rum/model/ViewEvent$Configuration;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Boolean;ILjava/lang/Object;)Lcom/datadog/android/rum/model/ViewEvent$Configuration;
public final fun component5 ()Ljava/lang/Long;
public final fun copy (Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Boolean;Ljava/lang/Long;)Lcom/datadog/android/rum/model/ViewEvent$Configuration;
public static synthetic fun copy$default (Lcom/datadog/android/rum/model/ViewEvent$Configuration;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Boolean;Ljava/lang/Long;ILjava/lang/Object;)Lcom/datadog/android/rum/model/ViewEvent$Configuration;
public fun equals (Ljava/lang/Object;)Z
public static final fun fromJson (Ljava/lang/String;)Lcom/datadog/android/rum/model/ViewEvent$Configuration;
public static final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/rum/model/ViewEvent$Configuration;
public final fun getProfilingSampleRate ()Ljava/lang/Number;
public final fun getRcVersion ()Ljava/lang/Long;
public final fun getSessionReplaySampleRate ()Ljava/lang/Number;
public final fun getSessionSampleRate ()Ljava/lang/Number;
public final fun getStartSessionReplayRecordingManually ()Ljava/lang/Boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,12 @@
"type": "boolean",
"description": "Whether session replay recording configured to start manually",
"readOnly": true
},
"rc_version": {
"type": "integer",
"description": "FlashCat fork - version of the remote configuration the session was drawn under; 0 when none was ever fetched",
"minimum": 0,
"readOnly": true
}
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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

/**
* What the SDK is about to draw a new session with, handed to [BeforeSamplingCallback]: the rate
* that would apply (the console's where it published one, the value passed to init where it did
* not) and the console's custom values, decoded.
*
* @param sessionSampleRate the rate, between 0 and 100, that would decide this session.
* @param custom the console's custom values, or null when remote configuration is off or nothing
* is published. Same content as [RumMonitor.getRemoteConfig].
*/
data class BeforeSamplingContext(
val sessionSampleRate: Float,
val custom: Map<String, Any?>?
)

/**
* The application's last word on session sampling, called synchronously each time a new session is
* about to be drawn.
*
* Return a rate to override the one the SDK was going to use — 100 always collects, 0 never does —
* or null to leave it alone. The typical use is an allow-list: keep every session of the handful of
* users you are debugging while the fleet stays at a low rate.
*
* It runs inside session creation, so it must be fast and must not block. A throw, or a rate
* outside 0..100, is ignored and the incoming rate applies: a mistake here must never take a
* customer's collection down with it. A session already under way is never re-decided.
*/
fun interface BeforeSamplingCallback {

/**
* @param context the rate that would apply and the console's custom values.
* @return the rate to draw this session with, or null to keep [BeforeSamplingContext.sessionSampleRate].
*/
fun sampleRate(context: BeforeSamplingContext): Float?
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ object Rum {
sdkCore = sdkCore,
sessionEndedMetricDispatcher = sessionEndedMetricDispatcher,
sampleRate = rumFeature.sampleRate,
remoteConfig = rumFeature.remoteConfigStore,
// FLASHCAT FORK - looked up when it fires rather than captured now: a session start
// simply asks again, and there is nothing to ask with when the app did not opt in.
onSessionDrawn = { rumFeature.remoteConfigController?.onSessionStarted() },
beforeSampling = rumFeature.configuration.beforeSampling,
writer = rumFeature.dataWriter,
handler = Handler(Looper.getMainLooper()),
telemetryEventHandler = TelemetryEventHandler(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,45 @@ data class RumConfiguration internal constructor(
return this
}

/**
* Take the sampling rates from the application's settings in the Flashcat console instead
* of only from the values set here, so they can be changed without shipping a new release
* of this app.
*
* A change applies to sessions started after it arrives; a session already under way keeps
* the decision it was created with, unless the console asks for immediate activation, in
* which case the running session ends and a new one starts under the new rates. The values
* set here stay in use until the first settings arrive, and whenever they cannot be
* reached.
*
* Disabled by default: left off, the SDK makes no extra request and behaves exactly as it
* did before this existed.
*
* @param enabled whether the console may set the sampling rates.
*/
fun setRemoteConfigurationEnabled(enabled: Boolean): Builder {
rumConfig = rumConfig.copy(remoteConfigurationEnabled = enabled)
return this
}

/**
* Have the last word on session sampling.
*
* The callback runs synchronously each time a new session is about to be drawn, with the
* rate that would apply and the console's custom values; return a rate to override it, or
* null to leave it alone. The typical use is an allow-list: keep every session of the
* handful of users you are debugging while the fleet stays at a low rate.
*
* It is the last step of the draw, after the console's rate, precisely so an allow-list can
* keep collecting a visitor the console's rate would drop.
*
* @param callback the hook to consult at every draw.
*/
fun setBeforeSampling(callback: BeforeSamplingCallback): Builder {
rumConfig = rumConfig.copy(beforeSampling = callback)
return this
}

/**
* Whether to collect accessibility attributes - this is disabled by default.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,26 @@ interface RumMonitor {
*/
fun stopSession()

/**
* Forces the session to be collected, with Session Replay, regardless of the configured sample
* rates. Call it when your own code decides a user needs debugging (an allow-list, a support
* flow). The current session is restarted so collection starts from a clean session; calling
* again while the forced session is running does nothing. The forced state lasts for the
* process lifetime, so decide on each app start whether to call again.
*/
fun setForcedSession()

/**
* Returns the custom values published for this application in the console, or null when
* nothing is published or remote configuration is off. The SDK delivers them verbatim and
* never interprets them - what a value means is entirely up to your own code (a debug
* allow-list to pair with [setForcedSession], a feature toggle). Nested objects and arrays
* come back as [Map] and [List]. Values are cached locally, so what a previous launch fetched
* answers immediately on the next. The content is readable by anyone holding the public client
* token - it is public information.
*/
fun getRemoteConfig(): Map<String, Any?>?

/**
* Adds view loading time to the active view based on the time elapsed since the view was started.
* The view loading time is automatically calculated as the difference between the current time
Expand Down
Loading
Loading