Overview

Error Tracking processes errors collected from the Android SDK.

Enable Android Crash Reporting and Error Tracking to get comprehensive crash reports and error trends. With this feature, you can access:

  • Aggregated Android crash dashboards and attributes
  • Deobfuscated Android crash reports
  • Trend analysis with Android error tracking

Your crash reports appear in Error Tracking.

Setup

If you have not set up the Android SDK yet, follow the in-app setup instructions or see the Android setup documentation.

  1. Add the latest version of the Android SDK to your Gradle dependencies.
  2. Configure your application’s env and variant when initializing the SDK.
  3. Run the Gradle tasks to upload your Proguard/R8 mapping file and NDK symbol files to Datadog to access deobfuscated stack traces.

For any given error, you can access the file path, line number, and a code snippet for each frame of the related stack trace.

Add NDK crash reporting

Your Android application may be running native code (C/C++) for performance or code reusability reasons. In order to enable NDK crash reporting, use the Datadog NDK plugin.

  1. Add the Gradle dependency by declaring the library as a dependency in your build.gradle file:

     dependencies {
         implementation "com.datadoghq:dd-sdk-android-ndk:x.x.x" 
         //(...)
     }
    
  2. Enable NDK crash collection after initializing the SDK.

    NdkCrashReports.enable()
    

Add ANR reporting

An “Application Not Responding” (ANR) is an Android-specific type of error that gets triggered when the application is unresponsive for too long.

ANRs are only reported through the SDK (not through Logs).

Report fatal ANRs

Fatal ANRs result in crashes. The application reports them when it’s unresponsive, leading to the Android OS displaying a popup dialog to the user, who chooses to force quit the app through the popup.

A fatal crash report in Error Tracking.
  • In the Error Tracking page, fatal ANRs are grouped based on their similarity, which can result into several individual issues being created
  • By default, Datadog catches fatal ANRs through the ApplicationExitInfo API (available since Android 30+), which can be read on the next app launch.
  • In Android 29 and below, reporting on fatal ANRs is not possible.

Report non-fatal ANRs

Non-fatal ANRs may or may not have led to the application being terminated (crashing).

A non-fatal crash report in Error Tracking.
  • In the Error Tracking page, non-fatal ANRs are grouped under a single issue due to their level of noise.
  • By default, the reporting of non-fatal ANRs on Android 30+ is disabled because it would create too much noise over fatal ANRs. On Android 29 and below, however, the reporting of non-fatal ANRs is enabled by default, as fatal ANRs cannot be reported on those versions.

For any Android version, you can override the default setting for reporting non-fatal ANRs by setting trackNonFatalAnrs to true or false when initializing the SDK.

Get deobfuscated stack traces

Mapping files are used to deobfuscate stack traces, which helps in debugging errors. Using a unique build ID that gets generated, Datadog automatically matches the correct stack traces with the corresponding mapping files. This ensures that regardless of when the mapping file was uploaded (either during pre-production or production builds), the correct information is available for efficient QA processes when reviewing crashes and errors reported in Datadog.

Depending on the Android Gradle plugin version, the matching of stack traces and mapping files relies on different fields:

  • Version 1.13.0 uses the build_id field
  • Older versions use a combination of the service, version, and variant fields

Upload your mapping file

Note: Re-uploading a source map does not override the existing one if the version has not changed.

  1. Add the Android Gradle Plugin to your Gradle project using the following code snippet.

    // In your app's build.gradle script
    plugins {
        id("com.datadoghq.dd-sdk-android-gradle-plugin") version "x.y.z"
    }
    
  2. Create a dedicated Datadog API key and export it as an environment variable named DD_API_KEY or DATADOG_API_KEY. Alternatively, pass it as a task property, or if you have datadog-ci.json file in the root of your project, it can be taken from an apiKey property there.

  3. Optionally, configure the plugin to upload files to the EU region by configuring the plugin in your build.gradle script:

    datadog {
        site = "EU1"
    }
    
  4. Run the upload task after your obfuscated APK builds:

    ./gradlew uploadMappingRelease
    
  5. If running native code, run the NDK symbol upload task:

    ./gradlew uploadNdkSymbolFilesRelease
    

Note: If your project uses additional flavors, the plugin provides an upload task for each variant with obfuscation enabled. In this case, initialize the Android SDK with a proper variant name (the necessary API is available in versions 1.8.0 and later).

  1. Add the Android Gradle Plugin to your Gradle project using the following code snippet.

    // In your app's build.gradle script
    plugins {
        id("com.datadoghq.dd-sdk-android-gradle-plugin") version "x.y.z"
    }
    
  2. Create a dedicated Datadog API key and export it as an environment variable named DD_API_KEY or DATADOG_API_KEY. Alternatively, pass it as a task property, or if you have datadog-ci.json file in the root of your project, it can be taken from an apiKey property there.

  3. Configure the plugin to use the EU region by adding the following snippet in your app’s build.gradle script file:

    datadog {
        site = "EU1"
    }
    
  4. Run the upload task after your obfuscated APK builds:

    ./gradlew uploadMappingRelease
    
  5. If running native code, run the NDK symbol upload task:

    ./gradlew uploadNdkSymbolFilesRelease
    

Note: If your project uses additional flavors, the plugin provides an upload task for each variant with obfuscation enabled. In this case, initialize the Android SDK with a proper variant name (the necessary API is available in versions 1.8.0 and later).

List uploaded mapping files

See the RUM Debug Symbols page to view all uploaded symbols.

Plugin configuration options

There are several plugin properties that can be configured through the plugin extension. In case you are using multiple variants, you can set a property value for a specific flavor in the variant.

For example, for a fooBarRelease variant, you can use the following configuration:

datadog {
    foo {
        versionName = "foo"
    }
    bar {
        versionName = "bar"
    }
    fooBar {
        versionName = "fooBar"
    }
}

The task configuration for this variant is merged from all three flavor configurations provided in the following order:

  1. bar
  2. foo
  3. fooBar

This resolves the final value for the versionName property as fooBar.

Property nameDescription
versionNameThe version name of the application (by default, the version declared in the android block of your build.gradle script).
serviceNameThe service name of the application (by default, the package name of your application as declared in the android block of your build.gradle script).
siteThe Datadog site to upload your data to (US1, US3, US5, EU1, US1_FED, or AP1).
remoteRepositoryUrlThe URL of the remote repository where the source code was deployed. If this is not provided, this value is resolved from your Git configuration during the task execution time.
checkProjectDependenciesThis property controls if the plugin should check if the Datadog Android SDK is included in the dependencies. If not, “none” is ignored, “warn” logs a warning, and “fail” fails the build with an error (default).

Integrate with a CI/CD pipeline

By default, the upload mapping task is independent from other tasks in the build graph. Run the task manually when you need to upload mapping.

If you want to run this task in a CI/CD pipeline, and the task is required as part of the build graph, you can set the upload task to run after the mapping file is generated.

For example:

tasks["minify${variant}WithR8"].finalizedBy { tasks["uploadMapping${variant}"] }

Limitations

File sizing

Mapping files are limited to 500 MB. If your project has a mapping file larger than this, use one of the following options to reduce the file size:

Mapping files are limited to 50 MB. If your project has a mapping file larger than this, use one of the following options to reduce the file size:

  • Set the mappingFileTrimIndents option to true. This reduces your file size by 5%, on average.
  • Set a map of mappingFilePackagesAliases: This replaces package names with shorter aliases. Note: Datadog’s stacktrace uses the same alias instead of the original package name, so it’s better to use this option for third party dependencies.
datadog {
    mappingFileTrimIndents = true
    mappingFilePackageAliases = mapOf(
        "kotlinx.coroutines" to "kx.cor",
        "com.google.android.material" to "material",
        "com.google.gson" to "gson",
        "com.squareup.picasso" to "picasso"
    )
}

Collection

When looking at RUM Crash Reporting behaviors for Android, consider the following:

  • The crash can only be detected after the SDK is initialised. Given this, the recommendation is to initialize the SDK as soon as possible in your application’s onCreate method.
  • RUM crashes must be attached to a RUM view. If a crash occurs before a view is visible (typically an Activity or Fragment in an onResume state), or after the app is sent to the background by the end-user navigating away from it, the crash is muted and isn’t reported for collection. To mitigate this, use the trackBackgroundEvents() method in your RumConfiguration builder.
  • Only crashes that occur in sampled sessions are kept, meaning if a session sampling rate is not 100%, some will not be reported.

Test your implementation

To verify your Android Crash Reporting and Error Tracking configuration, you need to trigger a crash in your application and confirm that the error appears in Datadog.

To test your implementation:

  1. Run your application on an Android emulator or a real device.

  2. Execute some code containing an error or crash. For example:

    fun onEvent() {
        throw RuntimeException("Crash the app")
    }
    
  3. After the crash happens, restart your application and wait for the Android SDK to upload the crash report in Error Tracking.

Further Reading