People are handling more and more matters on their smartphones through mobile apps, both privately and professionally. With thousands or even millions of users, ensuring great performance and reliability is a key challenge for mobile app teams and the backend services they depend on. Understanding real user impact, crash patterns, and the root causes of slow response times is fundamental to managing mobile app quality.
The challenge deepens when something goes wrong. A crash on the device, a slow screen, or an error response might originate in the Android app itself, in a backend service, or somewhere in the network path between them. Debugging these problems without a connected, E2E view from the mobile client to the backend is time-consuming and frustrating. And without a standard instrumentation format, mobile teams often end up maintaining separate tooling that doesn't integrate with what the backend and infrastructure teams already use.
OpenTelemetry offers a way out: a unified, open-standard instrumentation model that works across platforms and languages, backed by a large community. The Elastic Distribution of OpenTelemetry Android, or EDOT Android, is an APM agent for native Android applications built on top of OpenTelemetry. It gives Android teams a practical path to observe mobile app behavior in Elastic, providing them with distributed tracing, crash reporting, session tracking, disk buffering, and automatic instrumentation, with as little code as possible while staying grounded in open standards.
To see what it all looks like, we will instrument a demo Android weather application end to end. You will run Elasticsearch, Kibana, and the Elastic Agent locally. The Elastic Agent provides the OTLP endpoint that receives telemetry from the Android app and backend. You will then generate distributed traces, custom spans, logs, and Android crashes from the app, and explore the results in Kibana using the Android OpenTelemetry dashboards.
This article focuses on a hands-on experiment to explore the E2E experience of observing Android apps with Elastic, using the EDOT Android agent. For more specific details on the EDOT Android agent, such as a list of supported features and a setup guide for your own Android project, take a look at EDOT Android docs.
Setting up EDOT Android with Elasticsearch and Kibana
We will use the EDOT Android demo application. The demo is intentionally small but covers the main workflows you need when evaluating mobile observability with Elastic.
The demo has two main components: an Android app, and a Spring Boot backend. Additionally, you'll need an Elastic Stack environment up and running; we'll explain more about how to get one later in this guide.
Prerequisites
- Java 17 or higher.
- Docker.
- Android Studio.
- An Android emulator.
- On Windows, use Windows Subsystem for Linux (WSL) to run the demo scripts.
Step 1: Clone the demo app's repository
We'll start by cloning the EDOT Android demo application:
git clone git@github.com:elastic/android-agent-demo.git
Step 2: Start the Elastic Stack
The demo uses start-local to run Elasticsearch, Kibana, and the Elastic Agent with a single command. In this setup, the Elastic Agent provides the OTLP endpoint that receives telemetry from the application and backend. Run this from the directory where you want the local Elastic files to be created:
curl -fsSL https://elastic.co/start-local | sh -s -- --edot
For more information on this step, take a look at the demo app's instructions.
Step 3: Start the local backend
The demo backend is a Spring Boot service instrumented with the EDOT Java agent. It handles the app's weather requests and calls the Open-Meteo public API for weather data.
./backend-manager start
For more information on managing the backend service, take a look at the demo app's instructions.
Step 4: Launch the Android application
Use Android Studio to open up the EDOT Android demo application repo and run the application in your emulator. More info on how to run Android apps from Android Studio here.
Generating distributed traces, errors and crashes from an Android app
The Android app has two screens: a city selector and a weather display screen that shows the current weather for the selected city on the previous screen. It includes two intentional failure paths: the first one is reached by selecting New York, which causes the backend to reject the request (the demo backend only supports European cities), and tapping the floating crash button intentionally crashes the app so you can review crash reporting in Kibana after relaunch. We'll take a look at those use cases in more detail below.
Tracing a successful request end to end
In the EDOT Android demo app, selecting "Paris" as the city triggers a successful backend request on the second screen, for which a span will be automatically generated using EDOT Android's OkHttp auto-instrumentation, which supports all OkHttp-generated HTTP requests and tools using it, such as Retrofit. Aside from the Android HTTP span, the successful city request continues e2e and creates a backend HTTP client span to Open-Meteo.
How backend errors appear in the Android distributed trace
The demo backend only supports European cities, so selecting "New York" causes it to fail, which in turn automatically creates an error associated with our Android app's HTTP span. This is done automatically. We'll see later how to find and inspect these issues from Kibana.
How EDOT Android captures and reports app crashes
The crash button creates a crash event that appears in Kibana after the app is reopened. This event contains session information that will help us narrow down its root cause from Kibana, as we'll see later.
Note: EDOT Android automatically attaches Android session context to spans and logs. That means that any span or log created before the crash can be reviewed together with the crash event and nearby spans from the same session, giving you a complete picture of what the user was doing. This even applies to manually created spans and logs.
Visualizing Android application monitoring data in Kibana
To see the whole story from our Android app in a single place, we'll install Kibana's Android OpenTelemetry Assets package by following the steps below.
- In Kibana, search for "Android OpenTelemetry Assets" in the global search field.
- Open it and click Install to add the Android dashboards to your Kibana instance.
Exploring Android application monitoring dashboards in Kibana
Once the content package is installed, open the [Android OTel] Application Overview dashboard:
- In Kibana, search for Dashboards in the global search field or in Kibana's menu.
- In Dashboards, search for Android OTel and open the "[Android OTel] Application Overview" dashboard.
- Select your application from the Applications panel at the top of the dashboard.
The dashboard provides a set of metric panels for an overview of your app's health, performance, and RUM, as well as a set of panels that can be further explored either in Discover or the Exception dashboard, as explained below.
How to inspect the distributed tracing waterfall in Kibana
From the Application Overview dashboard, go to one of the span tables (either All spans or Failed spans) and click its Explore in Discover button.
In Discover, click the expand icon on the left side of any span row to open its details panel. The trace waterfall UI appears inside, showing the full span hierarchy and timing for that trace. You can expand the waterfall to full screen and drill down from there.
Analyzing failed spans and backend errors in Kibana
While you can find all kinds of spans in the dashboard's All spans panel, you can narrow them down to failed ones only by exploring the Failed spans panel instead.
For the New York path, find a failed span and expand it. The trace waterfall highlights the backend error, and the exception details show the intentional backend rule that only supports European cities.
Reviewing crash details and stacktraces in the exception dashboard
Crash reporting is provided by the crash automatic instrumentation plugin. When an unhandled exception crashes the app, EDOT Android stores the crash event on disk. The event is exported the next time the app starts. Disk buffering ensures the crash event is not lost even if the network was unavailable at the time of the crash.
In the Application Overview dashboard, scroll to the Crashes section. You will see crash groups listed by a computed stacktrace group ID. Select a group and click View crash details to open the Exception Details dashboard.
The Exception Details dashboard shows a set of metrics to better understand the impact of the selected crash, as well as its full stacktrace. Crash events are grouped based on their stacktrace, which helps ensure that the same crash is counted and aggregated in this dashboard to better understand a single crash's impact on your application.
For this demo, the stacktrace points to the intentional crash in MainActivity. The nearby session events should also include the custom Crash button click log created just before the crash, which helps explain how the crash was triggered. We'll take a look at how to inspect a session to get an idea of the user's journey within your application that led them to a crash.
Using sessions to understand user flow in EDOT Android
Mobile troubleshooting often starts with a single bad outcome (a crash, an error, a slow UX), but the useful question is what happened before that outcome. EDOT Android helps answer that by attaching session.id to every span and log emitted by the application, even for manually created ones.
A session is meant to cover a single user interaction with your application. A new one is created when there is no previous active session or when the previous session has expired. Sessions expire after 30 minutes of inactivity. If the app stays active, a session can last up to 4 hours.
This lets you query all the telemetry from a single session and review it in order. After finding a crash group, drill into one affected session from the Top affected sessions panel and review the event timeline. You can see the custom logs, app startup spans, HTTP request spans, and crash data together in one investigation path.
The Event timeline panel on the Application Overview dashboard is also useful here: select a session from the dashboard's top filters, and the timeline shows the full sequence of spans and logs in that session chronologically.
What EDOT Android adds as a mobile APM for production apps
The demo uses a local stack and simple code, but the same agent features apply to production apps.
Disk buffering stores telemetry locally before export. This reduces data loss when the device has poor connectivity or the app is temporarily offline.
Automatic instrumentation creates telemetry for supported targets without adding code around every call. Today that includes OkHttp, crash reporting, and an adapter for OpenTelemetry Android instrumentation.
Manual instrumentation lets you add spans, logs, and metrics for app-specific workflows. This is useful for screen loading times, checkout flows, login steps, feature usage, or any area where framework-level telemetry alone is not enough.
Central configuration can remotely adjust selected EDOT Android behavior through Kibana when the OpAMP endpoint is configured. At the time of writing, central configuration for EDOT Android is in preview and supports settings such as recording and session sample rate.
Distributed tracing connects Android app requests to backend service spans so you can trace the full path of any user action, from the tap on the screen to the database query on the server. EDOT Android ensures that your application's telemetry timestamps are in sync with the coordinated universal time. This ensures a proper trace waterfall hierarchy later on in Kibana, where different components are properly coordinated in time.
Clean up the demo
When you are finished, stop the backend in case you're planning to restart it later, or uninstall it otherwise:
./backend-manager stop
# ./backend-manager uninstall
Then stop or uninstall the local Elastic Stack:
cd elastic-start-local
./stop.sh
# ./uninstall.sh
Getting started with EDOT Android in your own app
EDOT Android gives native Android teams an OpenTelemetry-based path for mobile APM in Elastic. With a small Gradle setup and one early initialization call, you get automatic HTTP spans, crash reporting, session tracking, and direct access to the OpenTelemetry SDK for custom telemetry, and you can see it all tied together in Kibana's Android dashboards.
Observability is a crucial part of modern mobile development. Crashes, slow screens, and backend errors all impact real users, and the sooner you can identify root causes across the full request path, from the device to the database, the better. The demo app is a good first step because it exercises the complete workflow without requiring a production deployment. After that, the same setup model applies to your own app with production endpoints, API key authentication, and custom spans and logs tailored to your use cases.
Developer resources:
- EDOT Android demo application
- EDOT Android documentation
- EDOT Android getting started guide
- EDOT Android automatic instrumentation
- EDOT Android manual instrumentation
- EDOT Android configuration
- EDOT Android troubleshooting
- Android OpenTelemetry Assets dashboard docs
Don't have an Elastic Cloud account yet? Sign up for Elastic Cloud and try out mobile observability with EDOT Android as described in this guide. We'd love to hear about your experience gaining visibility into your Android application stack with Elastic.