Skip to content

Commit 2fc1ed7

Browse files
authored
Set mechanism type to suppressed for suppressed exceptions (#4125)
* set mechanism type to suppressed for suppressed exceptions * changelog
1 parent 880dc4f commit 2fc1ed7

File tree

3 files changed

+39
-14
lines changed

3 files changed

+39
-14
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
- Remove `java.lang.ClassNotFoundException` debug logs when searching for OpenTelemetry marker classes ([#4091](https://github.com/getsentry/sentry-java/pull/4091))
1919
- 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`.
2020
- These were not indicators of something being wrong but rather the SDK looking at what is available at runtime to configure itself accordingly.
21+
- Set mechanism `type` to `suppressed` for suppressed exceptions ([#4125](https://github.com/getsentry/sentry-java/pull/4125))
22+
- This helps to distinguish an exceptions cause from any suppressed exceptions in the Sentry UI
2123

2224
### Dependencies
2325

sentry/src/main/java/io/sentry/SentryExceptionFactory.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,15 @@ public List<SentryException> getSentryExceptions(final @NotNull Throwable throwa
137137
@NotNull
138138
Deque<SentryException> extractExceptionQueue(final @NotNull Throwable throwable) {
139139
return extractExceptionQueueInternal(
140-
throwable, new AtomicInteger(-1), new HashSet<>(), new ArrayDeque<>());
140+
throwable, new AtomicInteger(-1), new HashSet<>(), new ArrayDeque<>(), null);
141141
}
142142

143143
Deque<SentryException> extractExceptionQueueInternal(
144144
final @NotNull Throwable throwable,
145145
final @NotNull AtomicInteger exceptionId,
146146
final @NotNull HashSet<Throwable> circularityDetector,
147-
final @NotNull Deque<SentryException> exceptions) {
147+
final @NotNull Deque<SentryException> exceptions,
148+
@Nullable String mechanismTypeOverride) {
148149
Mechanism exceptionMechanism;
149150
Thread thread;
150151

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

179182
if (exceptionMechanism.getType() == null) {
180-
exceptionMechanism.setType("chained");
183+
exceptionMechanism.setType(mechanismType);
181184
}
182185

183186
if (exceptionId.get() >= 0) {
@@ -194,11 +197,12 @@ Deque<SentryException> extractExceptionQueueInternal(
194197
// exceptionMechanism.setExceptionGroup(true);
195198
for (Throwable suppressedThrowable : suppressed) {
196199
extractExceptionQueueInternal(
197-
suppressedThrowable, exceptionId, circularityDetector, exceptions);
200+
suppressedThrowable, exceptionId, circularityDetector, exceptions, "suppressed");
198201
}
199202
}
200203
currentThrowable = currentThrowable.getCause();
201204
parentId = currentExceptionId;
205+
mechanismTypeOverride = null;
202206
}
203207

204208
return exceptions;

sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ class SentryExceptionFactoryTest {
212212
@Test
213213
fun `when exception with mechanism suppressed exceptions, add them and show as group`() {
214214
val exception = Exception("message")
215-
val suppressedException = Exception("suppressed")
215+
val suppressedException = Exception("suppressed exception")
216216
exception.addSuppressed(suppressedException)
217217

218218
val mechanism = Mechanism()
@@ -225,19 +225,21 @@ class SentryExceptionFactoryTest {
225225
val suppressedInQueue = queue.pop()
226226
val mainInQueue = queue.pop()
227227

228-
assertEquals("suppressed", suppressedInQueue.value)
228+
assertEquals("suppressed exception", suppressedInQueue.value)
229229
assertEquals(1, suppressedInQueue.mechanism?.exceptionId)
230230
assertEquals(0, suppressedInQueue.mechanism?.parentId)
231+
assertEquals("suppressed", suppressedInQueue.mechanism?.type)
231232

232233
assertEquals("message", mainInQueue.value)
233234
assertEquals(0, mainInQueue.mechanism?.exceptionId)
235+
assertEquals("ANR", mainInQueue.mechanism?.type)
234236
// assertEquals(true, mainInQueue.mechanism?.isExceptionGroup)
235237
}
236238

237239
@Test
238240
fun `nested exception that contains suppressed exceptions is marked as group`() {
239241
val exception = Exception("inner")
240-
val suppressedException = Exception("suppressed")
242+
val suppressedException = Exception("suppressed exception")
241243
exception.addSuppressed(suppressedException)
242244

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

251-
assertEquals("suppressed", suppressedInQueue.value)
253+
assertEquals("suppressed exception", suppressedInQueue.value)
252254
assertEquals(2, suppressedInQueue.mechanism?.exceptionId)
253255
assertEquals(1, suppressedInQueue.mechanism?.parentId)
256+
assertEquals("suppressed", suppressedInQueue.mechanism?.type)
254257

255258
assertEquals("inner", mainInQueue.value)
256259
assertEquals(1, mainInQueue.mechanism?.exceptionId)
257260
assertEquals(0, mainInQueue.mechanism?.parentId)
261+
assertEquals("chained", mainInQueue.mechanism?.type)
258262
// assertEquals(true, mainInQueue.mechanism?.isExceptionGroup)
259263

260264
assertEquals("outer", outerInQueue.value)
261265
assertEquals(0, outerInQueue.mechanism?.exceptionId)
262266
assertNull(outerInQueue.mechanism?.parentId)
267+
assertEquals("chained", outerInQueue.mechanism?.type)
263268
// assertNull(outerInQueue.mechanism?.isExceptionGroup)
264269
}
265270

266271
@Test
267272
fun `nested exception within Mechanism that contains suppressed exceptions is marked as group`() {
268273
val exception = Exception("inner")
269-
val suppressedException = Exception("suppressed")
274+
val suppressedException = Exception("suppressed exception")
270275
exception.addSuppressed(suppressedException)
271276

272277
val mechanism = Mechanism()
@@ -281,18 +286,21 @@ class SentryExceptionFactoryTest {
281286
val mainInQueue = queue.pop()
282287
val outerInQueue = queue.pop()
283288

284-
assertEquals("suppressed", suppressedInQueue.value)
289+
assertEquals("suppressed exception", suppressedInQueue.value)
285290
assertEquals(2, suppressedInQueue.mechanism?.exceptionId)
286291
assertEquals(1, suppressedInQueue.mechanism?.parentId)
292+
assertEquals("suppressed", suppressedInQueue.mechanism?.type)
287293

288294
assertEquals("inner", mainInQueue.value)
289295
assertEquals(1, mainInQueue.mechanism?.exceptionId)
290296
assertEquals(0, mainInQueue.mechanism?.parentId)
297+
assertEquals("chained", mainInQueue.mechanism?.type)
291298
// assertEquals(true, mainInQueue.mechanism?.isExceptionGroup)
292299

293300
assertEquals("outer", outerInQueue.value)
294301
assertEquals(0, outerInQueue.mechanism?.exceptionId)
295302
assertNull(outerInQueue.mechanism?.parentId)
303+
assertEquals("ANR", outerInQueue.mechanism?.type)
296304
// assertNull(outerInQueue.mechanism?.isExceptionGroup)
297305
}
298306

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

305313
val innerException = Exception("inner", innerMostException)
306-
val innerSuppressed = Exception("suppressed")
314+
val innerSuppressed = Exception("suppressed exception")
307315
innerException.addSuppressed(innerSuppressed)
308316

309317
val outerException = Exception("outer", innerException)
@@ -319,27 +327,32 @@ class SentryExceptionFactoryTest {
319327
assertEquals("innermostSuppressed", innerMostSuppressedInQueue.value)
320328
assertEquals(4, innerMostSuppressedInQueue.mechanism?.exceptionId)
321329
assertEquals(3, innerMostSuppressedInQueue.mechanism?.parentId)
330+
assertEquals("suppressed", innerMostSuppressedInQueue.mechanism?.type)
322331
assertNull(innerMostSuppressedInQueue.mechanism?.isExceptionGroup)
323332

324333
assertEquals("innermost", innerMostExceptionInQueue.value)
325334
assertEquals(3, innerMostExceptionInQueue.mechanism?.exceptionId)
326335
assertEquals(1, innerMostExceptionInQueue.mechanism?.parentId)
336+
assertEquals("chained", innerMostExceptionInQueue.mechanism?.type)
327337
// assertEquals(true, innerMostExceptionInQueue.mechanism?.isExceptionGroup)
328338

329-
assertEquals("suppressed", innerSuppressedInQueue.value)
339+
assertEquals("suppressed exception", innerSuppressedInQueue.value)
330340
assertEquals(2, innerSuppressedInQueue.mechanism?.exceptionId)
331341
assertEquals(1, innerSuppressedInQueue.mechanism?.parentId)
342+
assertEquals("suppressed", innerSuppressedInQueue.mechanism?.type)
332343
assertNull(innerSuppressedInQueue.mechanism?.isExceptionGroup)
333344

334345
assertEquals("inner", innerExceptionInQueue.value)
335346
assertEquals(1, innerExceptionInQueue.mechanism?.exceptionId)
336347
assertEquals(0, innerExceptionInQueue.mechanism?.parentId)
348+
assertEquals("chained", innerExceptionInQueue.mechanism?.type)
337349
// assertEquals(true, innerExceptionInQueue.mechanism?.isExceptionGroup)
338350

339351
assertEquals("outer", outerInQueue.value)
340352
assertEquals(0, outerInQueue.mechanism?.exceptionId)
341353
assertNull(outerInQueue.mechanism?.parentId)
342354
assertNull(outerInQueue.mechanism?.isExceptionGroup)
355+
assertEquals("chained", outerInQueue.mechanism?.type)
343356
}
344357

345358
@Test
@@ -351,7 +364,7 @@ class SentryExceptionFactoryTest {
351364
innerMostException.addSuppressed(innerMostSuppressed)
352365

353366
val innerException = Exception("inner", innerMostException)
354-
val innerSuppressed = Exception("suppressed")
367+
val innerSuppressed = Exception("suppressed exception")
355368
innerException.addSuppressed(innerSuppressed)
356369

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

373387
assertEquals("innermostSuppressed", innerMostSuppressedInQueue.value)
374388
assertEquals(4, innerMostSuppressedInQueue.mechanism?.exceptionId)
375389
assertEquals(3, innerMostSuppressedInQueue.mechanism?.parentId)
376390
assertNull(innerMostSuppressedInQueue.mechanism?.isExceptionGroup)
391+
assertEquals("suppressed", innerMostSuppressedInQueue.mechanism?.type)
377392

378393
assertEquals("innermost", innerMostExceptionInQueue.value)
379394
assertEquals(3, innerMostExceptionInQueue.mechanism?.exceptionId)
380395
assertEquals(1, innerMostExceptionInQueue.mechanism?.parentId)
396+
assertEquals("chained", innerMostExceptionInQueue.mechanism?.type)
381397
// assertEquals(true, innerMostExceptionInQueue.mechanism?.isExceptionGroup)
382398

383-
assertEquals("suppressed", innerSuppressedInQueue.value)
399+
assertEquals("suppressed exception", innerSuppressedInQueue.value)
384400
assertEquals(2, innerSuppressedInQueue.mechanism?.exceptionId)
385401
assertEquals(1, innerSuppressedInQueue.mechanism?.parentId)
402+
assertEquals("suppressed", innerSuppressedInQueue.mechanism?.type)
386403
assertNull(innerSuppressedInQueue.mechanism?.isExceptionGroup)
387404

388405
assertEquals("inner", innerExceptionInQueue.value)
389406
assertEquals(1, innerExceptionInQueue.mechanism?.exceptionId)
390407
assertEquals(0, innerExceptionInQueue.mechanism?.parentId)
408+
assertEquals("chained", innerExceptionInQueue.mechanism?.type)
391409
// assertEquals(true, innerExceptionInQueue.mechanism?.isExceptionGroup)
392410

393411
assertEquals("outer", outerInQueue.value)
394412
assertEquals(0, outerInQueue.mechanism?.exceptionId)
395413
assertNull(outerInQueue.mechanism?.parentId)
396414
assertNull(outerInQueue.mechanism?.isExceptionGroup)
415+
assertEquals("chained", outerInQueue.mechanism?.type)
397416
}
398417

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

0 commit comments

Comments
 (0)