diff --git a/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt b/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt index 4217954be1..96c3dad9f1 100644 --- a/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt +++ b/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt @@ -77,6 +77,7 @@ class SentryAppenderTest { @BeforeTest fun `clear MDC`() { MDC.clear() + Sentry.close() } @Test diff --git a/sentry/src/main/java/io/sentry/Breadcrumb.java b/sentry/src/main/java/io/sentry/Breadcrumb.java index fcd9407993..ddb19e5052 100644 --- a/sentry/src/main/java/io/sentry/Breadcrumb.java +++ b/sentry/src/main/java/io/sentry/Breadcrumb.java @@ -22,6 +22,8 @@ public final class Breadcrumb implements JsonUnknown, JsonSerializable, Comparab /** A timestamp representing when the breadcrumb occurred. */ private final @NotNull Date timestamp; + private final @NotNull Long nanos; + /** If a message is provided, its rendered as text and the whitespace is preserved. */ private @Nullable String message; @@ -46,10 +48,12 @@ public final class Breadcrumb implements JsonUnknown, JsonSerializable, Comparab * @param timestamp the timestamp */ public Breadcrumb(final @NotNull Date timestamp) { + this.nanos = System.nanoTime(); this.timestamp = timestamp; } Breadcrumb(final @NotNull Breadcrumb breadcrumb) { + this.nanos = System.nanoTime(); this.timestamp = breadcrumb.timestamp; this.message = breadcrumb.message; this.type = breadcrumb.type; @@ -663,8 +667,11 @@ public void setUnknown(@Nullable Map unknown) { @Override @SuppressWarnings("JavaUtilDate") public int compareTo(@NotNull Breadcrumb o) { - // TODO also use nano time if equal - return timestamp.compareTo(o.timestamp); + int timestampCompare = timestamp.compareTo(o.timestamp); + if (timestampCompare == 0) { + return nanos.compareTo(o.nanos); + } + return timestampCompare; } public static final class JsonKeys { diff --git a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt new file mode 100644 index 0000000000..38023da18e --- /dev/null +++ b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt @@ -0,0 +1,72 @@ +package io.sentry + +import kotlin.test.Test +import kotlin.test.assertEquals + +class CombinedScopeViewTest { + + @Test + fun `adds breadcrumbs from all scopes in sorted order`() { + val options = SentryOptions() + val globalScope = Scope(options) + val isolationScope = Scope(options) + val scope = Scope(options) + + val combined = CombinedScopeView(globalScope, isolationScope, scope) + + globalScope.addBreadcrumb(Breadcrumb.info("global 1")) + isolationScope.addBreadcrumb(Breadcrumb.info("isolation 1")) + scope.addBreadcrumb(Breadcrumb.info("current 1")) + + globalScope.addBreadcrumb(Breadcrumb.info("global 2")) + isolationScope.addBreadcrumb(Breadcrumb.info("isolation 2")) + scope.addBreadcrumb(Breadcrumb.info("current 2")) + + val breadcrumbs = combined.breadcrumbs + assertEquals("global 1", breadcrumbs.poll().message) + assertEquals("isolation 1", breadcrumbs.poll().message) + assertEquals("current 1", breadcrumbs.poll().message) + assertEquals("global 2", breadcrumbs.poll().message) + assertEquals("isolation 2", breadcrumbs.poll().message) + assertEquals("current 2", breadcrumbs.poll().message) + } + + @Test + fun `oldest breadcrumbs are dropped first`() { + val options = SentryOptions().also { it.maxBreadcrumbs = 5 } + val globalScope = Scope(options) + val isolationScope = Scope(options) + val scope = Scope(options) + + val combined = CombinedScopeView(globalScope, isolationScope, scope) + + globalScope.addBreadcrumb(Breadcrumb.info("global 1")) + isolationScope.addBreadcrumb(Breadcrumb.info("isolation 1")) + scope.addBreadcrumb(Breadcrumb.info("current 1")) + + globalScope.addBreadcrumb(Breadcrumb.info("global 2")) + isolationScope.addBreadcrumb(Breadcrumb.info("isolation 2")) + scope.addBreadcrumb(Breadcrumb.info("current 2")) + + val breadcrumbs = combined.breadcrumbs +// assertEquals("global 1", breadcrumbs.poll().message) <-- was dropped + assertEquals("isolation 1", breadcrumbs.poll().message) + assertEquals("current 1", breadcrumbs.poll().message) + assertEquals("global 2", breadcrumbs.poll().message) + assertEquals("isolation 2", breadcrumbs.poll().message) + assertEquals("current 2", breadcrumbs.poll().message) + + scope.addBreadcrumb(Breadcrumb.info("current 3")) + scope.addBreadcrumb(Breadcrumb.info("current 4")) + + val breadcrumbs2 = combined.breadcrumbs +// assertEquals("global 1", breadcrumbs.poll().message) <-- was dropped +// assertEquals("isolation 1", breadcrumbs2.poll().message) <-- dropped +// assertEquals("current 1", breadcrumbs2.poll().message) <-- dropped + assertEquals("global 2", breadcrumbs2.poll().message) + assertEquals("isolation 2", breadcrumbs2.poll().message) + assertEquals("current 2", breadcrumbs2.poll().message) + assertEquals("current 3", breadcrumbs2.poll().message) + assertEquals("current 4", breadcrumbs2.poll().message) + } +}