Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Fixes

- Prevent duplicated breadcrumbs on tombstone-merged native crash events ([#5888](https://github.com/getsentry/sentry-java/pull/5888))

### Dependencies

- Bump Native SDK from v0.16.2 to v0.16.3 ([#5962](https://github.com/getsentry/sentry-java/pull/5962))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,16 +349,19 @@ private void setExtras(final @NotNull SentryBaseEvent event) {

@SuppressWarnings("unchecked")
private void setBreadcrumbs(final @NotNull SentryBaseEvent event) {
final List<Breadcrumb> eventBreadcrumbs = event.getBreadcrumbs();
if (eventBreadcrumbs != null && !eventBreadcrumbs.isEmpty()) {
// the event already carries its own breadcrumbs (e.g. a tombstone-merged native
// crash event), so appending the persisted ones here would duplicate entries. Skip the
// disk read altogether since the result would be discarded anyway.
return;
}
final List<Breadcrumb> breadcrumbs =
(List<Breadcrumb>) readFromDisk(options, BREADCRUMBS_FILENAME, List.class);
if (breadcrumbs == null) {
return;
}
if (event.getBreadcrumbs() == null) {
event.setBreadcrumbs(breadcrumbs);
} else {
event.getBreadcrumbs().addAll(breadcrumbs);
}
event.setBreadcrumbs(breadcrumbs);
}

@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,21 @@ class ApplicationExitInfoEventProcessorTest {
assertEquals("Google Chrome", processed.contexts.browser!!.name)
}

@Test
fun `when backfillable event already has breadcrumbs, does not duplicate them with persisted ones`() {
// simulates a tombstone-merged native crash event, which already carries its own
// breadcrumb history captured at crash time, overlapping with what was persisted to disk
val hint = HintUtils.createWithTypeCheckHint(BackfillableHint())

val processed =
processEvent(hint, populateScopeCache = true) {
breadcrumbs = listOf(Breadcrumb.debug("own-crash-time-breadcrumb"))
}

assertEquals(1, processed.breadcrumbs!!.size)
assertEquals("own-crash-time-breadcrumb", processed.breadcrumbs!![0].message)
}

@Test
fun `when backfillable event is enrichable, does not backfill user ip`() {
val hint = HintUtils.createWithTypeCheckHint(BackfillableHint())
Expand Down Expand Up @@ -670,10 +685,11 @@ class ApplicationExitInfoEventProcessorTest {

assertEquals("MainActivity", processed.transaction)
assertEquals(DEBUG, processed.level)
assertEquals(3, processed.breadcrumbs!!.size)
// breadcrumbs already set on the event are preserved as-is, not merged with the persisted
// ones, since the event already carries its own authoritative breadcrumb history
assertEquals(1, processed.breadcrumbs!!.size)
assertEquals("debug", processed.breadcrumbs!![0].type)
assertEquals("debug", processed.breadcrumbs!![1].type)
assertEquals("navigation", processed.breadcrumbs!![2].type)
assertEquals("test", processed.breadcrumbs!![0].message)

assertEquals("debug", processed.environment)
assertEquals("io.sentry.samples@1.1.0+220", processed.release)
Expand Down
9 changes: 7 additions & 2 deletions sentry/src/main/java/io/sentry/SentryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -1618,8 +1618,13 @@ public void captureBatchedMetricsEvents(final @NotNull SentryMetricsEvents metri
}
}
}
if (sentryBaseEvent.getBreadcrumbs() == null) {
sentryBaseEvent.setBreadcrumbs(new ArrayList<>(scope.getBreadcrumbs()));
final List<Breadcrumb> eventBreadcrumbs = sentryBaseEvent.getBreadcrumbs();
if (eventBreadcrumbs == null || eventBreadcrumbs.isEmpty()) {
// A cached event comes from the outbox; its breadcrumbs (even if empty) belong to a
// past session, so the current scope's breadcrumbs are unrelated and must not be applied.
if (!isCached) {
sentryBaseEvent.setBreadcrumbs(new ArrayList<>(scope.getBreadcrumbs()));
}
} else if (!isCached) {
Comment thread
sentry[bot] marked this conversation as resolved.
// A Cached event comes from the outbox and already carries its own breadcrumbs (e.g. native
// events written by sentry-native). Appending the scope's breadcrumbs would duplicate them.
Expand Down
Loading