Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add time-to-full-display span to activity transactions #2432

Merged
merged 26 commits into from
Feb 15, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fd54fa6
added ttid span to ActivityLifecycleIntegration
stefanosiano Nov 16, 2022
1d48b5a
SentryFrameMetricsCollector start collecting frameMetrics in onActivi…
stefanosiano Nov 16, 2022
7af811a
Merge branch 'main' into feat/android-ttid
stefanosiano Nov 16, 2022
501d3e7
updated changelog
stefanosiano Nov 16, 2022
ff733e5
Update CHANGELOG.md
stefanosiano Nov 16, 2022
d49606b
Merge branch 'main' into feat/android-ttid
stefanosiano Nov 18, 2022
0c9a0a8
ActivityLifecycleIntegration now keeps a map of ttidSpans, with one s…
stefanosiano Dec 1, 2022
1e8fc53
Merge branch 'main' into feat/android-ttid
stefanosiano Dec 1, 2022
37b5edc
added Instrumenter.Sentry to ttid span
stefanosiano Dec 1, 2022
13a6c1a
added reference to Firebase sdk
stefanosiano Dec 2, 2022
a72bdda
added first ttfd internal implementation
stefanosiano Dec 4, 2022
bb725f3
Merge branch 'main' into android/ttfd
stefanosiano Dec 14, 2022
d54c1f0
added ttfd span
stefanosiano Dec 15, 2022
b2e36c2
Merge branch 'main' into android/ttfd
stefanosiano Dec 16, 2022
43c1107
updated changelog
stefanosiano Dec 16, 2022
ac48c4d
Merge branch 'main' into android/ttfd
stefanosiano Dec 16, 2022
4bb58c0
updated ui test
stefanosiano Dec 16, 2022
31b99a4
Merge branch 'main' into android/ttfd
stefanosiano Feb 6, 2023
edcb1ff
added Sentry.reportFullyDrawn() API
stefanosiano Feb 9, 2023
16a39fb
Merge branch 'main' into android/ttfd
stefanosiano Feb 9, 2023
a4b6643
added Sentry.reportFullyDrawn() API
stefanosiano Feb 9, 2023
09e758c
updated changelog
stefanosiano Feb 9, 2023
fa7292a
Merge branch 'main' into android/ttfd
stefanosiano Feb 14, 2023
932e25b
renamed classes
stefanosiano Feb 14, 2023
8821e3f
renamed classes
stefanosiano Feb 14, 2023
2bbc3f1
renamed classes
stefanosiano Feb 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Features

- Add time-to-full-display span to Activity automatic transactions ([#2432](https://github.com/getsentry/sentry-java/pull/2432))
stefanosiano marked this conversation as resolved.
Show resolved Hide resolved
- Add `main` flag to threads and `in_foreground` flag for app contexts ([#2516](https://github.com/getsentry/sentry-java/pull/2516))

### Fixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import android.view.View;
import androidx.annotation.NonNull;
import io.sentry.Breadcrumb;
import io.sentry.FullyDisplayedReporter;
import io.sentry.Hint;
import io.sentry.IHub;
import io.sentry.ISpan;
Expand All @@ -33,6 +34,7 @@
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.Future;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
Expand All @@ -45,6 +47,8 @@ public final class ActivityLifecycleIntegration
static final String APP_START_WARM = "app.start.warm";
static final String APP_START_COLD = "app.start.cold";
static final String TTID_OP = "ui.load.initial_display";
static final String TTFD_OP = "ui.load.full_display";
static final long TTFD_TIMEOUT_MILLIS = 30000;

private final @NotNull Application application;
private final @NotNull BuildInfoProvider buildInfoProvider;
Expand All @@ -53,15 +57,20 @@ public final class ActivityLifecycleIntegration

private boolean performanceEnabled = false;

private boolean timeToFullDisplaySpanEnabled = false;

private boolean isAllActivityCallbacksAvailable;

private boolean firstActivityCreated = false;
private final boolean foregroundImportance;

private @Nullable FullyDisplayedReporter fullyDisplayedReporter = null;
private @Nullable ISpan appStartSpan;
private final @NotNull WeakHashMap<Activity, ISpan> ttidSpanMap = new WeakHashMap<>();
private @NotNull SentryDate lastPausedTime = AndroidDateUtils.getCurrentSentryDateTime();
private final @NotNull Handler mainHandler = new Handler(Looper.getMainLooper());
private @Nullable ISpan ttfdSpan = null;
private @Nullable Future<?> ttfdAutoCloseFuture = null;

// WeakHashMap isn't thread safe but ActivityLifecycleCallbacks is only called from the
// main-thread
Expand Down Expand Up @@ -106,6 +115,8 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio
this.options.isEnableActivityLifecycleBreadcrumbs());

performanceEnabled = isPerformanceEnabled(this.options);
fullyDisplayedReporter = this.options.getFullyDrawnReporter();
timeToFullDisplaySpanEnabled = this.options.isEnableTimeToFullDisplayTracing();

if (this.options.isEnableActivityLifecycleBreadcrumbs() || performanceEnabled) {
application.registerActivityLifecycleCallbacks(this);
Expand Down Expand Up @@ -200,6 +211,8 @@ private void startTracing(final @NotNull Activity activity) {
new TransactionContext(activityName, TransactionNameSource.COMPONENT, UI_LOAD_OP),
transactionOptions);

final @NotNull SentryDate ttidStartTime;

// in case appStartTime isn't available, we don't create a span for it.
if (!(firstActivityCreated || appStartTime == null || coldStart == null)) {
// start specific span for app start
Expand All @@ -214,18 +227,26 @@ private void startTracing(final @NotNull Activity activity) {
// we can finish the app-start span
finishAppStartSpan();

// The first activity ttidSpan should start at the same time as the app start time
ttidSpanMap.put(
activity,
transaction.startChild(
TTID_OP, getTtidDesc(activityName), appStartTime, Instrumenter.SENTRY));
// The first activity ttid/ttfd spans should start at the app start time
ttidStartTime = appStartTime;
} else {
// Other activities (or in case appStartTime is not available) the ttid span should
// start when the previous activity called its onPause method.
ttidSpanMap.put(
activity,
// The ttid/ttfd spans should start when the previous activity called its onPause method
ttidStartTime = lastPausedTime;
}
ttidSpanMap.put(
activity,
transaction.startChild(
TTID_OP, getTtidDesc(activityName), ttidStartTime, Instrumenter.SENTRY));

if (timeToFullDisplaySpanEnabled && fullyDisplayedReporter != null && options != null) {
ttfdSpan =
transaction.startChild(
TTID_OP, getTtidDesc(activityName), lastPausedTime, Instrumenter.SENTRY));
TTFD_OP, getTtfdDesc(activityName), ttidStartTime, Instrumenter.SENTRY);
ttfdAutoCloseFuture =
options
.getExecutorService()
.schedule(
() -> finishSpan(ttfdSpan, SpanStatus.DEADLINE_EXCEEDED), TTFD_TIMEOUT_MILLIS);
}

// lets bind to the scope so other integrations can pick it up
Expand Down Expand Up @@ -288,7 +309,9 @@ private void finishTransaction(
}

// in case the ttidSpan isn't completed yet, we finish it as cancelled to avoid memory leak
finishSpan(ttidSpan, SpanStatus.CANCELLED);
finishSpan(ttidSpan, SpanStatus.DEADLINE_EXCEEDED);
finishSpan(ttfdSpan, SpanStatus.DEADLINE_EXCEEDED);
cancelTtfdAutoClose();

SpanStatus status = transaction.getStatus();
// status might be set by other integrations, let's not overwrite it
Expand All @@ -315,6 +338,14 @@ public synchronized void onActivityCreated(
startTracing(activity);

firstActivityCreated = true;

if (fullyDisplayedReporter != null) {
fullyDisplayedReporter.registerFullyDrawnListener(
() -> {
finishSpan(ttfdSpan);
cancelTtfdAutoClose();
});
}
}

@Override
Expand Down Expand Up @@ -419,7 +450,11 @@ public synchronized void onActivityDestroyed(final @NotNull Activity activity) {

// we finish the ttidSpan as cancelled in case it isn't completed yet
final ISpan ttidSpan = ttidSpanMap.get(activity);
finishSpan(ttidSpan, SpanStatus.CANCELLED);
finishSpan(ttidSpan, SpanStatus.DEADLINE_EXCEEDED);

// we finish the ttfdSpan as cancelled in case it isn't completed yet
finishSpan(ttfdSpan, SpanStatus.DEADLINE_EXCEEDED);
cancelTtfdAutoClose();

// in case people opt-out enableActivityLifecycleTracingAutoFinish and forgot to finish it,
// we make sure to finish it when the activity gets destroyed.
Expand All @@ -428,6 +463,7 @@ public synchronized void onActivityDestroyed(final @NotNull Activity activity) {
// set it to null in case its been just finished as cancelled
appStartSpan = null;
ttidSpanMap.remove(activity);
ttfdSpan = null;

// clear it up, so we don't start again for the same activity if the activity is in the activity
// stack still.
Expand All @@ -443,6 +479,13 @@ private void finishSpan(@Nullable ISpan span) {
}
}

private void cancelTtfdAutoClose() {
if (ttfdAutoCloseFuture != null) {
ttfdAutoCloseFuture.cancel(false);
ttfdAutoCloseFuture = null;
}
}

private void finishSpan(@Nullable ISpan span, @NotNull SpanStatus status) {
if (span != null && !span.isFinished()) {
span.finish(status);
Expand Down Expand Up @@ -473,6 +516,12 @@ WeakHashMap<Activity, ISpan> getTtidSpanMap() {
return ttidSpanMap;
}

@TestOnly
@Nullable
ISpan getTtfdSpan() {
return ttfdSpan;
}

private void setColdStart(final @Nullable Bundle savedInstanceState) {
if (!firstActivityCreated) {
// if Activity has savedInstanceState then its a warm start
Expand All @@ -485,6 +534,10 @@ private void setColdStart(final @Nullable Bundle savedInstanceState) {
return activityName + " initial display";
}

private @NotNull String getTtfdDesc(final @NotNull String activityName) {
return activityName + " full display";
}

private @NotNull String getAppStartDesc(final boolean coldStart) {
if (coldStart) {
return "Cold Start";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ final class ManifestMetadataReader {
"io.sentry.traces.activity.auto-finish.enable";
static final String TRACES_UI_ENABLE = "io.sentry.traces.user-interaction.enable";

static final String TTFD_ENABLE = "io.sentry.traces.time-to-full-display.enable";

static final String TRACES_PROFILING_ENABLE = "io.sentry.traces.profiling.enable";
static final String PROFILES_SAMPLE_RATE = "io.sentry.traces.profiling.sample-rate";

Expand Down Expand Up @@ -271,6 +273,9 @@ static void applyMetadata(
options.setEnableUserInteractionTracing(
readBool(metadata, logger, TRACES_UI_ENABLE, options.isEnableUserInteractionTracing()));

options.setEnableTimeToFullDisplayTracing(
readBool(metadata, logger, TTFD_ENABLE, options.isEnableTimeToFullDisplayTracing()));

final long idleTimeout = readLong(metadata, logger, IDLE_TIMEOUT, -1);
if (idleTimeout != -1) {
options.setIdleTimeout(idleTimeout);
Expand Down
Loading