Report Android crashes
EDOT Android can capture unhandled exceptions and report them to your Elastic Stack as crash events. Each event includes exception details, a stacktrace, and session information that helps correlate the crash with other telemetry from the application.
First, set up EDOT Android in your application. Then add the crash reporting instrumentation plugin to the application module's build.gradle.kts file:
plugins {
id("com.android.application")
id("co.elastic.otel.android.agent") version "[latest_version]"
id("co.elastic.otel.android.instrumentation.crash") version "[latest_version]"
}
Find the latest crash reporting plugin version in the Gradle Plugin Portal.
The instrumentation automatically captures crashes when an unhandled exception occurs. Because the application process is terminating, EDOT Android stores the crash event on disk and exports it after EDOT Android initializes the next time the application is launched.
Crash events are available in the "Crashes" section of the Kibana Android dashboard. Refer to Visualize your telemetry for instructions to install and open the dashboard.
Android's R8 optimizer can rename classes and methods in release builds. When an optimized application crashes, its stacktrace contains these obfuscated names. EDOT Android can upload the R8 mapping.txt file for each application build to Elasticsearch so that the stacktrace can be restored to its original class, method, file, and line information.
EDOT Android identifies the correct mapping using the app.build_id resource attribute included with application telemetry. Mapping documents for a build are stored in a Elasticsearch index named .android-r8-mappings-<build_id>.
Deobfuscating stacktraces in Kibana requires version 1.0.0 or later of the Android OpenTelemetry Assets integration. Refer to the integration page for the minimum supported Kibana versions.
- Enable R8 for the variant whose mapping you want to upload.
- Have your Elasticsearch endpoint URL at hand to use it in the Gradle task later.
- Create a dedicated Elasticsearch API key for mapping uploads.
- Install version 1.0.0 or later of the Android OpenTelemetry Assets integration in Kibana. The integration page lists the minimum supported Kibana versions. Refer to Visualize telemetry for installation instructions.
Create an API key specifically for R8 mapping uploads. Don't reuse the API key that the application uses to export telemetry. Keeping these credentials separate limits the impact if a build-time credential is exposed and allows each key to be rotated or revoked independently.
The mapping uploader only needs permission to create mapping indices and index documents into them. Create a least-privilege key in Kibana:
- Open the API keys management page using the navigation menu or the global search field.
- Select Create API key and give the key a descriptive name, such as
edot-android-r8-mapping-upload. - Select Control security privileges.
- Use the following role descriptor to restrict the key to mapping uploads:
{
"r8_mapping_uploader": {
"indices": [
{
"names": [".android-r8-mappings-*"],
"privileges": ["create_index", "index"]
}
]
}
}
- Set an expiration that matches your release process, create the key, and store its encoded value as the
ELASTICSEARCH_API_KEYCI secret.
This API key is a build-time credential. Don't package it in the application or commit it to source control.
The user creating the key must have API key creation permissions and must already hold the index privileges being granted. Refer to Elastic API keys for deployment-specific requirements and instructions.
Add the R8 mapping plugin to the application module's build.gradle.kts file. Use the same version as the EDOT Android agent plugin:
plugins {
id("com.android.application")
id("co.elastic.otel.android.agent") version "[latest_version]"
id("co.elastic.otel.android.mapping") version "[latest_version]"
}
Find the latest mapping plugin version in the Gradle Plugin Portal.
Configure the Elasticsearch endpoint and API key in the elasticOtel block. Gradle providers allow CI to supply these values without storing credentials in the build script:
elasticOtel {
mapping {
elasticsearch {
endpoint.set(providers.environmentVariable("ELASTICSEARCH_ENDPOINT"))
apiKey.set(providers.environmentVariable("ELASTICSEARCH_API_KEY"))
}
}
}
android {
buildTypes {
release {
isMinifyEnabled = true
}
}
}
The endpoint must be the Elasticsearch HTTP endpoint, not the OTLP endpoint used to export telemetry.
By default, EDOT Android generates the build ID as:
sha256("<applicationId>-<versionName>-<versionCode>")
The agent adds this value to telemetry as app.build_id, and the mapping plugin uses the same value in the mapping index name. Increment the application's version code for each release so that every distinct application binary has a unique build ID.
The plugin creates an upload task for every application variant. The task name follows this pattern:
<variant>UploadMappingToElasticsearch
For the release variant, export the credentials and run:
export ELASTICSEARCH_ENDPOINT="https://your-elasticsearch-endpoint"
export ELASTICSEARCH_API_KEY="your-api-key"
./gradlew releaseUploadMappingToElasticsearch
For a flavored variant such as paidRelease, run:
./gradlew paidReleaseUploadMappingToElasticsearch
The upload task:
- Ensures R8 has produced
mapping.txtfor the selected variant. - Converts the mapping into one Elasticsearch document per obfuscated class.
- Creates
.android-r8-mappings-<build_id>if it doesn't already exist. - Uploads the documents using the Elasticsearch Bulk API.
Mapping upload is manual and isn't attached to assemble or bundle. Run the upload task in the release workflow for every optimized application binary that you distribute.
Uploading the same build again updates documents with deterministic IDs instead of creating duplicates.
After the crash event and its corresponding R8 mapping are available in your Elastic Stack, you can retrace the stacktrace from the Kibana Android dashboard. Refer to Deobfuscating stacktraces for instructions to open the crash details and view the original class names, methods, source files, and line numbers.
No mapping file is generated
: Confirm that R8 is enabled for the selected variant with isMinifyEnabled = true, and invoke the task for that variant.
Elasticsearch rejects the request
: Confirm that the endpoint is the Elasticsearch HTTP endpoint and that the API key can create and write to .android-r8-mappings-* indices.
The mapping can't be associated with a crash
: Confirm that the mapping index suffix matches the crash event's app.build_id.