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

Extract Activity Breadcrumbs generation into own Integration #3064

Merged
merged 22 commits into from
Dec 18, 2023
Merged
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
Prev Previous commit
Next Next commit
Address PR feedback, improve tests
markushi committed Nov 30, 2023
commit 1a0fcee3ce9ee0074189404ccb0f41016cfdcafd
19 changes: 5 additions & 14 deletions sentry-android-core/api/sentry-android-core.api
Original file line number Diff line number Diff line change
@@ -396,11 +396,11 @@ public class io/sentry/android/core/performance/ActivityLifecycleCallbacksAdapte
}

public class io/sentry/android/core/performance/ActivityLifecycleTimeSpan : java/lang/Comparable {
public final field onCreate Lio/sentry/android/core/performance/TimeSpan;
public final field onStart Lio/sentry/android/core/performance/TimeSpan;
public fun <init> ()V
public fun compareTo (Lio/sentry/android/core/performance/ActivityLifecycleTimeSpan;)I
public synthetic fun compareTo (Ljava/lang/Object;)I
public final fun getOnCreate ()Lio/sentry/android/core/performance/TimeSpan;
public final fun getOnStart ()Lio/sentry/android/core/performance/TimeSpan;
}

public class io/sentry/android/core/performance/AppStartMetrics {
@@ -413,7 +413,7 @@ public class io/sentry/android/core/performance/AppStartMetrics {
public fun getApplicationOnCreateTimeSpan ()Lio/sentry/android/core/performance/TimeSpan;
public fun getContentProviderOnCreateTimeSpans ()Ljava/util/List;
public static fun getInstance ()Lio/sentry/android/core/performance/AppStartMetrics;
public fun getLegacyAppStartTimeSpan ()Lio/sentry/android/core/performance/TimeSpan;
public fun getSdkAppStartTimeSpan ()Lio/sentry/android/core/performance/TimeSpan;
public fun isAppLaunchedInForeground ()Z
public static fun onApplicationCreate (Landroid/app/Application;)V
public static fun onApplicationPostCreate (Landroid/app/Application;)V
@@ -430,15 +430,6 @@ public final class io/sentry/android/core/performance/AppStartMetrics$AppStartTy
public static fun values ()[Lio/sentry/android/core/performance/AppStartMetrics$AppStartType;
}

public class io/sentry/android/core/performance/NextDrawListener : android/view/View$OnAttachStateChangeListener, android/view/ViewTreeObserver$OnDrawListener {
protected fun <init> (Landroid/os/Handler;Ljava/lang/Runnable;)V
public static fun forActivity (Landroid/app/Activity;Ljava/lang/Runnable;)Lio/sentry/android/core/performance/NextDrawListener;
public fun onDraw ()V
public fun onViewAttachedToWindow (Landroid/view/View;)V
public fun onViewDetachedFromWindow (Landroid/view/View;)V
public fun unregister ()V
}

public class io/sentry/android/core/performance/TimeSpan : java/lang/Comparable {
public fun <init> ()V
public fun compareTo (Lio/sentry/android/core/performance/TimeSpan;)I
@@ -447,10 +438,10 @@ public class io/sentry/android/core/performance/TimeSpan : java/lang/Comparable
public fun getDurationMs ()J
public fun getProjectedStopTimestamp ()Lio/sentry/SentryDate;
public fun getProjectedStopTimestampMs ()J
public fun getProjectedStopTimestampS ()D
public fun getProjectedStopTimestampSecs ()D
public fun getStartTimestamp ()Lio/sentry/SentryDate;
public fun getStartTimestampMs ()J
public fun getStartTimestampS ()D
public fun getStartTimestampSecs ()D
public fun getStartUptimeMs ()J
public fun hasNotStarted ()Z
public fun hasNotStopped ()Z
Original file line number Diff line number Diff line change
@@ -691,7 +691,7 @@ private void finishAppStartSpan() {
final @NotNull TimeSpan appStartTimeSpan =
options.isEnableStarfish()
? AppStartMetrics.getInstance().getAppStartTimeSpan()
: AppStartMetrics.getInstance().getLegacyAppStartTimeSpan();
: AppStartMetrics.getInstance().getSdkAppStartTimeSpan();
return appStartTimeSpan;
}
}
Original file line number Diff line number Diff line change
@@ -204,7 +204,7 @@ private void setAppExtras(final @NotNull App app, final @NotNull Hint hint) {
final @NotNull TimeSpan appStartTimeSpan =
options.isEnableStarfish()
? AppStartMetrics.getInstance().getAppStartTimeSpan()
: AppStartMetrics.getInstance().getLegacyAppStartTimeSpan();
: AppStartMetrics.getInstance().getSdkAppStartTimeSpan();
if (appStartTimeSpan.hasStarted()) {
app.setAppStartTime(DateUtils.toUtilDate(appStartTimeSpan.getStartTimestamp()));
}
Original file line number Diff line number Diff line change
@@ -105,7 +105,7 @@ public static Map<String, Object> serializeScope(
final @NotNull TimeSpan appStartTimeSpan =
options.isEnableStarfish()
? AppStartMetrics.getInstance().getAppStartTimeSpan()
: AppStartMetrics.getInstance().getLegacyAppStartTimeSpan();
: AppStartMetrics.getInstance().getSdkAppStartTimeSpan();
if (appStartTimeSpan.hasStarted()) {
app.setAppStartTime(DateUtils.toUtilDate(appStartTimeSpan.getStartTimestamp()));
}
Original file line number Diff line number Diff line change
@@ -73,7 +73,7 @@ public SentryEvent process(@NotNull SentryEvent event, @NotNull Hint hint) {
final @NotNull TimeSpan appStartTimeSpan =
options.isEnableStarfish()
? AppStartMetrics.getInstance().getAppStartTimeSpan()
: AppStartMetrics.getInstance().getLegacyAppStartTimeSpan();
: AppStartMetrics.getInstance().getSdkAppStartTimeSpan();
final long appStartUpInterval = appStartTimeSpan.getDurationMs();

// if appStartUpInterval is 0, metrics are not ready to be sent
@@ -155,10 +155,10 @@ private void attachColdAppStartSpans(
if (!contentProviderOnCreates.isEmpty()) {
final @NotNull SentrySpan contentProviderRootSpan =
new SentrySpan(
contentProviderOnCreates.get(0).getStartTimestampS(),
contentProviderOnCreates.get(0).getStartTimestampSecs(),
contentProviderOnCreates
.get(contentProviderOnCreates.size() - 1)
.getProjectedStopTimestampS(),
.getProjectedStopTimestampSecs(),
traceId,
new SpanId(),
null,
@@ -183,11 +183,11 @@ private void attachColdAppStartSpans(
if (!activityLifecycleTimeSpans.isEmpty()) {
final SentrySpan activityRootSpan =
new SentrySpan(
activityLifecycleTimeSpans.get(0).onCreate.getStartTimestampS(),
activityLifecycleTimeSpans.get(0).getOnCreate().getStartTimestampSecs(),
activityLifecycleTimeSpans
.get(activityLifecycleTimeSpans.size() - 1)
.onStart
.getProjectedStopTimestampS(),
.getOnStart()
.getProjectedStopTimestampSecs(),
traceId,
new SpanId(),
null,
@@ -200,17 +200,19 @@ private void attachColdAppStartSpans(
txn.getSpans().add(activityRootSpan);

for (ActivityLifecycleTimeSpan activityTimeSpan : activityLifecycleTimeSpans) {
if (activityTimeSpan.onCreate.hasStarted() && activityTimeSpan.onCreate.hasStopped()) {
if (activityTimeSpan.getOnCreate().hasStarted()
&& activityTimeSpan.getOnCreate().hasStopped()) {
txn.getSpans()
.add(
timeSpanToSentrySpan(
activityTimeSpan.onCreate, activityRootSpan.getSpanId(), traceId));
activityTimeSpan.getOnCreate(), activityRootSpan.getSpanId(), traceId));
}
if (activityTimeSpan.onStart.hasStarted() && activityTimeSpan.onStart.hasStopped()) {
if (activityTimeSpan.getOnStart().hasStarted()
&& activityTimeSpan.getOnStart().hasStopped()) {
txn.getSpans()
.add(
timeSpanToSentrySpan(
activityTimeSpan.onStart, activityRootSpan.getSpanId(), traceId));
activityTimeSpan.getOnStart(), activityRootSpan.getSpanId(), traceId));
}
}
}
@@ -222,8 +224,8 @@ private static SentrySpan timeSpanToSentrySpan(
final @Nullable SpanId parentSpanId,
final @NotNull SentryId traceId) {
return new SentrySpan(
span.getStartTimestampS(),
span.getProjectedStopTimestampS(),
span.getStartTimestampSecs(),
span.getProjectedStopTimestampSecs(),
traceId,
new SpanId(),
parentSpanId,
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.android.core;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Process;
import android.os.SystemClock;
@@ -76,6 +77,7 @@ public static void init(
* @param logger your custom logger that implements ILogger
* @param configuration Sentry.OptionsConfiguration configuration handler
*/
@SuppressLint("NewApi")
public static synchronized void init(
@NotNull final Context context,
@NotNull ILogger logger,
@@ -127,15 +129,14 @@ public static synchronized void init(
final @NotNull TimeSpan appStartTimeSpan =
AppStartMetrics.getInstance().getAppStartTimeSpan();
if (appStartTimeSpan.hasNotStarted()
&& android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
&& buildInfoProvider.getSdkInfoVersion() >= android.os.Build.VERSION_CODES.N) {
appStartTimeSpan.setStartedAt(Process.getStartUptimeMillis());
}
} else {
final @NotNull TimeSpan appStartTime =
AppStartMetrics.getInstance().getLegacyAppStartTimeSpan();
if (appStartTime.hasNotStarted()) {
appStartTime.setStartedAt(appStart);
}
}
final @NotNull TimeSpan sdkAppStartTime =
AppStartMetrics.getInstance().getSdkAppStartTimeSpan();
if (sdkAppStartTime.hasNotStarted()) {
sdkAppStartTime.setStartedAt(appStart);
}

AndroidOptionsInitializer.initializeIntegrationsAndProcessors(
Original file line number Diff line number Diff line change
@@ -592,12 +592,12 @@ public void setAttachAnrThreadDump(final boolean attachAnrThreadDump) {
this.attachAnrThreadDump = attachAnrThreadDump;
}

@ApiStatus.Internal
@ApiStatus.Experimental
public boolean isEnableStarfish() {
return enableStarfish;
}

@ApiStatus.Internal
@ApiStatus.Experimental
public void setEnableStarfish(final boolean enableStarfish) {
this.enableStarfish = enableStarfish;
}
Original file line number Diff line number Diff line change
@@ -11,10 +11,11 @@
import android.os.Process;
import android.os.SystemClock;
import androidx.annotation.NonNull;
import io.sentry.NoOpLogger;
import io.sentry.android.core.internal.util.FirstDrawDoneListener;
import io.sentry.android.core.performance.ActivityLifecycleCallbacksAdapter;
import io.sentry.android.core.performance.ActivityLifecycleTimeSpan;
import io.sentry.android.core.performance.AppStartMetrics;
import io.sentry.android.core.performance.NextDrawListener;
import io.sentry.android.core.performance.TimeSpan;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -28,7 +29,7 @@ public final class SentryPerformanceProvider extends EmptySecureContentProvider

// static to rely on Class load
// SystemClock.uptimeMillis() isn't affected by phone provider or clock changes.
private static final long legacyAppStartMillis = SystemClock.uptimeMillis();
private static final long sdkAppStartMillis = SystemClock.uptimeMillis();

private @Nullable Application app;
private @Nullable Application.ActivityLifecycleCallbacks activityCallback;
@@ -59,8 +60,8 @@ public String getType(@NotNull Uri uri) {
public void onAppLaunched() {
// pre-starfish: use static field init as app start time
final @NotNull AppStartMetrics appStartMetrics = AppStartMetrics.getInstance();
final @NotNull TimeSpan legacyAppStartSpan = appStartMetrics.getLegacyAppStartTimeSpan();
legacyAppStartSpan.setStartedAt(legacyAppStartMillis);
final @NotNull TimeSpan sdkAppStartTimeSpan = appStartMetrics.getSdkAppStartTimeSpan();
sdkAppStartTimeSpan.setStartedAt(sdkAppStartMillis);

// starfish: Use Process.getStartUptimeMillis()
// Process.getStartUptimeMillis() requires API level 24+
@@ -99,7 +100,7 @@ public void onActivityPreCreated(
}

final ActivityLifecycleTimeSpan timeSpan = new ActivityLifecycleTimeSpan();
timeSpan.onCreate.setStartedAt(now);
timeSpan.getOnCreate().setStartedAt(now);
activityLifecycleMap.put(activity, timeSpan);
}

@@ -123,8 +124,8 @@ public void onActivityPostCreated(

final @Nullable ActivityLifecycleTimeSpan timeSpan = activityLifecycleMap.get(activity);
if (timeSpan != null) {
timeSpan.onCreate.stop();
timeSpan.onCreate.setDescription(activity.getClass().getName() + ".onCreate");
timeSpan.getOnCreate().stop();
timeSpan.getOnCreate().setDescription(activity.getClass().getName() + ".onCreate");
}
}

@@ -136,7 +137,7 @@ public void onActivityPreStarted(@NonNull Activity activity) {
}
final @Nullable ActivityLifecycleTimeSpan timeSpan = activityLifecycleMap.get(activity);
if (timeSpan != null) {
timeSpan.onStart.setStartedAt(now);
timeSpan.getOnStart().setStartedAt(now);
}
}

@@ -145,16 +146,17 @@ public void onActivityStarted(@NonNull Activity activity) {
if (firstDrawDone.get()) {
return;
}
NextDrawListener.forActivity(
FirstDrawDoneListener.registerForNextDraw(
activity,
() -> {
handler.postAtFrontOfQueue(
() -> {
if (firstDrawDone.compareAndSet(false, true)) {
onAppStartDone();
}
});
});
() ->
handler.postAtFrontOfQueue(
() -> {
if (firstDrawDone.compareAndSet(false, true)) {
onAppStartDone();
}
}),
// as the SDK isn't initialized yet, we don't have access to SentryOptions
new BuildInfoProvider(NoOpLogger.getInstance()));
}

@Override
@@ -165,8 +167,8 @@ public void onActivityPostStarted(@NonNull Activity activity) {
return;
}
if (timeSpan != null) {
timeSpan.onStart.stop();
timeSpan.onStart.setDescription(activity.getClass().getName() + ".onStart");
timeSpan.getOnStart().stop();
timeSpan.getOnStart().setDescription(activity.getClass().getName() + ".onStart");

appStartMetrics.addActivityLifecycleTimeSpans(timeSpan);
}
Original file line number Diff line number Diff line change
@@ -56,7 +56,7 @@ public void store(@NotNull SentryEnvelope envelope, @NotNull Hint hint) {
final TimeSpan appStartTimeSpan =
options.isEnableStarfish()
? AppStartMetrics.getInstance().getAppStartTimeSpan()
: AppStartMetrics.getInstance().getLegacyAppStartTimeSpan();
: AppStartMetrics.getInstance().getSdkAppStartTimeSpan();

if (HintUtils.hasType(hint, UncaughtExceptionHandlerIntegration.UncaughtExceptionHint.class)
&& appStartTimeSpan.hasStarted()) {
Original file line number Diff line number Diff line change
@@ -18,12 +18,17 @@

package io.sentry.android.core.internal.util;

import android.app.Activity;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.Window;
import androidx.annotation.Nullable;
import io.sentry.android.core.BuildInfoProvider;
import io.sentry.android.core.internal.gestures.NoOpWindowCallback;
import io.sentry.android.core.performance.WindowContentChangedCallback;
import java.util.concurrent.atomic.AtomicReference;
import org.jetbrains.annotations.NotNull;

@@ -36,6 +41,33 @@ public class FirstDrawDoneListener implements ViewTreeObserver.OnDrawListener {
private final @NotNull AtomicReference<View> viewReference;
private final @NotNull Runnable callback;

public static void registerForNextDraw(
final @NotNull Activity activity,
final @NotNull Runnable drawDoneCallback,
final @NotNull BuildInfoProvider buildInfoProvider) {

@Nullable Window window = activity.getWindow();
if (window != null) {
@Nullable View decorView = window.peekDecorView();
if (decorView != null) {
registerForNextDraw(decorView, drawDoneCallback, buildInfoProvider);
} else {
final @Nullable Window.Callback oldCallback = window.getCallback();
window.setCallback(
new WindowContentChangedCallback(
oldCallback != null ? oldCallback : new NoOpWindowCallback(),
() -> {
@Nullable View newDecorView = window.peekDecorView();
if (newDecorView != null) {
// let's set the old callback again, so we don't intercept anymore
window.setCallback(oldCallback);
registerForNextDraw(newDecorView, drawDoneCallback, buildInfoProvider);
}
}));
}
}
}

/** Registers a post-draw callback for the next draw of a view. */
public static void registerForNextDraw(
final @NotNull View view,
Original file line number Diff line number Diff line change
@@ -5,11 +5,25 @@

@ApiStatus.Internal
public class ActivityLifecycleTimeSpan implements Comparable<ActivityLifecycleTimeSpan> {
public final @NotNull TimeSpan onCreate = new TimeSpan();
public final @NotNull TimeSpan onStart = new TimeSpan();
private final @NotNull TimeSpan onCreate = new TimeSpan();
private final @NotNull TimeSpan onStart = new TimeSpan();

public final @NotNull TimeSpan getOnCreate() {
return onCreate;
}

public final @NotNull TimeSpan getOnStart() {
return onStart;
}

@Override
public int compareTo(ActivityLifecycleTimeSpan o) {
return Long.compare(onCreate.getStartUptimeMs(), o.onCreate.getStartUptimeMs());
final int onCreateDiff =
Long.compare(onCreate.getStartUptimeMs(), o.onCreate.getStartUptimeMs());
if (onCreateDiff == 0) {
return Long.compare(onStart.getStartUptimeMs(), o.onStart.getStartUptimeMs());
} else {
return onCreateDiff;
}
}
}
Loading