Performance Tracing
Trace any operation in your app with nested spans and attributes across Android, iOS, Flutter, React Native, and Kotlin Multiplatform.
Tracing shows how long an operation takes and where the time goes inside it.
A span times a single piece of work, like an HTTP request, a database query, or a function. A trace groups related spans into one operation, from something small like an app launch to a whole journey like onboarding. Spans nest inside a trace to show how the work is structured.
This page covers the common operations. See Track performance traces for the full API.
Time an operation
Wrap an operation in a span to time it. Start a span when the work begins and end it when it's done. For example: time a feed refresh, an image decode, or a database query. Then set the status to mark whether the work succeeded (Ok) or failed (Error) before you end the span.
import sh.measure.android.Measure
import sh.measure.android.tracing.SpanStatus
val span = Measure.startSpan("refresh-feed")
val refreshed = feedRepository.refresh()
span.setStatus(if (refreshed) SpanStatus.Ok else SpanStatus.Error).end()Break a flow into steps
A single span records how long the whole flow took. Often you need a drill down into what steps made it slow. To understand that, set one span as the parent of another to nest them. For example: for a checkout flow, create a parent span for the whole flow with a child span for each step, like validating the cart and processing the payment.
import sh.measure.android.Measure
val checkout = Measure.startSpan("checkout")
val validate = Measure.startSpan("validate-cart").setParent(checkout)
cartService.validate(cart)
validate.end()
val payment = Measure.startSpan("process-payment").setParent(checkout)
paymentGateway.charge(cart)
payment.end()
checkout.end()Add context with attributes
Attach attributes to a span to record additional context alongside its timing. For example, on a feed-load span, that might be the screen name, how many items are in the feed, or whether the data was served from cache. See Attribute limits for the allowed keys and values.
import sh.measure.android.Measure
val span = Measure.startSpan("load-feed")
val items = feedApi.fetchFeed()
span.setAttribute("screen", "Home")
span.setAttribute("item_count", items.size)
span.end()Mark checkpoints
Mark a checkpoint inside a span to record a moment that matters. On a screen-load span, checkpoint when the network call returns and when the list finishes rendering. It helps to see which half of the load was slow. Checkpoints split one span's time into phases without a child span for each.
import sh.measure.android.Measure
val span = Measure.startSpan("load-screen")
val products = productApi.fetchProducts()
span.setCheckpoint("network_done")
renderList(products)
span.setCheckpoint("list_rendered")
span.end()Defer or backdate a span
Some spans start before there's a good place to create them. A cold launch begins when the process starts but the SDK is not initialzied at the time. you can create a backdated span in such a case.
import sh.measure.android.Measure
import sh.measure.android.tracing.SpanStatus
// in Application.onCreate
val launchStart = Measure.getCurrentTime()
// when the first screen is drawn
val span = Measure.startSpan("cold-launch", timestamp = launchStart)
span.setStatus(SpanStatus.Ok).end()To configure a span now and start it at the right moment, build it in advance with createSpanBuilder.
import sh.measure.android.Measure
val builder = Measure.createSpanBuilder("checkout")
// later, when the work begins
val span = builder?.startSpan()Control the sampling rate
Measure reports every trace by default. To collect less, set a trace sampling rate from the dashboard.
Think this page can be better?
Open an issue