Overview

Datadog Real User Monitoring (RUM) enables you to visualize and analyze the real-time performance and user journeys of your application’s individual users.

The Datadog Android SDK supports Android 5.0+ (API level 21) and Android TV.

Setup

  1. Declare Datadog RUM SDK as a dependency.
  2. Specify application details in the UI.
  3. Initialize Datadog SDK with application context.
  4. Enable RUM feature to start sending data.
  5. Initialize RUM Interceptor to track network events.

Declare the Datadog RUM SDK as a dependency

Declare dd-sdk-android-rum and the Gradle plugin as a dependency in your application module’s build.gradle file.

buildscript {
    dependencies {
        classpath("com.datadoghq:dd-sdk-android-gradle-plugin:x.x.x")
    }
}
plugins {
    id("com.datadoghq.dd-sdk-android-gradle-plugin")
    //(...)
}
android {
    //(...)
}
dependencies {
    implementation "com.datadoghq:dd-sdk-android-rum:x.x.x" 
    //(...)
}

Specify application details in the UI

  1. Navigate to Digital Experience > Add an Application.

  2. Select android as the application type and enter an application name to generate a unique Datadog application ID and client token.

  3. To instrument your web views, click the Instrument your webviews toggle. For more information, see Web View Tracking.

  4. To disable automatic user data collection for either client IP or geolocation data, uncheck the boxes for those settings. For more information, see RUM Android Data Collected.

    Create a RUM application for Android in Datadog

To ensure the safety of your data, you must use a client token. If you used only Datadog API keys to configure the Datadog SDK, they would be exposed client-side in the Android application’s APK byte code.

For more information about setting up a client token, see the Client Token documentation.

Initialize Datadog SDK with application context

In the initialization snippet, set an environment name, service name, and version number. In the examples below, APP_VARIANT_NAME specifies the variant of the application that generates data. For more information, see Using Tags.

See trackingConsent to add GDPR compliance for your EU users, and other configuration options to initialize the library.

class SampleApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        val configuration = Configuration.Builder(
            clientToken = <CLIENT_TOKEN>,
            env = <ENV_NAME>,
            variant = <APP_VARIANT_NAME>
        ).build()
        Datadog.initialize(this, configuration, trackingConsent)
    }
}
public class SampleApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Configuration configuration =
                new Configuration.Builder(<CLIENT_TOKEN>, <ENV_NAME>, <APP_VARIANT_NAME>)
                        .build();
        Datadog.initialize(this, configuration, trackingConsent);
    }
}

class SampleApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        val configuration = Configuration.Builder(
                clientToken = <CLIENT_TOKEN>,
                env = <ENV_NAME>,
                variant = <APP_VARIANT_NAME>
            )
            .useSite(DatadogSite.EU1)
            .build()
        Datadog.initialize(this, configuration, trackingConsent)
    }
}
public class SampleApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Configuration configuration =
                new Configuration.Builder(<CLIENT_TOKEN>, <ENV_NAME>, <APP_VARIANT_NAME>)
                        .useSite(DatadogSite.EU1)
                        .build();
        Datadog.initialize(this, configuration, trackingConsent);
    }
}

class SampleApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        val configuration = Configuration.Builder(
                clientToken = <CLIENT_TOKEN>,
                env = <ENV_NAME>,
                variant = <APP_VARIANT_NAME>
            )
            .useSite(DatadogSite.US3)
            .build()
        Datadog.initialize(this, configuration, trackingConsent)
    }
}
public class SampleApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Configuration configuration =
                new Configuration.Builder(<CLIENT_TOKEN>, <ENV_NAME>, <APP_VARIANT_NAME>)
                        .useSite(DatadogSite.US3)
                        .build();
        Datadog.initialize(this, configuration, trackingConsent);
    }
}

class SampleApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        val configuration = Configuration.Builder(
                clientToken = <CLIENT_TOKEN>,
                env = <ENV_NAME>,
                variant = <APP_VARIANT_NAME>
            )
            .useSite(DatadogSite.US5)
            .build()
        Datadog.initialize(this, configuration, trackingConsent)
    }
}
public class SampleApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Configuration configuration =
                new Configuration.Builder(<CLIENT_TOKEN>, <ENV_NAME>, <APP_VARIANT_NAME>)
                        .useSite(DatadogSite.US5)
                        .build();
        Datadog.initialize(this, configuration, trackingConsent);
    }
}

class SampleApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        val configuration = Configuration.Builder(
                clientToken = <CLIENT_TOKEN>,
                env = <ENV_NAME>,
                variant = <APP_VARIANT_NAME>
            )
            .useSite(DatadogSite.US1_FED)
            .build()
        Datadog.initialize(this, configuration, trackingConsent)
    }
}
public class SampleApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Configuration configuration =
                new Configuration.Builder(<CLIENT_TOKEN>, <ENV_NAME>, <APP_VARIANT_NAME>)
                        .useSite(DatadogSite.US1_FED)
                        .build();
        Datadog.initialize(this, configuration, trackingConsent);
    }
}

class SampleApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        val configuration = Configuration.Builder(
                clientToken = <CLIENT_TOKEN>,
                env = <ENV_NAME>,
                variant = <APP_VARIANT_NAME>
            )
            .useSite(DatadogSite.AP1)
            .build()
        Datadog.initialize(this, configuration, trackingConsent)
    }
}
public class SampleApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Configuration configuration =
                new Configuration.Builder(<CLIENT_TOKEN>, <ENV_NAME>, <APP_VARIANT_NAME>)
                        .useSite(DatadogSite.AP1)
                        .build();
        Datadog.initialize(this, configuration, trackingConsent);
    }
}

The initialization credentials require your application’s variant name and uses the value of BuildConfig.FLAVOR. With the variant, RUM can match the errors reported from your application with the mapping files uploaded by the Gradle plugin. If you do not have variants, the credentials use an empty string.

The Gradle plugin automatically uploads the appropriate ProGuard mapping.txt file at build time so you can view deobfuscated RUM error stack traces. For more information, see the Track Android Errors.

Sample RUM sessions

To control the data your application sends to Datadog RUM, you can specify a sample rate for RUM sessions while initializing the RUM feature as a percentage between 0 and 100.

val rumConfig = RumConfiguration.Builder(applicationId)
        // Here 75% of the RUM sessions are sent to Datadog
        .setSessionSampleRate(75.0f)
        .build()
Rum.enable(rumConfig)

To be compliant with the GDPR regulation, the SDK requires the tracking consent value at initialization. Tracking consent can be one of the following values:

  • TrackingConsent.PENDING: (Default) The SDK starts collecting and batching the data but does not send it to the collection endpoint. The SDK waits for the new tracking consent value to decide what to do with the batched data.
  • TrackingConsent.GRANTED: The SDK starts collecting the data and sends it to the data collection endpoint.
  • TrackingConsent.NOT_GRANTED: The SDK does not collect any data. You are not able to manually send any logs, traces, or RUM events.

To update the tracking consent after the SDK is initialized, call Datadog.setTrackingConsent(<NEW CONSENT>). The SDK changes its behavior according to the new consent. For example, if the current tracking consent is TrackingConsent.PENDING and you update it to:

  • TrackingConsent.GRANTED: The SDK sends all current batched data and future data directly to the data collection endpoint.
  • TrackingConsent.NOT_GRANTED: The SDK wipes all batched data and does not collect any future data.

Enable RUM feature to start sending data

    val rumConfig = RumConfiguration.Builder(applicationId)
      .trackInteractions()
      .trackLongTasks(durationThreshold)
      .useViewTrackingStrategy(strategy)
      .build()
    Rum.enable(rumConfig)
    RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
      .trackInteractions()
      .trackLongTasks(durationThreshold)
      .useViewTrackingStrategy(strategy)
      .build();
    Rum.enable(rumConfig);

See ViewTrackingStrategy to enable automatic tracking of all your views (activities, fragments, and more).

Initialize RUM Interceptor to track network events

  1. If you want to have distributed tracing, add and enable Trace feature, see Datadog Android Trace Collection documentation to learn how.

  2. Add the Gradle dependency to the dd-sdk-android-okhttp library in the module-level build.gradle file:

    dependencies {
        implementation "com.datadoghq:dd-sdk-android-okhttp:x.x.x"
    }
    
  3. To track your OkHttp requests as resources, add the provided Interceptor:

val tracedHostsWithHeaderType = mapOf(
    "example.com" to setOf(
        TracingHeaderType.DATADOG,
        TracingHeaderType.TRACECONTEXT),
    "example.eu" to  setOf(
        TracingHeaderType.DATADOG,
        TracingHeaderType.TRACECONTEXT))
val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(DatadogInterceptor.Builder(tracedHostsWithHeaderType).build())
    .build()
final Map<String, Set<TracingHeaderType>> tracedHostsWithHeaderType = new HashMap<>();
final Set<TracingHeaderType> datadogAndW3HeadersTypes = new HashSet<>(Arrays.asList(TracingHeaderType.DATADOG, TracingHeaderType.TRACECONTEXT));
tracedHostsWithHeaderType.put("example.com", datadogAndW3HeadersTypes);
tracedHostsWithHeaderType.put("example.eu", datadogAndW3HeadersTypes);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
    .addInterceptor(new DatadogInterceptor.Builder(tracedHostsWithHeaderType).build())
    .build();

This records each request processed by the OkHttpClient as a resource in RUM, with all the relevant information automatically filled (URL, method, status code, and error). Only the network requests that started when a view is active are tracked. To track requests when your application is in the background, create a view manually.

Note: If you also use multiple Interceptors, add DatadogInterceptor first.

You can also add an EventListener for the OkHttpClient to automatically track resource timing for third-party providers and network requests.

Track background events

You can track events such as crashes and network requests when your application is in the background (for example, no active view is available).

Add the following snippet during RUM configuration:

.trackBackgroundEvents(true)
.trackBackgroundEvents(true)

Tracking background events may lead to additional sessions, which can impact billing. For questions, contact Datadog support.

Kotlin Extensions

Closeable extension

You can monitor Closeable instance usage by using useMonitored method, it will report any error happened to Datadog and close the resource afterwards.

val closeable: Closeable = ...
closeable.useMonitored {
    // Your code here
}

Track local assets as RUM resources

You can track access to the assets by using getAssetAsRumResource extension method:

val inputStream = context.getAssetAsRumResource(fileName)

Usage of the local resources can be tracked by using getRawResAsRumResource extension method:

val inputStream = context.getRawResAsRumResource(id)

Sending data when device is offline

RUM ensures availability of data when your user device is offline. In case of low-network areas, or when the device battery is too low, all the RUM events are first stored on the local device in batches.

Each batch follows the intake specification. They are sent as soon as the network is available, and the battery is high enough to ensure the Datadog SDK does not impact the end user’s experience. If the network is not available while your application is in the foreground, or if an upload of data fails, the batch is kept until it can be sent successfully.

This means that even if users open your application while offline, no data is lost. To ensure the SDK does not use too much disk space, the data on the disk is automatically discarded if it gets too old.

Further Reading

PREVIEWING: rtrieu/product-analytics-ui-changes