Skip to content

Commit 83be7c7

Browse files
markushiadinauer
andauthored
Add option to enable or disable Activity Frame Tracking (#2314)
* Add option to enable or disable Activity Frames Tracker * Add Activity Frames Tracker to changelog * Use options flag instead of Noop impl for FrameTracker, improve naming * Restore ActivityFramesTracker constructor, as it's required for flutter * Fix missing @deprecated annotation for ActivityFramesTracker * Fix disable InlineMeSuggester error * Add missing .api file update * Adapt ActivityFramesTracker ctor and options nullability Co-authored-by: Alexander Dinauer <[email protected]>
1 parent 649f171 commit 83be7c7

File tree

10 files changed

+152
-36
lines changed

10 files changed

+152
-36
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- Add support for using Encoder with logback.SentryAppender ([#2246](https://github.com/getsentry/sentry-java/pull/2246))
1818
- Report Startup Crashes ([#2277](https://github.com/getsentry/sentry-java/pull/2277))
1919
- HTTP Client errors for OkHttp ([#2287](https://github.com/getsentry/sentry-java/pull/2287))
20+
- Add option to enable or disable Frame Tracking ([#2314](https://github.com/getsentry/sentry-java/pull/2314))
2021

2122
### Dependencies
2223

sentry-android-core/api/sentry-android-core.api

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
public final class io/sentry/android/core/ActivityFramesTracker {
2-
public fun <init> (Lio/sentry/android/core/LoadClass;)V
3-
public fun <init> (Lio/sentry/android/core/LoadClass;Lio/sentry/ILogger;)V
4-
public fun <init> (Lio/sentry/android/core/LoadClass;Lio/sentry/ILogger;Lio/sentry/android/core/MainLooperHandler;)V
2+
public fun <init> (Lio/sentry/android/core/LoadClass;Lio/sentry/android/core/SentryAndroidOptions;)V
3+
public fun <init> (Lio/sentry/android/core/LoadClass;Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/core/MainLooperHandler;)V
54
public fun addActivity (Landroid/app/Activity;)V
5+
public fun isFrameMetricsAggregatorAvailable ()Z
66
public fun setMetrics (Landroid/app/Activity;Lio/sentry/protocol/SentryId;)V
77
public fun stop ()V
88
public fun takeMetrics (Lio/sentry/protocol/SentryId;)Ljava/util/Map;
@@ -141,6 +141,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
141141
public fun isEnableAppComponentBreadcrumbs ()Z
142142
public fun isEnableAppLifecycleBreadcrumbs ()Z
143143
public fun isEnableAutoActivityLifecycleTracing ()Z
144+
public fun isEnableFramesTracking ()Z
144145
public fun isEnableSystemEventBreadcrumbs ()Z
145146
public fun isEnableUserInteractionBreadcrumbs ()Z
146147
public fun isEnableUserInteractionTracing ()Z
@@ -155,6 +156,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
155156
public fun setEnableAppComponentBreadcrumbs (Z)V
156157
public fun setEnableAppLifecycleBreadcrumbs (Z)V
157158
public fun setEnableAutoActivityLifecycleTracing (Z)V
159+
public fun setEnableFramesTracking (Z)V
158160
public fun setEnableSystemEventBreadcrumbs (Z)V
159161
public fun setEnableUserInteractionBreadcrumbs (Z)V
160162
public fun setEnableUserInteractionTracing (Z)V

sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import android.app.Activity;
44
import android.util.SparseIntArray;
55
import androidx.core.app.FrameMetricsAggregator;
6-
import io.sentry.ILogger;
76
import io.sentry.MeasurementUnit;
87
import io.sentry.SentryLevel;
98
import io.sentry.android.core.internal.util.MainThreadChecker;
@@ -16,6 +15,7 @@
1615
import org.jetbrains.annotations.NotNull;
1716
import org.jetbrains.annotations.Nullable;
1817
import org.jetbrains.annotations.TestOnly;
18+
import org.jetbrains.annotations.VisibleForTesting;
1919

2020
/**
2121
* A class that tracks slow and frozen frames using the FrameMetricsAggregator class from
@@ -25,48 +25,49 @@
2525
public final class ActivityFramesTracker {
2626

2727
private @Nullable FrameMetricsAggregator frameMetricsAggregator = null;
28-
private boolean androidXAvailable = true;
28+
private @NotNull final SentryAndroidOptions options;
2929

3030
private final @NotNull Map<SentryId, Map<String, @NotNull MeasurementValue>>
3131
activityMeasurements = new ConcurrentHashMap<>();
3232
private final @NotNull Map<Activity, FrameCounts> frameCountAtStartSnapshots =
3333
new WeakHashMap<>();
3434

35-
private final @Nullable ILogger logger;
3635
private final @NotNull MainLooperHandler handler;
3736

3837
public ActivityFramesTracker(
3938
final @NotNull LoadClass loadClass,
40-
final @Nullable ILogger logger,
39+
final @NotNull SentryAndroidOptions options,
4140
final @NotNull MainLooperHandler handler) {
42-
androidXAvailable =
43-
loadClass.isClassAvailable("androidx.core.app.FrameMetricsAggregator", logger);
41+
42+
final boolean androidXAvailable =
43+
loadClass.isClassAvailable("androidx.core.app.FrameMetricsAggregator", options.getLogger());
44+
4445
if (androidXAvailable) {
4546
frameMetricsAggregator = new FrameMetricsAggregator();
4647
}
47-
this.logger = logger;
48+
this.options = options;
4849
this.handler = handler;
4950
}
5051

51-
public ActivityFramesTracker(final @NotNull LoadClass loadClass, final @Nullable ILogger logger) {
52-
this(loadClass, logger, new MainLooperHandler());
53-
}
54-
55-
public ActivityFramesTracker(final @NotNull LoadClass loadClass) {
56-
this(loadClass, null);
52+
public ActivityFramesTracker(
53+
final @NotNull LoadClass loadClass, final @NotNull SentryAndroidOptions options) {
54+
this(loadClass, options, new MainLooperHandler());
5755
}
5856

5957
@TestOnly
6058
ActivityFramesTracker(
61-
final @Nullable FrameMetricsAggregator frameMetricsAggregator,
62-
final @NotNull MainLooperHandler handler) {
59+
final @NotNull LoadClass loadClass,
60+
final @NotNull SentryAndroidOptions options,
61+
final @NotNull MainLooperHandler handler,
62+
final @Nullable FrameMetricsAggregator frameMetricsAggregator) {
63+
64+
this(loadClass, options, handler);
6365
this.frameMetricsAggregator = frameMetricsAggregator;
64-
this.logger = null;
65-
this.handler = handler;
6666
}
6767

68-
private boolean isFrameMetricsAggregatorAvailable() {
69-
return androidXAvailable && frameMetricsAggregator != null;
68+
@VisibleForTesting
69+
public boolean isFrameMetricsAggregatorAvailable() {
70+
return frameMetricsAggregator != null && options.isEnableFramesTracking();
7071
}
7172

7273
@SuppressWarnings("NullAway")
@@ -215,15 +216,15 @@ private void runSafelyOnUiThread(final Runnable runnable, final String tag) {
215216
try {
216217
runnable.run();
217218
} catch (Throwable ignored) {
218-
if (logger != null && tag != null) {
219-
logger.log(SentryLevel.WARNING, "Failed to execute " + tag);
219+
if (tag != null) {
220+
options.getLogger().log(SentryLevel.WARNING, "Failed to execute " + tag);
220221
}
221222
}
222223
});
223224
}
224225
} catch (Throwable ignored) {
225-
if (logger != null && tag != null) {
226-
logger.log(SentryLevel.WARNING, "Failed to execute " + tag);
226+
if (tag != null) {
227+
options.getLogger().log(SentryLevel.WARNING, "Failed to execute " + tag);
227228
}
228229
}
229230
}

sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,12 @@ WeakHashMap<Activity, ITransaction> getActivitiesWithOngoingTransactions() {
387387
return activitiesWithOngoingTransactions;
388388
}
389389

390+
@TestOnly
391+
@NotNull
392+
ActivityFramesTracker getActivityFramesTracker() {
393+
return activityFramesTracker;
394+
}
395+
390396
@TestOnly
391397
@Nullable
392398
ISpan getAppStartSpan() {

sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ static void init(
135135
options.setEnvelopeDiskCache(new AndroidEnvelopeCache(options));
136136

137137
final ActivityFramesTracker activityFramesTracker =
138-
new ActivityFramesTracker(loadClass, options.getLogger());
138+
new ActivityFramesTracker(loadClass, options);
139+
139140
installDefaultIntegrations(
140141
context,
141142
options,

sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ final class ManifestMetadataReader {
8080

8181
static final String SEND_DEFAULT_PII = "io.sentry.send-default-pii";
8282

83+
static final String PERFORM_FRAMES_TRACKING = "io.sentry.traces.frames-tracking";
84+
8385
/** ManifestMetadataReader ctor */
8486
private ManifestMetadataReader() {}
8587

@@ -288,6 +290,8 @@ static void applyMetadata(
288290
options.setTracePropagationTargets(tracePropagationTargets);
289291
}
290292

293+
options.setEnableFramesTracking(readBool(metadata, logger, PERFORM_FRAMES_TRACKING, true));
294+
291295
options.setProguardUuid(
292296
readString(metadata, logger, PROGUARD_UUID, options.getProguardUuid()));
293297

sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ public final class SentryAndroidOptions extends SentryOptions {
131131
*/
132132
private final long startupCrashDurationThresholdMillis = 2000; // 2s
133133

134+
private boolean enableFramesTracking = true;
135+
134136
public SentryAndroidOptions() {
135137
setSentryClientName(BuildConfig.SENTRY_ANDROID_SDK_NAME + "/" + BuildConfig.VERSION_NAME);
136138
setSdkVersion(createSdkVersion());
@@ -357,6 +359,19 @@ public void setCollectAdditionalContext(boolean collectAdditionalContext) {
357359
this.collectAdditionalContext = collectAdditionalContext;
358360
}
359361

362+
public boolean isEnableFramesTracking() {
363+
return enableFramesTracking;
364+
}
365+
366+
/**
367+
* Enable or disable Frames Tracking, which is used to report slow and frozen frames.
368+
*
369+
* @param enableFramesTracking true if frames tracking should be enabled, false otherwise.
370+
*/
371+
public void setEnableFramesTracking(boolean enableFramesTracking) {
372+
this.enableFramesTracking = enableFramesTracking;
373+
}
374+
360375
/**
361376
* Returns the Startup Crash flush timeout in Millis
362377
*

sentry-android-core/src/test/java/io/sentry/android/core/ActivityFramesTrackerTest.kt

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,14 @@ class ActivityFramesTrackerTest {
2525
val sentryId = SentryId()
2626
val loadClass = mock<LoadClass>()
2727
val handler = mock<MainLooperHandler>()
28-
29-
fun getSut(): ActivityFramesTracker {
30-
return ActivityFramesTracker(aggregator, handler)
28+
val options = SentryAndroidOptions()
29+
30+
fun getSut(mockAggregator: Boolean = true): ActivityFramesTracker {
31+
return if (mockAggregator) {
32+
ActivityFramesTracker(loadClass, options, handler, aggregator)
33+
} else {
34+
ActivityFramesTracker(loadClass, options, handler)
35+
}
3136
}
3237
}
3338
private val fixture = Fixture()
@@ -287,23 +292,23 @@ class ActivityFramesTrackerTest {
287292
@Test
288293
fun `addActivity does not throw if no AndroidX`() {
289294
whenever(fixture.loadClass.isClassAvailable(any(), any<ILogger>())).thenReturn(false)
290-
val sut = ActivityFramesTracker(fixture.loadClass)
295+
val sut = fixture.getSut(false)
291296

292297
sut.addActivity(fixture.activity)
293298
}
294299

295300
@Test
296301
fun `setMetrics does not throw if no AndroidX`() {
297302
whenever(fixture.loadClass.isClassAvailable(any(), any<ILogger>())).thenReturn(false)
298-
val sut = ActivityFramesTracker(fixture.loadClass)
303+
val sut = fixture.getSut(false)
299304

300305
sut.setMetrics(fixture.activity, fixture.sentryId)
301306
}
302307

303308
@Test
304309
fun `addActivity and setMetrics combined do not throw if no AndroidX`() {
305310
whenever(fixture.loadClass.isClassAvailable(any(), any<ILogger>())).thenReturn(false)
306-
val sut = ActivityFramesTracker(fixture.loadClass)
311+
val sut = fixture.getSut(false)
307312

308313
sut.addActivity(fixture.activity)
309314
sut.setMetrics(fixture.activity, fixture.sentryId)
@@ -312,15 +317,15 @@ class ActivityFramesTrackerTest {
312317
@Test
313318
fun `setMetrics does not throw if Activity is not added`() {
314319
whenever(fixture.aggregator.metrics).thenThrow(IllegalArgumentException())
315-
val sut = ActivityFramesTracker(fixture.loadClass)
320+
val sut = fixture.getSut()
316321

317322
sut.setMetrics(fixture.activity, fixture.sentryId)
318323
}
319324

320325
@Test
321326
fun `stop does not throw if no AndroidX`() {
322327
whenever(fixture.loadClass.isClassAvailable(any(), any<ILogger>())).thenReturn(false)
323-
val sut = ActivityFramesTracker(fixture.loadClass)
328+
val sut = fixture.getSut(false)
324329

325330
sut.stop()
326331
}
@@ -337,7 +342,7 @@ class ActivityFramesTrackerTest {
337342
@Test
338343
fun `takeMetrics returns null if no AndroidX`() {
339344
whenever(fixture.loadClass.isClassAvailable(any(), any<ILogger>())).thenReturn(false)
340-
val sut = ActivityFramesTracker(fixture.loadClass)
345+
val sut = fixture.getSut(false)
341346

342347
assertNull(sut.takeMetrics(fixture.sentryId))
343348
}

sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,4 +371,47 @@ class AndroidOptionsInitializerTest {
371371

372372
assertTrue { fixture.sentryOptions.envelopeDiskCache is AndroidEnvelopeCache }
373373
}
374+
375+
@Test
376+
fun `When Activity Frames Tracking is enabled, the Activity Frames Tracker should be available`() {
377+
fixture.initSut(hasAppContext = true, configureOptions = {
378+
isEnableFramesTracking = true
379+
})
380+
381+
val activityLifeCycleIntegration = fixture.sentryOptions.integrations
382+
.first { it is ActivityLifecycleIntegration }
383+
384+
assertTrue(
385+
(activityLifeCycleIntegration as ActivityLifecycleIntegration).activityFramesTracker.isFrameMetricsAggregatorAvailable
386+
)
387+
}
388+
389+
@Test
390+
fun `When Frames Tracking is disabled, the Activity Frames Tracker should not be available`() {
391+
fixture.initSut(hasAppContext = true, configureOptions = {
392+
isEnableFramesTracking = false
393+
})
394+
395+
val activityLifeCycleIntegration = fixture.sentryOptions.integrations
396+
.first { it is ActivityLifecycleIntegration }
397+
398+
assertFalse(
399+
(activityLifeCycleIntegration as ActivityLifecycleIntegration).activityFramesTracker.isFrameMetricsAggregatorAvailable
400+
)
401+
}
402+
403+
@Test
404+
fun `When Frames Tracking is initially disabled, but enabled via configureOptions it should be available`() {
405+
fixture.sentryOptions.isEnableFramesTracking = false
406+
fixture.initSut(hasAppContext = true, configureOptions = {
407+
isEnableFramesTracking = true
408+
})
409+
410+
val activityLifeCycleIntegration = fixture.sentryOptions.integrations
411+
.first { it is ActivityLifecycleIntegration }
412+
413+
assertTrue(
414+
(activityLifeCycleIntegration as ActivityLifecycleIntegration).activityFramesTracker.isFrameMetricsAggregatorAvailable
415+
)
416+
}
374417
}

sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,4 +1086,42 @@ class ManifestMetadataReaderTest {
10861086
// Assert
10871087
assertTrue(fixture.options.isSendDefaultPii)
10881088
}
1089+
1090+
@Test
1091+
fun `applyMetadata reads frames tracking flag and keeps default value if not found`() {
1092+
// Arrange
1093+
val context = fixture.getContext()
1094+
1095+
// Act
1096+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1097+
1098+
// Assert
1099+
assertTrue(fixture.options.isEnableFramesTracking)
1100+
}
1101+
1102+
@Test
1103+
fun `applyMetadata reads frames tracking and sets it to enabled if true`() {
1104+
// Arrange
1105+
val bundle = bundleOf(ManifestMetadataReader.PERFORM_FRAMES_TRACKING to true)
1106+
val context = fixture.getContext(metaData = bundle)
1107+
1108+
// Act
1109+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1110+
1111+
// Assert
1112+
assertTrue(fixture.options.isEnableFramesTracking)
1113+
}
1114+
1115+
@Test
1116+
fun `applyMetadata reads frames tracking and sets it to disabled if false`() {
1117+
// Arrange
1118+
val bundle = bundleOf(ManifestMetadataReader.PERFORM_FRAMES_TRACKING to false)
1119+
val context = fixture.getContext(metaData = bundle)
1120+
1121+
// Act
1122+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1123+
1124+
// Assert
1125+
assertFalse(fixture.options.isEnableFramesTracking)
1126+
}
10891127
}

0 commit comments

Comments
 (0)