Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report suppressed exceptions as exception group #3396

Merged
merged 61 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
305baf5
replace hub with scopes
adinauer Mar 27, 2024
95f5e1b
Add Scopes
adinauer Apr 2, 2024
27f2398
Introduce `IScopes` interface.
adinauer Apr 2, 2024
ce3c14f
Replace `IHub` with `IScopes` in core
adinauer Apr 2, 2024
ce615f4
Replace `IHub` with `IScopes` in android core
adinauer Apr 2, 2024
22ddc00
Replace `IHub` with `IScopes` in android integrations
adinauer Apr 2, 2024
305c217
Replace `IHub` with `IScopes` in apollo integrations
adinauer Apr 2, 2024
da927bc
Replace `IHub` with `IScopes` in okhttp integration
adinauer Apr 2, 2024
8279276
Replace `IHub` with `IScopes` in graphql integration
adinauer Apr 2, 2024
9bfc086
Replace `IHub` with `IScopes` in logging integrations
adinauer Apr 2, 2024
b998e50
Replace `IHub` with `IScopes` in more integrations
adinauer Apr 2, 2024
739827a
Replace `IHub` with `IScopes` in OTel integration
adinauer Apr 2, 2024
69f2d63
Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations
adinauer Apr 2, 2024
792d482
Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations
adinauer Apr 2, 2024
9bcbce6
Replace `IHub` with `IScopes` in samples
adinauer Apr 2, 2024
3f25a4b
Merge branch 'feat/hsm-13-replacements-in-samples' into feat/hubs-sco…
adinauer Apr 2, 2024
d6fb40a
gitscopes -> github
adinauer Apr 2, 2024
7752bcc
Replace ThreadLocal with ScopesStorage
adinauer Apr 4, 2024
1e329c5
Move client and throwable to span map to scope
adinauer Apr 4, 2024
b0d89ae
Add global scope
adinauer Apr 4, 2024
cdd414a
use global scope in Scopes
adinauer Apr 4, 2024
98da9ff
Implement pushScope popScope and withScope for Scopes
adinauer Apr 4, 2024
2d26033
Add pushIsolationScope; add fork methods to ISCope
adinauer Apr 12, 2024
bbb6700
Use separate scopes for current, isolation and global scope; rename m…
adinauer Apr 12, 2024
c714b21
Allow controlling which scope configureScope uses
adinauer Apr 12, 2024
a474402
Combine scopes
adinauer Apr 12, 2024
ae93e33
Use new API for CRONS integrations
adinauer Apr 12, 2024
b01298b
Add lifecycle helper
adinauer Apr 12, 2024
b64e688
Change spring integrations to use new API
adinauer Apr 12, 2024
d06fc50
Use new API in servlet integrations
adinauer Apr 12, 2024
f0af5c3
Use new API for kotlin coroutines and wrapers for Supplier/Callable
adinauer Apr 12, 2024
2f02001
Discussion TODOs
adinauer Apr 12, 2024
bf4a7bf
Fix breadcrumb ordering
adinauer Apr 15, 2024
62cb91a
Mark TODOS with [HSM]
adinauer Apr 15, 2024
b1630ea
Add getGlobalScope and forkedRootScopes to IScopes
adinauer Apr 16, 2024
136b9ce
Fix EventProcessor ordering on scopes
adinauer Apr 16, 2024
94d54ef
Reuse code in Scopes
adinauer Apr 16, 2024
017599d
No longer replace global scope
adinauer Apr 16, 2024
f4c2b3c
Replace hub occurrences in comments, var names etc.
adinauer Apr 16, 2024
61c9d4a
Implement ScopesTest
adinauer Apr 18, 2024
04f3892
Implement CombinedScopeViewTest
adinauer Apr 18, 2024
840c194
Fix combined contexts
adinauer Apr 19, 2024
ab1c3a6
Use combined scopes for cross platform
adinauer Apr 19, 2024
23506c5
Changes according to reviews of previous PRs
adinauer Apr 23, 2024
c9b6f8b
more
adinauer Apr 23, 2024
696a809
even more
adinauer Apr 23, 2024
847200d
isEnabled checks client instead of having a property on Scopes
adinauer Apr 24, 2024
8e86d3b
Use SentryOptions.empty
adinauer Apr 25, 2024
06db228
Remove Hub
adinauer Apr 25, 2024
8b006e9
Report suppressed exceptions as exception group
adinauer Apr 29, 2024
fe6dcf4
api dump
lbloder Apr 30, 2024
7316651
add tests for suppressed exceptions
lbloder Apr 30, 2024
3518c05
Format code
getsentry-bot Apr 30, 2024
663b337
add additinoal test
lbloder Apr 30, 2024
2cf9d8f
Format code
getsentry-bot Apr 30, 2024
bde22c4
apply suggestion
lbloder May 1, 2024
d2857be
Merge branch '8.x.x' into feat/hsm-42c-suppressed-exceptions
adinauer May 2, 2024
988dc88
Merge branch '8.x.x' into feat/hsm-42c-suppressed-exceptions
adinauer May 3, 2024
f52300b
add changelog
lbloder May 10, 2024
5897bd7
fix changelog
lbloder May 10, 2024
18ad266
Merge branch '8.x.x' into feat/hsm-42c-suppressed-exceptions
adinauer May 13, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Features

- Add support for Spring Rest Client ([#3199](https://github.com/getsentry/sentry-java/pull/3199))
- Report exceptions returned by Throwable.getSuppressed() to Sentry as exception groups ([#3396] https://github.com/getsentry/sentry-java/pull/3396)

## 7.6.0

Expand Down
9 changes: 9 additions & 0 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -4472,18 +4472,24 @@ public final class io/sentry/protocol/Mechanism : io/sentry/JsonSerializable, io
public fun <init> (Ljava/lang/Thread;)V
public fun getData ()Ljava/util/Map;
public fun getDescription ()Ljava/lang/String;
public fun getExceptionId ()Ljava/lang/Integer;
public fun getHelpLink ()Ljava/lang/String;
public fun getMeta ()Ljava/util/Map;
public fun getParentId ()Ljava/lang/Integer;
public fun getSynthetic ()Ljava/lang/Boolean;
public fun getType ()Ljava/lang/String;
public fun getUnknown ()Ljava/util/Map;
public fun isExceptionGroup ()Ljava/lang/Boolean;
public fun isHandled ()Ljava/lang/Boolean;
public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V
public fun setData (Ljava/util/Map;)V
public fun setDescription (Ljava/lang/String;)V
public fun setExceptionGroup (Ljava/lang/Boolean;)V
public fun setExceptionId (Ljava/lang/Integer;)V
public fun setHandled (Ljava/lang/Boolean;)V
public fun setHelpLink (Ljava/lang/String;)V
public fun setMeta (Ljava/util/Map;)V
public fun setParentId (Ljava/lang/Integer;)V
public fun setSynthetic (Ljava/lang/Boolean;)V
public fun setType (Ljava/lang/String;)V
public fun setUnknown (Ljava/util/Map;)V
Expand All @@ -4498,9 +4504,12 @@ public final class io/sentry/protocol/Mechanism$Deserializer : io/sentry/JsonDes
public final class io/sentry/protocol/Mechanism$JsonKeys {
public static final field DATA Ljava/lang/String;
public static final field DESCRIPTION Ljava/lang/String;
public static final field EXCEPTION_ID Ljava/lang/String;
public static final field HANDLED Ljava/lang/String;
public static final field HELP_LINK Ljava/lang/String;
public static final field IS_EXCEPTION_GROUP Ljava/lang/String;
public static final field META Ljava/lang/String;
public static final field PARENT_ID Ljava/lang/String;
public static final field SYNTHETIC Ljava/lang/String;
public static final field TYPE Ljava/lang/String;
public fun <init> ()V
Expand Down
40 changes: 34 additions & 6 deletions sentry/src/main/java/io/sentry/SentryExceptionFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -136,12 +136,20 @@ public List<SentryException> getSentryExceptions(final @NotNull Throwable throwa
@TestOnly
@NotNull
Deque<SentryException> extractExceptionQueue(final @NotNull Throwable throwable) {
final Deque<SentryException> exceptions = new ArrayDeque<>();
final Set<Throwable> circularityDetector = new HashSet<>();
return extractExceptionQueueInternal(
throwable, new AtomicInteger(-1), new HashSet<>(), new ArrayDeque<>());
}

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

Throwable currentThrowable = throwable;
int parentId = exceptionId.get();

// Stack the exceptions to send them in the reverse order
while (currentThrowable != null && circularityDetector.add(currentThrowable)) {
Expand All @@ -155,20 +163,40 @@ Deque<SentryException> extractExceptionQueue(final @NotNull Throwable throwable)
thread = exceptionMechanismThrowable.getThread();
snapshot = exceptionMechanismThrowable.isSnapshot();
} else {
exceptionMechanism = null;
exceptionMechanism = new Mechanism();
thread = Thread.currentThread();
}

final boolean includeSentryFrames =
exceptionMechanism != null && Boolean.FALSE.equals(exceptionMechanism.isHandled());
final boolean includeSentryFrames = Boolean.FALSE.equals(exceptionMechanism.isHandled());
final List<SentryStackFrame> frames =
sentryStackTraceFactory.getStackFrames(
currentThrowable.getStackTrace(), includeSentryFrames);
SentryException exception =
getSentryException(
currentThrowable, exceptionMechanism, thread.getId(), frames, snapshot);
exceptions.addFirst(exception);

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

if (exceptionId.get() >= 0) {
exceptionMechanism.setParentId(parentId);
}

final int currentExceptionId = exceptionId.incrementAndGet();
exceptionMechanism.setExceptionId(currentExceptionId);

Throwable[] suppressed = currentThrowable.getSuppressed();
if (suppressed != null && suppressed.length > 0) {
exceptionMechanism.setExceptionGroup(true);
for (Throwable suppressedThrowable : suppressed) {
extractExceptionQueueInternal(
suppressedThrowable, exceptionId, circularityDetector, exceptions);
}
}
currentThrowable = currentThrowable.getCause();
parentId = currentExceptionId;
}

return exceptions;
Expand Down
57 changes: 57 additions & 0 deletions sentry/src/main/java/io/sentry/protocol/Mechanism.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ public final class Mechanism implements JsonUnknown, JsonSerializable {
* for grouping or display purposes.
*/
private @Nullable Boolean synthetic;
/**
* Exception ID. Used. e.g. for exception groups to build a hierarchy. This is referenced as
* parent by child exceptions which for Java SDK means Throwable.getSuppressed().
*/
private @Nullable Integer exceptionId;
/** Parent exception ID. Used e.g. for exception groups to build a hierarchy. */
private @Nullable Integer parentId;
/**
* Whether this is a group of exceptions. For Java SDK this means there were suppressed
* exceptions.
*/
private @Nullable Boolean exceptionGroup;

@SuppressWarnings("unused")
private @Nullable Map<String, Object> unknown;
Expand Down Expand Up @@ -140,6 +152,30 @@ public void setSynthetic(final @Nullable Boolean synthetic) {
this.synthetic = synthetic;
}

public @Nullable Integer getExceptionId() {
return exceptionId;
}

public void setExceptionId(final @Nullable Integer exceptionId) {
this.exceptionId = exceptionId;
}

public @Nullable Integer getParentId() {
return parentId;
}

public void setParentId(final @Nullable Integer parentId) {
this.parentId = parentId;
}

public @Nullable Boolean isExceptionGroup() {
return exceptionGroup;
}

public void setExceptionGroup(final @Nullable Boolean exceptionGroup) {
this.exceptionGroup = exceptionGroup;
}

// JsonKeys

public static final class JsonKeys {
Expand All @@ -150,6 +186,9 @@ public static final class JsonKeys {
public static final String META = "meta";
public static final String DATA = "data";
public static final String SYNTHETIC = "synthetic";
public static final String EXCEPTION_ID = "exception_id";
public static final String PARENT_ID = "parent_id";
public static final String IS_EXCEPTION_GROUP = "is_exception_group";
}

// JsonUnknown
Expand Down Expand Up @@ -191,6 +230,15 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger
if (synthetic != null) {
writer.name(JsonKeys.SYNTHETIC).value(synthetic);
}
if (exceptionId != null) {
writer.name(JsonKeys.EXCEPTION_ID).value(logger, exceptionId);
}
if (parentId != null) {
writer.name(JsonKeys.PARENT_ID).value(logger, parentId);
}
if (exceptionGroup != null) {
writer.name(JsonKeys.IS_EXCEPTION_GROUP).value(exceptionGroup);
}
if (unknown != null) {
for (String key : unknown.keySet()) {
Object value = unknown.get(key);
Expand Down Expand Up @@ -238,6 +286,15 @@ public static final class Deserializer implements JsonDeserializer<Mechanism> {
case JsonKeys.SYNTHETIC:
mechanism.synthetic = reader.nextBooleanOrNull();
break;
case JsonKeys.EXCEPTION_ID:
mechanism.exceptionId = reader.nextIntegerOrNull();
break;
case JsonKeys.PARENT_ID:
mechanism.parentId = reader.nextIntegerOrNull();
break;
case JsonKeys.IS_EXCEPTION_GROUP:
mechanism.exceptionGroup = reader.nextBooleanOrNull();
break;
default:
if (unknown == null) {
unknown = new HashMap<>();
Expand Down
Loading
Loading