Skip to main content

Performance Tracing

Overview

Embrace’s Performance Tracing solution gives you visibility into any app operation you’d like to track, including duration, success rate, and any contextual metadata collected at runtime that helps debug the root cause of your mobile app's performance issues. With our tool, you can quickly spot any bottlenecks in your app’s architecture, pinpoint areas you need to troubleshoot with high precision, and ultimately deliver a truly optimized user experience.

Feature Support

Minimum Requirements
  • We recommend using the latest Android SDK version for the most up-to-date API. Even though Performance Tracing is enabled in earlier versions as well, they only support a subset of features described in this doc, which applies to versions 6.4.0 and above.
  • If your app supports Android versions below 7.0, you need to enable Java 8 API desugaring so that some language features that were not part of those earlier Android releases are added at build time. Please refer to the Google documentation on how to enable desugaring for your app.

The Embrace Performance Tracing API allows you to:

  • Create real-time performance timers or record data for past operations.
    • For real-time tracing, we use a “stopwatch” concept that lets you start and stop a span's recording manually.
    • To record past operations, you can specify the start and end times of your spans that you might have captured already.
    • You can mix and match real time and past events by specifying the start and end times when you start and stop your spans.
  • Add child spans to a parent span to track sub-operations within an operation.
  • Attach attributes and span events to each span to give them further context
    • Attributes allow you to specify string key-value pairs that can be useful for filtering, grouping, and deriving custom metrics
    • Span events represent a point in time of the execution of the span and they can also have attributes

There are no limits on the duration of a span as long as the app is running.

There are also no limits to the number of child spans you can have per trace, provided the total number of spans do not exceed the per-session maximum.

Limits

TypeLimit
Max number of spans per session500
Max number of attributes per span50
Max number of events per span10
Max number of attributes per event10
Length of attribute keys50 characters
Length of attribute values200 characters
Length of Span names50 characters
Length of Event names100 characters
Exceeding Limits

If you exceed the listed limits, the operation with the limit-exceeding call will fail. See the API documentation for details.

Naming Conventions

  • Span Names are case-sensitive and are a max of 50 characters.
  • Key Names are case-sensitive, have a max of 50 characters, and are alphanumeric ASCII characters
Internal Prefixes

The emb- and emb. prefixes are reserved for internal Embrace span and attribute names, respectively. You should never create a span or attribute key name with emb- and emb. prefixes

Adding Performance Traces To Your App

To use this feature:

  1. Ensure you’re using a version of the Embrace SDK that supports Performance Tracing.
  2. (Optional) Enable API desugaring for your app if you want users running Android 5.x and 6.x to report traces.
  3. Instrument your app using the reference guide in this section to start adding traces to your operations, or refer to the API docs for a more comprehensive description of the public API.
  4. See the traces in the Traces section of the Embrace dashboard.

API Usage Examples

Create a Span

// create a trace by creating its root span
// recording will not behind until the span has been started
val activityLoad = Embrace.getInstance().createSpan("load-activity")

Create and Start Span Atomically

// activityLoad will either be a span that has already started or null if 
// the creation or start attempt was unsuccessful
val activityLoad = Embrace.getInstance().startSpan("load-activity")

Start Span That Tracks an Operation That Started at an Earlier Time

val appStartTimeMillis = getAppStartTime()
val appLaunchTrace = Embrace.getInstance().createSpan("app-launch")

// begin recording a trace that has a different start time than
// the current time by starting its root span with a specific timestamp
appLaunchTrace?.start(startTimeMs = appStartTimeMillis)

Add Attributes and Span Events

val embrace = Embrace.getInstance()
val activityLoad = embrace.startSpan("load-activity")
val imageLoad = activityLoad?.let { embrace.startSpan("load-image", this) }

val image = fetchImage()

// record important event at point in time
imageLoad?.addEvent("network-request-finished")

// record attribute particular to this span instance
imageLoad?.addAttribute("image-name", image.name)

Stop Span For Operation That Ended Earlier

val activityLoad = Embrace.getInstance().startSpan("load-activity")

// some time passes after the operation being time has finished

activityLoad?.stop(endTimeMs = getActualEndTimeMilllis())

Stop Span For an Operation That Failed

val activityLoad = Embrace.getInstance().startSpan("load-activity")

try {
loadActivity()
} catch (e: IllegalStateException) {
activityLoad?.addAttribute("error-message", getErrorMessage(e))
activityLoad?.stop(ErrorCode.FAILURE)
} finally {
// calling stop on an already-stopped span will not change its state
activityLoad?.stop()
}

Add a Child Span If the Parent Started Properly

val embrace = Embrace.getInstance()
val activityLoad = embrace.startSpan("load-activity")

// create and start a child span if activityLoad is created and started successfully
val imageLoad = activityLoad?.let { embrace.startSpan("load-image", it) }

Record a Trace Before the Embrace SDK Has Started

// record a span based on start and end times that are in the past
Embrace.getInstance().recordCompletedSpan(
name = "activity-create",
startTimeMs = startTimeMillis,
endTimeMs = endTimeMillis
)

Get a Reference to an In-Progress Span

val embrace = Embrace.getInstance()
val activityLoad = embrace.startSpan("load-activity")
val activityLoadSpanId = activityLoad?.spanId

/* some other part of the code without access to activityLoad */

embrace.getSpan(activityLoadSpanId)?.stop()

Export your telemetry

A SpanExporter can be easily injected, to directly export your data to any OpenTelemetry Collector.

Local testing

Injecting a LoggingSpanExporter will allow you to see your telemetry in the logcat.

2024-03-05 14:15:15.342 29672-29756 LoggingSpanExporter     io.embrace.mysampleapp          I  'emb-startup-moment' : d38b4ac26baf1a862ed4a028af7d08ac e3e82dd0f86c0eed INTERNAL [tracer: io.embrace.android.embracesdk:=6.6.0] AttributesMap{data={emb.sequence_id=4, emb.type=PERFORMANCE, emb.key=true}, capacity=128, totalAddedValues=3}

Adding a SpanExporter for a custom OTel Collector

You can send your data to a custom (OTel Collector)

 //grpc through an otel collector in a local docker image
val customDockerExporter = OtlpGrpcSpanExporter.builder()
.setEndpoint("http://10.0.2.2:4317")
.build()

Exporting data to Grafana Cloud

Embrace Performance tracing can be exported to Grafana Cloud using an OTel Collector.

//... or directly to grafana cloud
val grafanaCloudExporter = OtlpHttpSpanExporter.builder()
.setEndpoint("https://myinstance.grafana.net/otlp/v1/traces")
.addHeader("Authorization", "YourToken")
.build()
info

Every exporter should be added before starting the SDK

Embrace.getInstance().addSpanExporter(LoggingSpanExporter.create())
Embrace.getInstance().addSpanExporter(grpcExporter)
Embrace.getInstance().addSpanExporter(protoExporter)

Embrace.getInstance().start(this)

Support

If you have any questions or if something is not working as intended, please get in touch with your Customer Success Manager.