- 필수 기능
- 시작하기
- Glossary
- 표준 속성
- Guides
- Agent
- 통합
- 개방형텔레메트리
- 개발자
- Administrator's Guide
- API
- Datadog Mobile App
- CoScreen
- Cloudcraft
- 앱 내
- 서비스 관리
- 인프라스트럭처
- 애플리케이션 성능
- APM
- Continuous Profiler
- 스팬 시각화
- 데이터 스트림 모니터링
- 데이터 작업 모니터링
- 디지털 경험
- 소프트웨어 제공
- 보안
- AI Observability
- 로그 관리
- 관리
",t};e.buildCustomizationMenuUi=t;function n(e){let t='
",t}function s(e){let n=e.filter.currentValue||e.filter.defaultValue,t='${e.filter.label}
`,e.filter.options.forEach(s=>{let o=s.id===n;t+=``}),t+="${e.filter.label}
`,t+=`Android Error Tracking gives you comprehensive visibility into your mobile app’s health by automatically capturing crashes, exceptions, and errors. With this feature, you can:
The Datadog Android SDK supports Android 5.0+ (API level 21) and Android TV.
Your crash reports appear in Error Tracking.
If you have not set up the Android SDK yet, follow the in-app setup instructions or see the Android setup documentation.
Declare dd-sdk-android-rum and the Gradle plugin as dependencies 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"
//(...)
}
android
as the application type and enter an application name to generate a unique Datadog application ID and client token.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.
During initialization, you can also set the sample rate (RUM sessions) and set the tracking consent for GDPR compliance, as described below. See 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);
}
}
class SampleApplication : Application() {
override fun onCreate() {
super.onCreate()
val configuration = Configuration.Builder(
clientToken = <CLIENT_TOKEN>,
env = <ENV_NAME>,
variant = <APP_VARIANT_NAME>
)
.useSite(DatadogSite.AP2)
.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.AP2)
.build();
Datadog.initialize(this, configuration, trackingConsent);
}
}
The initialization credentials require your application’s variant name and use the value of BuildConfig.FLAVOR
. With the variant, the SDK can match the errors reported from your application to 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 error stack traces. For more information, see the Upload your mapping file section.
To enable the Android SDK to start sending data:
val rumConfig = RumConfiguration.Builder(applicationId)
.trackInteractions()
.trackLongTasks(durationThreshold) // Not applicable to Error Tracking
.useViewTrackingStrategy(strategy)
.build()
Rum.enable(rumConfig)
RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
.trackInteractions()
.trackLongTasks(durationThreshold) // Not applicable to Error Tracking
.useViewTrackingStrategy(strategy)
.build();
Rum.enable(rumConfig);
See ViewTrackingStrategy
to enable automatic tracking of all your views (activities, fragments, and more).
If your Android application uses WebViews to display web content, you can instrument them to track JavaScript errors and crashes that occur within the web content.
To instrument your web views:
Add the Gradle dependency by declaring the dd-sdk-android-webview as dependency in your build.gradle file:
dependencies {
implementation "com.datadoghq:dd-sdk-android-webview:<latest_version>"
}
Enable webview tracking for a given WebView instance by providing a list of hosts to track:
WebViewTracking.enable(webView, hosts)
For more information, see Web View Tracking.
If your Android app uses native code (C/C++) through the Android NDK (Native Development Kit), you can track crashes that occur in this native code. Native code is often used for performance-critical operations, image processing, or when reusing existing C/C++ libraries.
Without NDK crash reporting, crashes in your native code do not appear in Error Tracking, making it difficult to debug issues in this part of your application.
To enable NDK crash reporting, use the Datadog NDK plugin:
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")
//(...)
}
Enable NDK crash collection after initializing the SDK:
NdkCrashReports.enable()
An “Application Not Responding” (ANR) is an Android-specific type of error that gets triggered when the application is unresponsive for too long. You can add ANR reporting to your RUM configuration to monitor these application responsiveness issues.
To enable ANR reporting, add the following to your RUM configuration:
val rumConfig = RumConfiguration.Builder(applicationId)
.trackInteractions()
.trackLongTasks(durationThreshold)
.trackNonFatalAnrs(true) // Enable non-fatal ANR reporting
.useViewTrackingStrategy(strategy)
.build()
Rum.enable(rumConfig)
RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
.trackInteractions()
.trackLongTasks(durationThreshold)
.trackNonFatalAnrs(true) // Enable non-fatal ANR reporting
.useViewTrackingStrategy(strategy)
.build();
Rum.enable(rumConfig);
ANRs are only reported through the SDK (not through Logs).
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.
Non-fatal ANRs may or may not have led to the application being terminated (crashing).
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.
When your Android app is built for production, the code is typically obfuscated using ProGuard or R8 to reduce app size and protect intellectual property. This obfuscation makes stack traces in crash reports unreadable, showing meaningless class and method names like a.b.c()
instead of com.example.MyClass.myMethod()
.
To make these stack traces readable for debugging, you need to upload your mapping files to Datadog. These files contain the mapping between obfuscated and original code, allowing Datadog to automatically deobfuscate stack traces in your error reports.
Datadog uses a unique build ID generated for each build to automatically match stack traces with the correct mapping files. This ensures that:
The matching process depends on your Android Gradle plugin version:
build_id
field (requires Datadog Android SDK 2.8.0 or later)service
, version
, and variant
fieldsThe Android Gradle plugin automates the mapping file upload process. After configuration, it automatically uploads the appropriate ProGuard/R8 mapping file for each build variant when you build your app.
Note: Re-uploading a mapping file does not override the existing one if the version has not changed. For information about file size limitations and other constraints, see the Limitations section.
After configuring the plugin, run the Gradle tasks to upload your Proguard/R8 mapping file and NDK symbol files to Datadog:
./gradlew uploadMappingRelease
./gradlew uploadNdkSymbolFilesRelease
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 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"
}
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.
Optionally, configure the plugin to upload files to the EU region by configuring the plugin in your build.gradle
script:
datadog {
site = "EU1"
}
Run the upload task after your obfuscated APK builds:
./gradlew uploadMappingRelease
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).
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"
}
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.
Configure the plugin to use the EU region by adding the following snippet in your app’s build.gradle
script file:
datadog {
site = "EU1"
}
Run the upload task after your obfuscated APK builds:
./gradlew uploadMappingRelease
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).
See the RUM Debug Symbols page to view all uploaded symbols.
To be compliant with the GDPR regulation, the SDK requires the tracking consent value upon 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 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.
To control the data your application sends to Datadog, you can specify a sample rate for sessions when initializing RUM. The sample rate is a percentage between 0 and 100. By default, sessionSamplingRate
is set to 100 (keep all sessions).
val rumConfig = RumConfiguration.Builder(applicationId)
// Here 75% of the RUM sessions are sent to Datadog
.setSessionSampleRate(75.0f)
.build()
Rum.enable(rumConfig)
The network interceptor automatically tracks HTTP requests and responses, capturing network errors, timeouts, and performance issues that can help you correlate network problems with app crashes and user experience issues. To initialize an interceptor for tracking network events:
For distributed tracing, add and enable the Trace feature.
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"
}
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()
Map<String, Set<TracingHeaderType>> tracedHostsWithHeaderType = new HashMap<>();
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();
To automatically create RUM resources and spans for your OkHttp requests, use the DatadogInterceptor
as an interceptor.
OkHttpClient
as a resource, with all the relevant information (URL, method, status code, and error) automatically filled in. 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.To monitor the network redirects or retries, you can use the DatadogInterceptor
as a network interceptor:
val okHttpClient = OkHttpClient.Builder()
.addNetworkInterceptor(DatadogInterceptor.Builder(tracedHostsWithHeaderType).build())
.build()
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addNetworkInterceptor(new DatadogInterceptor.Builder(tracedHostsWithHeaderType).build())
.build();
Notes:
TracingInterceptor
instead of DatadogInterceptor
as described above.DatadogInterceptor
first.You can also add an EventListener
for the OkHttpClient
to automatically track resource timing for third-party providers and network requests.
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 configuration:
.trackBackgroundEvents(true)
.trackBackgroundEvents(true)
Tracking background events may lead to additional sessions, which can impact billing. For questions, contact Datadog support.
The Android SDK 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 events are first stored on the local device in batches.
Each batch follows the intake specification. Batches 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.
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:
bar
foo
fooBar
This resolves the final value for the versionName
property as fooBar
.
Property name | Description |
---|---|
versionName | The version name of the application (by default, the version declared in the android block of your build.gradle script). |
serviceName | The service name of the application (by default, the package name of your application as declared in the android block of your build.gradle script). |
site | The Datadog site to upload your data to (US1, US3, US5, EU1, US1_FED, AP1, or AP2). |
remoteRepositoryUrl | The 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. |
checkProjectDependencies | This 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). |
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}"] }
Mapping files are limited in size to 500 MB each. If your project has a mapping file larger than this, use one of the following options to reduce the file size:
mappingFileTrimIndents
option to true
. This reduces your file size by 5%, on average.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"
)
}
When looking at RUM Crash Reporting behaviors for Android, consider the following:
onCreate
method.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.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:
Run your application on an Android emulator or a real device.
Execute some code containing an error or crash. For example:
fun onEvent() {
throw RuntimeException("Crash the app")
}
After the crash happens, restart your application and wait for the Android SDK to upload the crash report in Error Tracking.
Closeable
extensionYou can monitor Closeable
instance usage with the useMonitored
method, which reports errors to Datadog and closes the resource afterwards:
val closeable: Closeable = ...
closeable.useMonitored {
// Your code here
}
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)