Session Vitals tracks CPU, memory and playback quality across a session and shows them as a timeline under Sessions. It is off by default — enable it per app under the integration’s Session Vitals card. The SDK negotiates the flag on every config refresh; a session starts within one refresh of the toggle flipping on, and stops (sending its final summary) when it flips off.
Setup
// build.gradle.kts — the media3 artifact is optional; core alone still
// records CPU and memory.
implementation("com.traceitx:core:$traceitxVersion")
implementation("com.traceitx:reporter-ui:$traceitxVersion")
implementation("com.traceitx:media3:$traceitxVersion")
TraceItXConfig(
appId = "txx_app_…",
sdkKey = "txx_live_…",
vitals = VitalsConfig(
enabled = null, // null = follow the dashboard toggle; false = opt out locally
sampleRate = null, // 0.0–1.0; combined with the server rate by min()
captureSourceQuery = false // keep query strings on stream URLs (signed CDN URLs carry tokens)
),
)
Local config can only opt out or lower the rate. It can never enable vitals the dashboard has off.
Tracking a player
import com.traceitx.media3.trackPlayer
val player = ExoPlayer.Builder(context).build()
val handle = TraceItX.trackPlayer(player, name = "main")
// … later
player.release() // detaches automatically; handle.detach() is also fine
One call registers one AnalyticsListener. From it the timeline gets the
source (URL with query stripped, protocol, live flag), startup breakdown
(manifest, first fragment, licence, first frame), play/pause/seek/rate,
rebuffers, bitrate and resolution changes, DRM key system, fatal and
non-fatal errors, and a stats sample every 20 s (buffer ahead, bandwidth
estimate, dropped frames). Nothing is recorded per segment.
Calling trackPlayer before the collector has started is fine — the
registration is honoured the moment it does. Releasing the player without
calling detach() still records a clean player_detach.
Custom entries
TraceItX.trackVitals("ad_break", mapOf("position" to "midroll", "index" to 2))
handle.track("cdn_switch", mapOf("from" to "cdn-a", "to" to "cdn-b")) // scoped to that player
Any JSON-encodable value, capped at 2 KB serialised. Over the cap it is truncated and flagged, never dropped. Entries logged while no session is running are dropped.
Writing an integration
Implement com.traceitx.vitals.PlayerIntegration and pass it to
TraceItX.trackPlayer(integration, name). attach subscribes to your
player and emits through the context; snapshot is asked once per sampler
tick and must read player state on the player’s own thread; describe
re-emits the current source and DRM so a rotated session is self-describing;
detach removes every listener. The Media3 integration is the reference.
What it costs
One background thread ticking every 20 s (Process.getElapsedCpuTime() and
Debug.getPss()), push-based player listeners, chunks of at most 64 KB
flushed every 30 s. The sampler pauses while the app is in the background;
player events keep flowing. Vitals are never written to disk and never
retried more than once.