Skip to content

Commit

Permalink
Merge fa7292a into 4b32504
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanosiano authored Feb 14, 2023
2 parents 4b32504 + fa7292a commit 16ebe65
Show file tree
Hide file tree
Showing 22 changed files with 524 additions and 16 deletions.
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))
- 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

0 comments on commit 16ebe65

Please sign in to comment.