Skip to content

Commit 5e8b866

Browse files
markushiclaude
andauthored
feat(android): Add enableAnrFingerprinting option (#5168)
* feat(android): Add enableAnrFingerprinting option Decouple ANR fingerprinting from ANR profiling into a standalone opt-in option. This allows static fingerprinting of system-frame-only ANRs regardless of whether profiling is enabled. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(android): Mark enableAnrFingerprinting as experimental Also enable the option in the sample app. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(android): Default enableAnrFingerprinting to true Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(android): Remove experimental flag from enableAnrFingerprinting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix api dump * Address PR feedback --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 034445f commit 5e8b866

File tree

8 files changed

+72
-9
lines changed

8 files changed

+72
-9
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@
3636
- Add new experimental option to capture profiles for ANRs ([#4899](https://github.com/getsentry/sentry-java/pull/4899))
3737
- This feature will capture a stack profile of the main thread when it gets unresponsive
3838
- The profile gets attached to the ANR event on the next app start, providing a flamegraph of the ANR issue on the sentry issue details page
39-
- Breaking change: if the ANR stacktrace contains only system frames (e.g. `java.lang` or `android.os`), a static fingerprint is set on the ANR event, causing all ANR events to be grouped into a single issue, reducing the overall ANR issue noise
4039
- Enable via `options.setAnrProfilingSampleRate(<sample-rate>)` or AndroidManifest.xml: `<meta-data android:name="io.sentry.anr.profiling.sample-rate" android:value="[0.0-1.0]" />`
4140
- The sample rate controls the probability of collecting a profile for each detected foreground ANR (0.0 to 1.0, null to disable)
41+
- Add `enableAnrFingerprinting` option to reduce ANR noise by assigning static fingerprints to ANR events with system-only stacktraces
42+
- When enabled, ANRs whose stacktraces contain only system frames (e.g. `java.lang` or `android.os`) are grouped into a single issue instead of creating many separate issues
43+
- Enable via `options.setEnableAnrFingerprinting(true)` or AndroidManifest.xml: `<meta-data android:name="io.sentry.anr.enable-fingerprinting" android:value="true" />`
4244

4345
### Fixes
4446

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
367367
public fun isCollectExternalStorageContext ()Z
368368
public fun isEnableActivityLifecycleBreadcrumbs ()Z
369369
public fun isEnableActivityLifecycleTracingAutoFinish ()Z
370+
public fun isEnableAnrFingerprinting ()Z
370371
public fun isEnableAppComponentBreadcrumbs ()Z
371372
public fun isEnableAppLifecycleBreadcrumbs ()Z
372373
public fun isEnableAutoActivityLifecycleTracing ()Z
@@ -396,6 +397,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
396397
public fun setDebugImagesLoader (Lio/sentry/android/core/IDebugImagesLoader;)V
397398
public fun setEnableActivityLifecycleBreadcrumbs (Z)V
398399
public fun setEnableActivityLifecycleTracingAutoFinish (Z)V
400+
public fun setEnableAnrFingerprinting (Z)V
399401
public fun setEnableAppComponentBreadcrumbs (Z)V
400402
public fun setEnableAppLifecycleBreadcrumbs (Z)V
401403
public fun setEnableAutoActivityLifecycleTracing (Z)V

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -734,8 +734,8 @@ private void setDefaultAnrFingerprint(
734734
return;
735735
}
736736

737-
if (options.isAnrProfilingEnabled() && hasOnlySystemFrames(event)) {
738-
// If profiling did not identify any app frames, we want to statically group these events
737+
if (options.isEnableAnrFingerprinting() && hasOnlySystemFrames(event)) {
738+
// If the stacktrace only contains system frames, we want to statically group these events
739739
// to avoid ANR noise due to {{ default }} stacktrace grouping
740740
event.setFingerprints(
741741
Arrays.asList(

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

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

178178
static final String ANR_PROFILING_SAMPLE_RATE = "io.sentry.anr.profiling.sample-rate";
179179

180+
static final String ENABLE_ANR_FINGERPRINTING = "io.sentry.anr.enable-fingerprinting";
181+
180182
/** ManifestMetadataReader ctor */
181183
private ManifestMetadataReader() {}
182184

@@ -684,6 +686,10 @@ static void applyMetadata(
684686
options.setAnrProfilingSampleRate(anrProfilingSampleRate);
685687
}
686688
}
689+
690+
options.setEnableAnrFingerprinting(
691+
readBool(
692+
metadata, logger, ENABLE_ANR_FINGERPRINTING, options.isEnableAnrFingerprinting()));
687693
}
688694
options
689695
.getLogger()

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@ public interface BeforeCaptureCallback {
255255

256256
private @Nullable Double anrProfilingSampleRate;
257257

258+
private boolean enableAnrFingerprinting = true;
259+
258260
public SentryAndroidOptions() {
259261
setSentryClientName(BuildConfig.SENTRY_ANDROID_SDK_NAME + "/" + BuildConfig.VERSION_NAME);
260262
setSdkVersion(createSdkVersion());
@@ -716,6 +718,29 @@ public boolean isAnrProfilingEnabled() {
716718
return anrProfilingSampleRate != null && anrProfilingSampleRate > 0;
717719
}
718720

721+
/**
722+
* Returns whether ANR fingerprinting is enabled. When enabled, the SDK assigns static
723+
* fingerprints to ANR events that would otherwise produce noisy grouping. Currently, this applies
724+
* a static fingerprint to ANRs whose stacktraces contain only system frames and no application
725+
* frames.
726+
*
727+
* @return true if ANR fingerprinting is enabled
728+
*/
729+
public boolean isEnableAnrFingerprinting() {
730+
return enableAnrFingerprinting;
731+
}
732+
733+
/**
734+
* Sets whether ANR fingerprinting is enabled. When enabled, the SDK assigns static fingerprints
735+
* to ANR events that would otherwise produce noisy grouping. Currently, this applies a static
736+
* fingerprint to ANRs whose stacktraces contain only system frames and no application frames.
737+
*
738+
* @param enableAnrFingerprinting true to enable ANR fingerprinting
739+
*/
740+
public void setEnableAnrFingerprinting(final boolean enableAnrFingerprinting) {
741+
this.enableAnrFingerprinting = enableAnrFingerprinting;
742+
}
743+
719744
static class AndroidUserFeedbackIDialogHandler implements SentryFeedbackOptions.IDialogHandler {
720745
@Override
721746
public void showDialog(

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -636,8 +636,8 @@ class ApplicationExitInfoEventProcessorTest {
636636
}
637637

638638
@Test
639-
fun `sets system-frames-only fingerprint when ANR profiling enabled and no app frames`() {
640-
fixture.options.anrProfilingSampleRate = 1.0
639+
fun `sets system-frames-only fingerprint when ANR fingerprinting enabled and no app frames`() {
640+
fixture.options.isEnableAnrFingerprinting = true
641641
val hint = HintUtils.createWithTypeCheckHint(AbnormalExitHint(mechanism = "anr_foreground"))
642642

643643
val processed =
@@ -665,8 +665,8 @@ class ApplicationExitInfoEventProcessorTest {
665665
}
666666

667667
@Test
668-
fun `does not set system-frames-only fingerprint when ANR profiling is disabled but no app frames are present`() {
669-
fixture.options.anrProfilingSampleRate = null
668+
fun `does not set system-frames-only fingerprint when ANR fingerprinting is disabled and no app frames are present`() {
669+
fixture.options.isEnableAnrFingerprinting = false
670670
val hint = HintUtils.createWithTypeCheckHint(AbnormalExitHint(mechanism = "anr_foreground"))
671671

672672
val processed =
@@ -694,8 +694,8 @@ class ApplicationExitInfoEventProcessorTest {
694694
}
695695

696696
@Test
697-
fun `sets default fingerprint when ANR profiling enabled and app frames are present`() {
698-
fixture.options.anrProfilingSampleRate = 1.0
697+
fun `sets default fingerprint when ANR fingerprinting enabled and app frames are present`() {
698+
fixture.options.isEnableAnrFingerprinting = true
699699
val hint = HintUtils.createWithTypeCheckHint(AbnormalExitHint(mechanism = "anr_foreground"))
700700

701701
val processed =

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2022,6 +2022,31 @@ class ManifestMetadataReaderTest {
20222022
assertNull(fixture.options.anrProfilingSampleRate)
20232023
}
20242024

2025+
@Test
2026+
fun `applyMetadata reads enableAnrFingerprinting to options`() {
2027+
// Arrange
2028+
val bundle = bundleOf(ManifestMetadataReader.ENABLE_ANR_FINGERPRINTING to true)
2029+
val context = fixture.getContext(metaData = bundle)
2030+
2031+
// Act
2032+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
2033+
2034+
// Assert
2035+
assertTrue(fixture.options.isEnableAnrFingerprinting)
2036+
}
2037+
2038+
@Test
2039+
fun `applyMetadata keeps enableAnrFingerprinting default when not set in manifest`() {
2040+
// Arrange
2041+
val context = fixture.getContext()
2042+
2043+
// Act
2044+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
2045+
2046+
// Assert
2047+
assertTrue(fixture.options.isEnableAnrFingerprinting)
2048+
}
2049+
20252050
// Network Detail Configuration Tests
20262051

20272052
@Test

sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,5 +268,8 @@
268268
<meta-data
269269
android:name="io.sentry.anr.profiling.sample-rate"
270270
android:value="1.0" />
271+
<meta-data
272+
android:name="io.sentry.anr.enable-fingerprinting"
273+
android:value="true" />
271274
</application>
272275
</manifest>

0 commit comments

Comments
 (0)