Automatic Instrumentation
Learn what transactions are captured after tracing is enabled.
Capturing transactions requires that you first set up performance monitoring 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
<application>
<meta-data
android:name="io.sentry.traces.activity.enable"
android:value="false"
/>
</application>
The transaction finishes after each Activity's onResume
method is executed.
The transaction finishes automatically, but you may disable it by setting:
AndroidManifest.xml
<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:
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.
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 Activity#onResume
call 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
<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.
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.
implementation ('io.sentry:sentry-android:7.9.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
<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.testTag("<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
).
import androidx.compose.ui.platform.testTag
@Composable
fun LoginScreen() {
Column {
// ...
Button(
modifier = Modifier.testTag("button_login"),
onClick = { TODO() }) {
Text(text = "Login")
}
}
}
If the @Composable
doesn't have a testTag
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 testTag
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
plugins {
id "io.sentry.kotlin.compiler.gradle" version "4.6.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:
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.
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
<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:
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.
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").