Skip to main content
The SDKs measure how fast your app starts, how fast each screen shows its content, how its requests behave, when the main thread stops answering, and how smooth each screen is. Apsio adds these up per release, screen, endpoint and device, in the console’s Performance pages, the read API and the MCP tool query_vitals.

Vitals

The vitals of an app and release, in one view (GET /v1/projects/{projectId}/vitals): GET /v1/projects/{projectId}/performance/series follows one of them per day or per release: cold_start_p90, ttid_p90, network_p90, network_error_rate, hang_rate, anr_rate, slow_frames_per_minute or frozen_frames_per_minute. A performance view covers at most 30 days. Performance comes from sampled detail: spans and frame counts are sent only from kept sessions, and counts and percentiles are reweighted by each session’s sampling rate, so a span from a session kept at a rate of 1/4 counts four times.

App start

Each launch is one app.start span, from the start of the process (cold) or of the relaunch (warm, hot) to the first frame of the first screen. iOS can start an app’s process before the user opens it (prewarming). A prewarmed launch is marked app.start.prewarmed and measured from when the SDK starts, not from the process start, which can be minutes earlier. Start Apsio as early as possible so the measure covers your own launch work. GET /v1/projects/{projectId}/performance/start breaks app start down by release, OS version, device model or prewarming, with p50, p90, p95 and a histogram per group.

Screens

Each screen created is one app.screen.load span, from its creation (an activity’s onCreate, a view controller loading its view) to its first drawn frame:
  • TTID, time to initial display, is measured on its own.
  • TTFD, time to full display, is when the screen shows its real content, such as the data it loaded. Only the app knows that moment, so the app reports it: reportFullyDrawn() on every SDK. A screen that never reports it has no TTFD; Apsio never uses TTID in its place.
UIKit view controllers and Android activities are tracked on their own. In SwiftUI, Compose and React Native, name the screens yourself (see each SDK’s page). A screen shown again without being created, such as by going back, starts no load; it still counts in funnels and journeys. GET /v1/projects/{projectId}/performance/screens lists every screen with its loads, sessions, TTID and TTFD percentiles, and the hangs and ANRs that happened on it; performance/screens/detail shows one screen per release and per day, with its 20 slowest loads and their sessions.

Network

Each HTTP request is a client span with its method, its URL without the query string, its host, status, duration and error type. Request and response bodies and query strings are never recorded. Requests are grouped by endpoint: the method, the host and a template of the path. The template is url.template when the app or its HTTP library sends one. Otherwise Apsio derives it from the path, replacing every segment that looks like an identifier or personal data (numbers, UUIDs, long hexadecimal, phone numbers, tokens, email addresses, free text) with {id}, so GET /orders/1001 and GET /orders/1002 are one endpoint, GET /orders/{id}. A request failed when it got a 5xx or no response at all. 4xx responses are counted on their own, since many are expected. GET /v1/projects/{projectId}/network/endpoints lists the endpoints with their requests, error rates and latency percentiles, and network/endpoints/detail one endpoint’s statuses, per day and per release, with sample requests linked to their sessions. An alert can watch an endpoint’s failure rate.

Traces

Requests made while one of your spans is open nest under it, and a span started while another is open becomes its child. To follow a request into your backend, list your own hosts in firstPartyHosts: the SDK then adds a W3C traceparent header to their requests, and only theirs, never a third party’s. GET /v1/projects/{projectId}/traces/{traceId} returns the device spans of one trace. Set the project’s APM link (Settings, or PATCH /v1/orgs/{orgId}/projects/{projectId}) to an https URL with a {trace_id} placeholder, such as https://app.datadoghq.com/apm/trace/{trace_id}, and every trace and sample request carries apm_url, the same trace in your APM.

Hangs and ANRs

A hang (iOS) is the main thread blocked for more than 250 ms, seen by the SDK while the app runs in the foreground, without a debugger. It is recorded with the main thread’s stack, taken while it was blocked, and its duration once the thread runs again. An ANR (Android) is the main thread not answering for 5 seconds, recorded with its stack. Both are issues, grouped by the main thread’s frames in your app.
  • A hang that the app was killed during (by the watchdog, or a force quit) never ends. The SDK writes it to disk while it lasts and sends it at the next launch as unfinished: it counts in the hang rate, but its duration is only a lower bound, so duration percentiles leave it out.
  • MetricKit reports hangs too, up to a day later, and they may be the same hangs the SDK saw. Rates and screens count only the hangs the SDK saw while the app ran, so a hang counts once; MetricKit’s add to durations and stacks.
GET /v1/projects/{projectId}/performance/hangs gives hang and ANR session rates per release and per day, the screen of each hang, and hang durations.

Frames

The SDKs count the frames of the current screen while the app is in the foreground:
  • a frame is slow when it took longer than the display’s refresh interval for that frame (16.7 ms at 60 Hz, 8.3 ms at 120 Hz);
  • it is frozen when it took longer than 700 ms. Slow and frozen do not overlap.
Counts are sent per screen visit, never one record per frame. On iOS counting pauses on a screen nobody touches, 2 seconds after the last touch, screen change or keyboard event, so an animation nobody touched is not counted. A hang longer than 700 ms is also a frozen frame when the screen draws again at its end. Slow and frozen frames per minute in the foreground compare across platforms. The frame total, and the share of slow frames in it, compare within one platform only: iOS counts every refresh of the display, also when nothing changes, and Android only the frames it drew. GET /v1/projects/{projectId}/performance/frames gives slow and frozen frames per release and per screen.

MetricKit

On iOS, MetricKit’s daily reports add what the SDK cannot see from inside the app: launch and hang histograms, memory, CPU and disk, from every user’s day rather than from sampled sessions. GET /v1/projects/{projectId}/metrics/metrickit adds up one app’s histograms per release, with estimated percentiles.