Skip to content

Commit

Permalink
Set mechanism type to suppressed for suppressed exceptions (#4125)
Browse files Browse the repository at this point in the history
* set mechanism type to suppressed for suppressed exceptions

* changelog
  • Loading branch information
adinauer authored Jan 29, 2025
1 parent 880dc4f commit 2fc1ed7
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 14 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
- Remove `java.lang.ClassNotFoundException` debug logs when searching for OpenTelemetry marker classes ([#4091](https://github.com/getsentry/sentry-java/pull/4091))
- There was up to three of these, one for `io.sentry.opentelemetry.agent.AgentMarker`, `io.sentry.opentelemetry.agent.AgentlessMarker` and `io.sentry.opentelemetry.agent.AgentlessSpringMarker`.
- These were not indicators of something being wrong but rather the SDK looking at what is available at runtime to configure itself accordingly.
- Set mechanism `type` to `suppressed` for suppressed exceptions ([#4125](https://github.com/getsentry/sentry-java/pull/4125))
- This helps to distinguish an exceptions cause from any suppressed exceptions in the Sentry UI

### Dependencies

Expand Down
12 changes: 8 additions & 4 deletions sentry/src/main/java/io/sentry/SentryExceptionFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,15 @@ public List<SentryException> getSentryExceptions(final @NotNull Throwable throwa
@NotNull
Deque<SentryException> extractExceptionQueue(final @NotNull Throwable throwable) {
return extractExceptionQueueInternal(
throwable, new AtomicInteger(-1), new HashSet<>(), new ArrayDeque<>());
throwable, new AtomicInteger(-1), new HashSet<>(), new ArrayDeque<>(), null);
}

Deque<SentryException> extractExceptionQueueInternal(
final @NotNull Throwable throwable,
final @NotNull AtomicInteger exceptionId,
final @NotNull HashSet<Throwable> circularityDetector,
final @NotNull Deque<SentryException> exceptions) {
final @NotNull Deque<SentryException> exceptions,
@Nullable String mechanismTypeOverride) {
Mechanism exceptionMechanism;
Thread thread;

Expand All @@ -154,6 +155,8 @@ Deque<SentryException> extractExceptionQueueInternal(
// Stack the exceptions to send them in the reverse order
while (currentThrowable != null && circularityDetector.add(currentThrowable)) {
boolean snapshot = false;
final @NotNull String mechanismType =
mechanismTypeOverride == null ? "chained" : mechanismTypeOverride;
if (currentThrowable instanceof ExceptionMechanismException) {
// this is for ANR I believe
ExceptionMechanismException exceptionMechanismThrowable =
Expand All @@ -177,7 +180,7 @@ Deque<SentryException> extractExceptionQueueInternal(
exceptions.addFirst(exception);

if (exceptionMechanism.getType() == null) {
exceptionMechanism.setType("chained");
exceptionMechanism.setType(mechanismType);
}

if (exceptionId.get() >= 0) {
Expand All @@ -194,11 +197,12 @@ Deque<SentryException> extractExceptionQueueInternal(
// exceptionMechanism.setExceptionGroup(true);
for (Throwable suppressedThrowable : suppressed) {
extractExceptionQueueInternal(
suppressedThrowable, exceptionId, circularityDetector, exceptions);
suppressedThrowable, exceptionId, circularityDetector, exceptions, "suppressed");
}
}
currentThrowable = currentThrowable.getCause();
parentId = currentExceptionId;
mechanismTypeOverride = null;
}

return exceptions;
Expand Down
39 changes: 29 additions & 10 deletions sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ class SentryExceptionFactoryTest {
@Test
fun `when exception with mechanism suppressed exceptions, add them and show as group`() {
val exception = Exception("message")
val suppressedException = Exception("suppressed")
val suppressedException = Exception("suppressed exception")
exception.addSuppressed(suppressedException)

val mechanism = Mechanism()
Expand All @@ -225,19 +225,21 @@ class SentryExceptionFactoryTest {
val suppressedInQueue = queue.pop()
val mainInQueue = queue.pop()

assertEquals("suppressed", suppressedInQueue.value)
assertEquals("suppressed exception", suppressedInQueue.value)
assertEquals(1, suppressedInQueue.mechanism?.exceptionId)
assertEquals(0, suppressedInQueue.mechanism?.parentId)
assertEquals("suppressed", suppressedInQueue.mechanism?.type)

assertEquals("message", mainInQueue.value)
assertEquals(0, mainInQueue.mechanism?.exceptionId)
assertEquals("ANR", mainInQueue.mechanism?.type)
// assertEquals(true, mainInQueue.mechanism?.isExceptionGroup)
}

@Test
fun `nested exception that contains suppressed exceptions is marked as group`() {
val exception = Exception("inner")
val suppressedException = Exception("suppressed")
val suppressedException = Exception("suppressed exception")
exception.addSuppressed(suppressedException)

val outerException = Exception("outer", exception)
Expand All @@ -248,25 +250,28 @@ class SentryExceptionFactoryTest {
val mainInQueue = queue.pop()
val outerInQueue = queue.pop()

assertEquals("suppressed", suppressedInQueue.value)
assertEquals("suppressed exception", suppressedInQueue.value)
assertEquals(2, suppressedInQueue.mechanism?.exceptionId)
assertEquals(1, suppressedInQueue.mechanism?.parentId)
assertEquals("suppressed", suppressedInQueue.mechanism?.type)

assertEquals("inner", mainInQueue.value)
assertEquals(1, mainInQueue.mechanism?.exceptionId)
assertEquals(0, mainInQueue.mechanism?.parentId)
assertEquals("chained", mainInQueue.mechanism?.type)
// assertEquals(true, mainInQueue.mechanism?.isExceptionGroup)

assertEquals("outer", outerInQueue.value)
assertEquals(0, outerInQueue.mechanism?.exceptionId)
assertNull(outerInQueue.mechanism?.parentId)
assertEquals("chained", outerInQueue.mechanism?.type)
// assertNull(outerInQueue.mechanism?.isExceptionGroup)
}

@Test
fun `nested exception within Mechanism that contains suppressed exceptions is marked as group`() {
val exception = Exception("inner")
val suppressedException = Exception("suppressed")
val suppressedException = Exception("suppressed exception")
exception.addSuppressed(suppressedException)

val mechanism = Mechanism()
Expand All @@ -281,18 +286,21 @@ class SentryExceptionFactoryTest {
val mainInQueue = queue.pop()
val outerInQueue = queue.pop()

assertEquals("suppressed", suppressedInQueue.value)
assertEquals("suppressed exception", suppressedInQueue.value)
assertEquals(2, suppressedInQueue.mechanism?.exceptionId)
assertEquals(1, suppressedInQueue.mechanism?.parentId)
assertEquals("suppressed", suppressedInQueue.mechanism?.type)

assertEquals("inner", mainInQueue.value)
assertEquals(1, mainInQueue.mechanism?.exceptionId)
assertEquals(0, mainInQueue.mechanism?.parentId)
assertEquals("chained", mainInQueue.mechanism?.type)
// assertEquals(true, mainInQueue.mechanism?.isExceptionGroup)

assertEquals("outer", outerInQueue.value)
assertEquals(0, outerInQueue.mechanism?.exceptionId)
assertNull(outerInQueue.mechanism?.parentId)
assertEquals("ANR", outerInQueue.mechanism?.type)
// assertNull(outerInQueue.mechanism?.isExceptionGroup)
}

Expand All @@ -303,7 +311,7 @@ class SentryExceptionFactoryTest {
innerMostException.addSuppressed(innerMostSuppressed)

val innerException = Exception("inner", innerMostException)
val innerSuppressed = Exception("suppressed")
val innerSuppressed = Exception("suppressed exception")
innerException.addSuppressed(innerSuppressed)

val outerException = Exception("outer", innerException)
Expand All @@ -319,27 +327,32 @@ class SentryExceptionFactoryTest {
assertEquals("innermostSuppressed", innerMostSuppressedInQueue.value)
assertEquals(4, innerMostSuppressedInQueue.mechanism?.exceptionId)
assertEquals(3, innerMostSuppressedInQueue.mechanism?.parentId)
assertEquals("suppressed", innerMostSuppressedInQueue.mechanism?.type)
assertNull(innerMostSuppressedInQueue.mechanism?.isExceptionGroup)

assertEquals("innermost", innerMostExceptionInQueue.value)
assertEquals(3, innerMostExceptionInQueue.mechanism?.exceptionId)
assertEquals(1, innerMostExceptionInQueue.mechanism?.parentId)
assertEquals("chained", innerMostExceptionInQueue.mechanism?.type)
// assertEquals(true, innerMostExceptionInQueue.mechanism?.isExceptionGroup)

assertEquals("suppressed", innerSuppressedInQueue.value)
assertEquals("suppressed exception", innerSuppressedInQueue.value)
assertEquals(2, innerSuppressedInQueue.mechanism?.exceptionId)
assertEquals(1, innerSuppressedInQueue.mechanism?.parentId)
assertEquals("suppressed", innerSuppressedInQueue.mechanism?.type)
assertNull(innerSuppressedInQueue.mechanism?.isExceptionGroup)

assertEquals("inner", innerExceptionInQueue.value)
assertEquals(1, innerExceptionInQueue.mechanism?.exceptionId)
assertEquals(0, innerExceptionInQueue.mechanism?.parentId)
assertEquals("chained", innerExceptionInQueue.mechanism?.type)
// assertEquals(true, innerExceptionInQueue.mechanism?.isExceptionGroup)

assertEquals("outer", outerInQueue.value)
assertEquals(0, outerInQueue.mechanism?.exceptionId)
assertNull(outerInQueue.mechanism?.parentId)
assertNull(outerInQueue.mechanism?.isExceptionGroup)
assertEquals("chained", outerInQueue.mechanism?.type)
}

@Test
Expand All @@ -351,7 +364,7 @@ class SentryExceptionFactoryTest {
innerMostException.addSuppressed(innerMostSuppressed)

val innerException = Exception("inner", innerMostException)
val innerSuppressed = Exception("suppressed")
val innerSuppressed = Exception("suppressed exception")
innerException.addSuppressed(innerSuppressed)

val outerException = Exception("outer", innerException)
Expand All @@ -369,31 +382,37 @@ class SentryExceptionFactoryTest {
assertEquals(5, innerMostSuppressedNestedExceptionInQueue.mechanism?.exceptionId)
assertEquals(4, innerMostSuppressedNestedExceptionInQueue.mechanism?.parentId)
assertNull(innerMostSuppressedNestedExceptionInQueue.mechanism?.isExceptionGroup)
assertEquals("chained", innerMostSuppressedNestedExceptionInQueue.mechanism?.type)

assertEquals("innermostSuppressed", innerMostSuppressedInQueue.value)
assertEquals(4, innerMostSuppressedInQueue.mechanism?.exceptionId)
assertEquals(3, innerMostSuppressedInQueue.mechanism?.parentId)
assertNull(innerMostSuppressedInQueue.mechanism?.isExceptionGroup)
assertEquals("suppressed", innerMostSuppressedInQueue.mechanism?.type)

assertEquals("innermost", innerMostExceptionInQueue.value)
assertEquals(3, innerMostExceptionInQueue.mechanism?.exceptionId)
assertEquals(1, innerMostExceptionInQueue.mechanism?.parentId)
assertEquals("chained", innerMostExceptionInQueue.mechanism?.type)
// assertEquals(true, innerMostExceptionInQueue.mechanism?.isExceptionGroup)

assertEquals("suppressed", innerSuppressedInQueue.value)
assertEquals("suppressed exception", innerSuppressedInQueue.value)
assertEquals(2, innerSuppressedInQueue.mechanism?.exceptionId)
assertEquals(1, innerSuppressedInQueue.mechanism?.parentId)
assertEquals("suppressed", innerSuppressedInQueue.mechanism?.type)
assertNull(innerSuppressedInQueue.mechanism?.isExceptionGroup)

assertEquals("inner", innerExceptionInQueue.value)
assertEquals(1, innerExceptionInQueue.mechanism?.exceptionId)
assertEquals(0, innerExceptionInQueue.mechanism?.parentId)
assertEquals("chained", innerExceptionInQueue.mechanism?.type)
// assertEquals(true, innerExceptionInQueue.mechanism?.isExceptionGroup)

assertEquals("outer", outerInQueue.value)
assertEquals(0, outerInQueue.mechanism?.exceptionId)
assertNull(outerInQueue.mechanism?.parentId)
assertNull(outerInQueue.mechanism?.isExceptionGroup)
assertEquals("chained", outerInQueue.mechanism?.type)
}

internal class InnerClassThrowable constructor(cause: Throwable? = null) : Throwable(cause)
Expand Down

0 comments on commit 2fc1ed7

Please sign in to comment.