Automatic Instrumentation

Learn what transactions are captured after tracing is enabled.

Capturing transactions requires that you first set up tracing if you haven't already.

Supported in Sentry's Android SDK version 4.3.0 and above.

The Activity's instrumentation, once enabled, captures transactions for each launch of an Activity. The SDK sets the Transaction name to the name of the Activity, for example, MainActivity, and Transaction operation to ui.load.

The transaction starts before each Activity's onCreate method is called.

The Activity's instrumentation is enabled by default, but you may disable it by setting:

AndroidManifest.xml
Copied
<application>
    <meta-data
    android:name="io.sentry.traces.activity.enable"
    android:value="false"
  />
</application>

The transaction finishes after each Activity's first frame is drawn.

The transaction finishes automatically, but you may disable it by setting:

AndroidManifest.xml
Copied
<application>
    <meta-data
    android:name="io.sentry.traces.activity.auto-finish.enable"
    android:value="false"
  />
</application>

We offer the possibility to finish the transaction manually. For example, you might want to finish the transaction after an API call triggered on Activity's onCreate and shown the data to the user. To achieve that you can do:

Copied
import io.sentry.Sentry;

ISpan span = Sentry.getSpan();
if (span != null) {
  span.finish();
}

In case that the user left this Activity before you've finished the transaction manually, the SDK finishes the transaction automatically when the onDestroy method is called.

Transactions are always bound to the Scope automatically if there's none set. Because of that, you may create spans using custom instrumentation, and those spans will be automatically associated with the Activity's running transaction.

Copied
import java.lang.Exception;

import io.sentry.ISpan;
import io.sentry.Sentry;
import io.sentry.SpanStatus;

void displayUserData() {
  ISpan span = Sentry.getSpan();
  if (span != null) {
    ISpan innerSpan = span.startChild("displayUserData");
    try {
      // omitted code
    } catch (Exception e) {
      innerSpan.setThrowable(e);
      innerSpan.setStatus(SpanStatus.NOT_FOUND);
      throw e;
    } finally {
      innerSpan.finish();
    }
  }
}

When you didn't finish the transaction yet, but you start a new Activity, the SDK always automatically finishes the previous Activity transaction. This is due to the fact that only one transaction at a time can be bound to the Scope. To work around that, you may create transactions manually using the custom instrumentation and its instance to start spans instead of the Static API.

Once enabled, Fragment Instrumentation starts a span for each launch of a fragment. The SDK sets the span operation to ui.load and the span description to the fragment's name, for example, LoginFragment.

The span starts after each fragment's onCreate method is called and before its onCreateView method is called.

The span finishes after each fragment's onResume method is executed.

Fragment Instrumentation depends on having an active transaction bound to the scope, and ideally it'd be used along with Activity Instrumentation, which starts a transaction and binds it to the scope automatically.

Learn more in our Fragment documentation.

The App Start Instrumentation provides insight into how long your application takes to launch. It adds a span from the application launch to the first auto-generated UI transaction.

The SDK differentiates between a cold and a warm start, but doesn't track hot starts/resumes.

  • Cold start: A cold start refers to an app’s starting from scratch: the system’s process has not, until this start, created the app’s process. Cold starts happen in cases such as your app’s being launched for the first time since the device booted, or since the system killed the app.
  • Warm start: A warm start encompasses some subset of the operations that take place during a cold start; at the same time, it represents more overhead than a hot start. For instance: The system evicts your app from memory, and then the user re-launches it. The process and the activity need to be restarted, but the task can benefit somewhat from the saved instance state bundle passed into onCreate().

The SDK sets the Span operation to app.start.cold for Cold start and app.start.warm for Warm start.

The SDK uses the SentryPerformanceProvider (ContentProvider) creation time as the beginning of the app start and the first frame drawn as the end.

The app start is only measured if the process is of the importance RunningAppProcessInfo.IMPORTANCE_FOREGROUND, which means the process is running the foreground UI.

You can opt out of Activity Instrumentation and App Start Instrumentation using options:

AndroidManifest.xml
Copied
<application>
    <meta-data
    android:name="io.sentry.traces.activity.enable"
    android:value="false"
  />
</application>

Cold and warm start are Mobile Vitals, which you can learn about in the full documentation.

Supported on Android OS version 7.0 and above.

Unresponsive UI and animation hitches annoy users and degrade the user experience. Two measurements to track these types of experiences are slow frames and frozen frames. If you want your app to run smoothly, you should try to avoid both. The SDK adds these two measurements for the Activity transactions.

Slow and frozen frames are Mobile Vitals, which you can learn about in the full documentation.

Sentry adds an extra header with the trace id in the outgoing HTTP requests to continue the transaction in the backend. You can set the tracePropagationTarget option to filter which requests Sentry adds the extra header to. For example, to ensure that only your app backend will receive the trace id.

Copied
import io.sentry.android.core.SentryAndroid;

SentryAndroid.init(this, options -> {
    final List<String> targets = new ArrayList<>();
    targets.add("myapp.com");
    options.setTracePropagationTargets(targets);
});

The option may contain a list of Strings (including regular expressions) which are matched against the URLs of outgoing requests. If one of the entries in the list matches the URL of an outgoing request, trace data will be attached to that request. String entries do not have to be full matches, meaning the URL of a request is matched when it contains a string provided through the option. If tracePropagationTargets is not provided, trace data is attached to every outgoing request from the instrumented client.

Sentry uses the androidx.core library for detecting slow and frozen frames. This is necessary to produce accurate results across all Android OS versions.

We check for availability at runtime, so if you're not using androidx.core, you can remove it from Sentry's transitive dependencies.

Copied
implementation ('io.sentry:sentry-android:7.18.0') {
    exclude group: 'androidx.core', module: 'core'
}

Note that by removing this transitive dependency, slow and frozen frames won't be reported.

The OkHttp's instrumentation, once added the SentryOkHttpInterceptor, starts a span out of the active span bound to the scope for each HTTP Request. The SDK sets the span operation to http.client and description to request $METHOD $url; for example, GET https://sentry.io.

The span finishes once the request has been executed. The span status depends on either the HTTP Response code or SpanStatus.INTERNAL_ERROR if the code does not match any of Sentry's SpanStatus.

When the HTTP request throws an IOException, Sentry's SDK associates this exception to the running span. If you haven't set the SDK to swallow the exception and capture it, the span and SentryEvent will be linked when viewing it on the Issue Details page in sentry.io.

For more information see our OkHttp integration.

The Apollo's instrumentation, once added the SentryApolloInterceptor, starts a span out of the active span bound to the scope for each GraphQL Request. The SDK sets the span operation to the GraphQL operation name and description to request $operation.type and $operation.name; for example, query LaunchDetails.

The span finishes once the request has been executed. The span status depends on either the HTTP Response code or SpanStatus.INTERNAL_ERROR if the code does not match any of Sentry's SpanStatus.

When the Apollo request throws an ApolloException, Sentry's SDK associates this exception to the running span. If you haven't set the SDK to swallow the exception and capture it, the span and SentryEvent will be linked when viewing it on the Issue Details page in sentry.io.

For more information see our Apollo integration.

The Sentry Android Gradle Plugin does tracing auto instrumentation using bytecode manipulation for androidx.sqlite and androidx.room libraries.

The Plugin injects a code snippet that starts a span out of the active span bound to the scope for each CRUD operation. The SDK sets the span operation to db and description to the SQL Query if available.

The span finishes once the operation has been executed. The span status is set to SpanStatus.OK if successful or SpanStatus.INTERNAL_ERROR if there was any error.

When the operation throws an Exception, Sentry's SDK associates this exception to the running span. If you haven't set the SDK to swallow the exception and capture it, the span and SentryEvent will be linked when viewing it on the Issue Details page in sentry.io.

For more information see our Room and SQLite integration.

The Sentry Android Gradle Plugin does tracing auto instrumentation using bytecode manipulation for java.io.FileInputStream, java.io.FileOutputStream, java.io.FileReader, and java.io.FileWriter.

The plugin replaces the aforementioned classes with custom Sentry-specific implementations.

The Sentry-specific file I/O implementation starts a span out of the active span, bound to the scope for each file I/O operation. The SDK sets the span operation to file.write/file.read and description to filename (pretty-printed file size), e.g. file.txt (123 kB)

In addition, the span contains other useful information such as file.size (raw number of bytes) and file.path (an absolute path to the file) as part of the data payload.

The span finishes once the operation has been executed. The span status is set to SpanStatus.OK if successful or SpanStatus.INTERNAL_ERROR if there was any error.

When the operation throws an Exception, Sentry's SDK associates this exception to the running span. If you haven't set the SDK to swallow the exception and capture it, the span and SentryEvent will be linked when viewing it on the Issue Details page in sentry.io.

For more information see our file I/O integration.

The UI instrumentation, once enabled, captures transactions for a set of different user interactions, which include clicks, scrolls, and swipes. User interaction instrumentation is available for both classic Android Views as well as Jetpack Compose.

This feature is disabled by default, but you can enable it by setting:

AndroidManifest.xml
Copied
<application>
    <meta-data
    android:name="io.sentry.traces.user-interaction.enable"
    android:value="true"
  />
</application>

(New in version 6.0.0)

The SDK composes the transaction name out of the host Activity and the id of the view that captured the user interaction — for example, for LoginActivity.login_button. The transaction operation is set to ui.action plus the interaction type (one of click, scroll, or swipe).

If the view doesn't have the id assigned, the transaction won't be captured because it can't be uniquely identified otherwise.

(New in version 6.10.0)

The SDK composes the transaction name out of the host Activity and the tag set by way of the Modifier.sentryTag("<tag>") of the Composable (for example, LoginActivity.login_button). The transaction operation is set to ui.action plus the interaction type (one of click, scroll, or swipe).

Copied
import io.sentry.compose.sentryTag

@Composable
fun LoginScreen() {
  Column {
    // ...
    Button(
        modifier = Modifier.sentryTag("button_login"),
        onClick = { TODO() }) {
        Text(text = "Login")
    }
  }
}

If the @Composable doesn't have a sentryTag modifier applied, the transaction won't be captured because it can't be uniquely identified. To capture a transaction for the @Composable, you must either add a sentryTag modifier or enable automatic @Composable tagging.

The Sentry Kotlin Compiler plugin is considered experimental. Give it a try and provide early feedback on GitHub.

The Sentry Kotlin Compiler Plugin can automatically enrich your @Composable functions during compilation with a tag name, based on the function name of your @Composable. In order to use this feature, the Sentry Kotlin Compiler plugin needs to be applied to all modules, which contain @Composable elements.

app/build.gradle
Copied
plugins {
  id "io.sentry.kotlin.compiler.gradle" version "4.14.0"
}

The transaction finishes automatically after it reaches the specified idleTimeout and all of its child spans are finished. The idleTimeoout defaults to 3000 milliseconds (three seconds). You can also disable the idle timeout by setting it to null, but the transaction must be finished manually in this case.

If the UI transaction has idled, but didn't have any child spans added, it will be dropped.

To finish the transaction manually, you can:

Copied
import io.sentry.Sentry;

ISpan span = Sentry.getSpan();
if (span != null) {
  span.finish();
}

If the host Activity transitions into a non-interactive state (for example, onPause), the UI transaction will be scheduled to finish automatically (as soon as all of its child spans are finished).

Transactions are always bound to the Scope automatically if there's none set. Because of that, you can create spans using custom instrumentation, and those spans will be automatically associated with the running UI transaction.

Copied
import java.lang.Exception;

import io.sentry.ISpan;
import io.sentry.Sentry;
import io.sentry.SpanStatus;

void loadUserDataOnClick() {
  ISpan span = Sentry.getSpan();
  if (span != null) {
    ISpan innerSpan = span.startChild("loadUserData");
    // omitted code
    innerSpan.finish();
  }
}

When the UI transaction is not finished yet, but the user makes a new interaction, the SDK automatically finishes the previous UI transaction. This is because only one transaction can be bound to the scope at a time. However, if the same view has been interacted with (for example, a RecyclerView was scrolled again within the idleTimeout window), the idle timer will be reset and the transaction duration will be extended with the idleTimeout value.

(New in version 6.10.0)

By adding a span for each launch of an activity, time to initial display (TTID) provides insight into how long it takes for your activities to launch and draw their first UI frame. The SDK sets the span operation to ui.load.initial-display and the span description to the activity's name, followed by initial display - for example, MainActivity initial display.

The span starts when each Activity is launched, which is defined as an application launch for the first Activity, and the onPause method of the previous Activity for each subsequent Activity launched.

The span finishes after the Activity draws its first frame.

Time to initial display is dependent on having an active transaction bound to the scope. Ideally, it should be used along with Activity Instrumentation, which starts a transaction and binds it to the scope automatically.

The following chart shows how time to initial display (TTID) and time to full display (TTFD) correlate to transitions between activities:

(New in version 6.14.0)

By adding a span for each launch of an activity, time to full display (TTFD) provides insight into how long it would take for your activities to launch and load all of their content. The SDK sets the span operation to ui.load.full-display and the span description to the activity's name followed by full display - for example, MainActivity full display.

The span starts when each Activity is launched, which is defined as the application launch for the first Activity, and the onPause method of the previous Activity for each subsequent Activity launched.

Time to full display is disabled by default, but you can enable it by setting:

AndroidManifest.xml
Copied
<application>
    <meta-data
    android:name="io.sentry.traces.time-to-full-display.enable"
    android:value="true"
  />
</application>

The span has to be finished manually, by using:

Copied
import io.sentry.Sentry;

Sentry.reportFullyDisplayed();

Time to full display is dependent on having an active transaction bound to the scope. Ideally, it should be used along with Activity Instrumentation, which starts a transaction and binds it to the scope automatically.

If the span finishes through the API, its status is set to SpanStatus.OK. If the span doesn't finish after 30 seconds, it is finished by the SDK automatically, and its status is set to SpanStatus.DEADLINE_EXCEEDED. If the span finishes before the Activity is first drawn and displayed as measured by the Time to initial display, the reported time will be shifted to Time to initial display measured time.

Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").