Skip to content

Commit e518122

Browse files
authored
Merge branch 'main' into feat/consider-port-for-is-internal-span-check
2 parents 66c7023 + 033bc88 commit e518122

File tree

11 files changed

+179
-9
lines changed

11 files changed

+179
-9
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ distributions/
2020
sentry-spring-boot-starter-jakarta/src/main/resources/META-INF/spring.factories
2121
sentry-samples/sentry-samples-spring-boot-jakarta/spy.log
2222
spy.log
23+
buildSrc/.kotlin/

CHANGELOG.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,29 @@
22

33
## Unreleased
44

5-
### Internal
5+
### Behavioural Changes
66

7-
- Also use port when checking if a request is made to Sentry DSN ([#4231](https://github.com/getsentry/sentry-java/pull/4231))
8-
- For our OpenTelemetry integration we check if a span is for a request to Sentry
9-
- We now also consider the port when performing this check
7+
- Use `java.net.URI` for parsing URLs in `UrlUtils` ([#4210](https://github.com/getsentry/sentry-java/pull/4210))
8+
- This could affect grouping for issues with messages containing URLs that fall in known corner cases that were handled incorrectly previously (e.g. email in URL path)
9+
10+
### Fixes
11+
12+
- Add support for setting in-app-includes/in-app-excludes via AndroidManifest.xml ([#4240](https://github.com/getsentry/sentry-java/pull/4240))
1013

1114
### Features
1215

1316
- The SDK now automatically propagates the trace-context to the native layer. This allows to connect errors on different layers of the application. ([#4137](https://github.com/getsentry/sentry-java/pull/4137))
17+
- Capture OpenTelemetry span events ([#3564](https://github.com/getsentry/sentry-java/pull/3564))
18+
- OpenTelemetry spans may have exceptions attached to them (`openTelemetrySpan.recordException`). We can now send those to Sentry as errors.
19+
- Set `capture-open-telemetry-events=true` in `sentry.properties` to enable it
20+
- Set `sentry.capture-open-telemetry-events=true` in Springs `application.properties` to enable it
21+
- Set `sentry.captureOpenTelemetryEvents: true` in Springs `application.yml` to enable it
1422

15-
### Behavioural Changes
23+
### Internal
1624

17-
- Use `java.net.URI` for parsing URLs in `UrlUtils` ([#4210](https://github.com/getsentry/sentry-java/pull/4210))
18-
- This could affect grouping for issues with messages containing URLs that fall in known corner cases that were handled incorrectly previously (e.g. email in URL path)
25+
- Also use port when checking if a request is made to Sentry DSN ([#4231](https://github.com/getsentry/sentry-java/pull/4231))
26+
- For our OpenTelemetry integration we check if a span is for a request to Sentry
27+
- We now also consider the port when performing this check
1928

2029
### Dependencies
2130

sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ final class ManifestMetadataReader {
107107

108108
static final String IGNORED_ERRORS = "io.sentry.ignored-errors";
109109

110+
static final String IN_APP_INCLUDES = "io.sentry.in-app-includes";
111+
112+
static final String IN_APP_EXCLUDES = "io.sentry.in-app-excludes";
113+
110114
static final String ENABLE_AUTO_TRACE_ID_GENERATION =
111115
"io.sentry.traces.enable-auto-id-generation";
112116

@@ -414,8 +418,21 @@ static void applyMetadata(
414418
.setMaskAllImages(readBool(metadata, logger, REPLAYS_MASK_ALL_IMAGES, true));
415419

416420
options.setIgnoredErrors(readList(metadata, logger, IGNORED_ERRORS));
417-
}
418421

422+
final @Nullable List<String> includes = readList(metadata, logger, IN_APP_INCLUDES);
423+
if (includes != null && !includes.isEmpty()) {
424+
for (final @NotNull String include : includes) {
425+
options.addInAppInclude(include);
426+
}
427+
}
428+
429+
final @Nullable List<String> excludes = readList(metadata, logger, IN_APP_EXCLUDES);
430+
if (excludes != null && !excludes.isEmpty()) {
431+
for (final @NotNull String exclude : excludes) {
432+
options.addInAppExclude(exclude);
433+
}
434+
}
435+
}
419436
options
420437
.getLogger()
421438
.log(SentryLevel.INFO, "Retrieving configuration from AndroidManifest.xml");

sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,4 +1447,54 @@ class ManifestMetadataReaderTest {
14471447
// Assert
14481448
assertEquals(listOf(FilterString("Some error"), FilterString("Another .*")), fixture.options.ignoredErrors)
14491449
}
1450+
1451+
@Test
1452+
fun `applyMetadata reads inAppIncludes to options and sets the value if found`() {
1453+
// Arrange
1454+
val bundle = bundleOf(ManifestMetadataReader.IN_APP_INCLUDES to "com.example.package1,com.example.package2")
1455+
val context = fixture.getContext(metaData = bundle)
1456+
1457+
// Act
1458+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1459+
1460+
// Assert
1461+
assertEquals(listOf("com.example.package1", "com.example.package2"), fixture.options.inAppIncludes)
1462+
}
1463+
1464+
@Test
1465+
fun `applyMetadata reads inAppIncludes to options and keeps empty if not found`() {
1466+
// Arrange
1467+
val context = fixture.getContext()
1468+
1469+
// Act
1470+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1471+
1472+
// Assert
1473+
assertTrue(fixture.options.inAppIncludes.isEmpty())
1474+
}
1475+
1476+
@Test
1477+
fun `applyMetadata reads inAppExcludes to options and sets the value if found`() {
1478+
// Arrange
1479+
val bundle = bundleOf(ManifestMetadataReader.IN_APP_EXCLUDES to "com.example.excluded1,com.example.excluded2")
1480+
val context = fixture.getContext(metaData = bundle)
1481+
1482+
// Act
1483+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1484+
1485+
// Assert
1486+
assertEquals(listOf("com.example.excluded1", "com.example.excluded2"), fixture.options.inAppExcludes)
1487+
}
1488+
1489+
@Test
1490+
fun `applyMetadata reads inAppExcludes to options and keeps empty if not found`() {
1491+
// Arrange
1492+
val context = fixture.getContext()
1493+
1494+
// Act
1495+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1496+
1497+
// Assert
1498+
assertTrue(fixture.options.inAppExcludes.isEmpty())
1499+
}
14501500
}

sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,25 @@
88
import io.opentelemetry.sdk.trace.ReadWriteSpan;
99
import io.opentelemetry.sdk.trace.ReadableSpan;
1010
import io.opentelemetry.sdk.trace.SpanProcessor;
11+
import io.opentelemetry.sdk.trace.data.EventData;
12+
import io.opentelemetry.sdk.trace.data.ExceptionEventData;
1113
import io.sentry.Baggage;
14+
import io.sentry.DateUtils;
1215
import io.sentry.IScopes;
1316
import io.sentry.PropagationContext;
1417
import io.sentry.ScopesAdapter;
1518
import io.sentry.Sentry;
1619
import io.sentry.SentryDate;
20+
import io.sentry.SentryEvent;
1721
import io.sentry.SentryLevel;
1822
import io.sentry.SentryLongDate;
1923
import io.sentry.SentryTraceHeader;
2024
import io.sentry.SpanId;
2125
import io.sentry.TracesSamplingDecision;
26+
import io.sentry.exception.ExceptionMechanismException;
27+
import io.sentry.protocol.Mechanism;
2228
import io.sentry.protocol.SentryId;
29+
import java.util.List;
2330
import org.jetbrains.annotations.NotNull;
2431
import org.jetbrains.annotations.Nullable;
2532

@@ -143,9 +150,46 @@ public void onEnd(final @NotNull ReadableSpan spanBeingEnded) {
143150
final @NotNull SentryDate finishDate =
144151
new SentryLongDate(spanBeingEnded.toSpanData().getEndEpochNanos());
145152
sentrySpan.updateEndDate(finishDate);
153+
154+
maybeCaptureSpanEventsAsExceptions(spanBeingEnded, sentrySpan);
146155
}
147156
}
148157

158+
private void maybeCaptureSpanEventsAsExceptions(
159+
final @NotNull ReadableSpan spanBeingEnded, final @NotNull IOtelSpanWrapper sentrySpan) {
160+
final @NotNull IScopes spanScopes = sentrySpan.getScopes();
161+
if (spanScopes.getOptions().isCaptureOpenTelemetryEvents()) {
162+
final @NotNull List<EventData> events = spanBeingEnded.toSpanData().getEvents();
163+
for (EventData event : events) {
164+
if (event instanceof ExceptionEventData) {
165+
final @NotNull ExceptionEventData exceptionEvent = (ExceptionEventData) event;
166+
captureException(spanScopes, exceptionEvent, sentrySpan);
167+
}
168+
}
169+
}
170+
}
171+
172+
private void captureException(
173+
final @NotNull IScopes scopes,
174+
final @NotNull ExceptionEventData exceptionEvent,
175+
final @NotNull IOtelSpanWrapper sentrySpan) {
176+
final @NotNull Throwable exception = exceptionEvent.getException();
177+
final Mechanism mechanism = new Mechanism();
178+
mechanism.setType("OpenTelemetrySpanEvent");
179+
mechanism.setHandled(true);
180+
// This is potentially the wrong Thread as it's the current thread meaning the thread where
181+
// the span is being ended on. This may not match the thread where the exception occurred.
182+
final Throwable mechanismException =
183+
new ExceptionMechanismException(mechanism, exception, Thread.currentThread());
184+
185+
final SentryEvent event = new SentryEvent(mechanismException);
186+
event.setTimestamp(DateUtils.nanosToDate(exceptionEvent.getEpochNanos()));
187+
event.setLevel(SentryLevel.ERROR);
188+
event.getContexts().setTrace(sentrySpan.getSpanContext());
189+
190+
scopes.captureEvent(event);
191+
}
192+
149193
@Override
150194
public boolean isEndRequired() {
151195
return true;

sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ class SentryAutoConfigurationTest {
182182
"sentry.spotlight-connection-url=http://local.sentry.io:1234",
183183
"sentry.force-init=true",
184184
"sentry.global-hub-mode=true",
185+
"sentry.capture-open-telemetry-events=true",
185186
"sentry.cron.default-checkin-margin=10",
186187
"sentry.cron.default-max-runtime=30",
187188
"sentry.cron.default-timezone=America/New_York",
@@ -222,6 +223,7 @@ class SentryAutoConfigurationTest {
222223
assertThat(options.isEnableBackpressureHandling).isEqualTo(false)
223224
assertThat(options.isForceInit).isEqualTo(true)
224225
assertThat(options.isGlobalHubMode).isEqualTo(true)
226+
assertThat(options.isCaptureOpenTelemetryEvents).isEqualTo(true)
225227
assertThat(options.isEnableSpotlight).isEqualTo(true)
226228
assertThat(options.spotlightConnectionUrl).isEqualTo("http://local.sentry.io:1234")
227229
assertThat(options.cron).isNotNull

sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ class SentryAutoConfigurationTest {
181181
"sentry.spotlight-connection-url=http://local.sentry.io:1234",
182182
"sentry.force-init=true",
183183
"sentry.global-hub-mode=true",
184+
"sentry.capture-open-telemetry-events=true",
184185
"sentry.cron.default-checkin-margin=10",
185186
"sentry.cron.default-max-runtime=30",
186187
"sentry.cron.default-timezone=America/New_York",
@@ -221,6 +222,7 @@ class SentryAutoConfigurationTest {
221222
assertThat(options.isEnableBackpressureHandling).isEqualTo(false)
222223
assertThat(options.isForceInit).isEqualTo(true)
223224
assertThat(options.isGlobalHubMode).isEqualTo(true)
225+
assertThat(options.isCaptureOpenTelemetryEvents).isEqualTo(true)
224226
assertThat(options.isEnableSpotlight).isEqualTo(true)
225227
assertThat(options.spotlightConnectionUrl).isEqualTo("http://local.sentry.io:1234")
226228
assertThat(options.cron).isNotNull

sentry/api/sentry.api

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ public final class io/sentry/ExternalOptions {
479479
public fun getTags ()Ljava/util/Map;
480480
public fun getTracePropagationTargets ()Ljava/util/List;
481481
public fun getTracesSampleRate ()Ljava/lang/Double;
482+
public fun isCaptureOpenTelemetryEvents ()Ljava/lang/Boolean;
482483
public fun isEnableBackpressureHandling ()Ljava/lang/Boolean;
483484
public fun isEnablePrettySerializationOutput ()Ljava/lang/Boolean;
484485
public fun isEnableSpotlight ()Ljava/lang/Boolean;
@@ -487,6 +488,7 @@ public final class io/sentry/ExternalOptions {
487488
public fun isGlobalHubMode ()Ljava/lang/Boolean;
488489
public fun isSendDefaultPii ()Ljava/lang/Boolean;
489490
public fun isSendModules ()Ljava/lang/Boolean;
491+
public fun setCaptureOpenTelemetryEvents (Ljava/lang/Boolean;)V
490492
public fun setCron (Lio/sentry/SentryOptions$Cron;)V
491493
public fun setDebug (Ljava/lang/Boolean;)V
492494
public fun setDist (Ljava/lang/String;)V
@@ -2930,6 +2932,7 @@ public class io/sentry/SentryOptions {
29302932
public fun isAttachServerName ()Z
29312933
public fun isAttachStacktrace ()Z
29322934
public fun isAttachThreads ()Z
2935+
public fun isCaptureOpenTelemetryEvents ()Z
29332936
public fun isDebug ()Z
29342937
public fun isEnableAppStartProfiling ()Z
29352938
public fun isEnableAutoSessionTracking ()Z
@@ -2967,6 +2970,7 @@ public class io/sentry/SentryOptions {
29672970
public fun setBeforeSendReplay (Lio/sentry/SentryOptions$BeforeSendReplayCallback;)V
29682971
public fun setBeforeSendTransaction (Lio/sentry/SentryOptions$BeforeSendTransactionCallback;)V
29692972
public fun setCacheDirPath (Ljava/lang/String;)V
2973+
public fun setCaptureOpenTelemetryEvents (Z)V
29702974
public fun setConnectionStatusProvider (Lio/sentry/IConnectionStatusProvider;)V
29712975
public fun setConnectionTimeoutMillis (I)V
29722976
public fun setCron (Lio/sentry/SentryOptions$Cron;)V

sentry/src/main/java/io/sentry/ExternalOptions.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public final class ExternalOptions {
5353
private @Nullable Boolean enableBackpressureHandling;
5454
private @Nullable Boolean globalHubMode;
5555
private @Nullable Boolean forceInit;
56+
private @Nullable Boolean captureOpenTelemetryEvents;
5657

5758
private @Nullable SentryOptions.Cron cron;
5859

@@ -146,6 +147,9 @@ public final class ExternalOptions {
146147

147148
options.setGlobalHubMode(propertiesProvider.getBooleanProperty("global-hub-mode"));
148149

150+
options.setCaptureOpenTelemetryEvents(
151+
propertiesProvider.getBooleanProperty("capture-open-telemetry-events"));
152+
149153
for (final String ignoredExceptionType :
150154
propertiesProvider.getList("ignored-exceptions-for-type")) {
151155
try {
@@ -504,4 +508,14 @@ public void setEnableSpotlight(final @Nullable Boolean enableSpotlight) {
504508
public void setSpotlightConnectionUrl(final @Nullable String spotlightConnectionUrl) {
505509
this.spotlightConnectionUrl = spotlightConnectionUrl;
506510
}
511+
512+
@ApiStatus.Experimental
513+
public void setCaptureOpenTelemetryEvents(final @Nullable Boolean captureOpenTelemetryEvents) {
514+
this.captureOpenTelemetryEvents = captureOpenTelemetryEvents;
515+
}
516+
517+
@ApiStatus.Experimental
518+
public @Nullable Boolean isCaptureOpenTelemetryEvents() {
519+
return captureOpenTelemetryEvents;
520+
}
507521
}

sentry/src/main/java/io/sentry/SentryOptions.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,7 @@ public class SentryOptions {
530530

531531
private @NotNull SentryReplayOptions sessionReplay;
532532

533+
@ApiStatus.Experimental private boolean captureOpenTelemetryEvents = false;
533534
/**
534535
* Adds an event processor
535536
*
@@ -2634,6 +2635,16 @@ public void setSessionReplay(final @NotNull SentryReplayOptions sessionReplayOpt
26342635
this.sessionReplay = sessionReplayOptions;
26352636
}
26362637

2638+
@ApiStatus.Experimental
2639+
public void setCaptureOpenTelemetryEvents(final boolean captureOpenTelemetryEvents) {
2640+
this.captureOpenTelemetryEvents = captureOpenTelemetryEvents;
2641+
}
2642+
2643+
@ApiStatus.Experimental
2644+
public boolean isCaptureOpenTelemetryEvents() {
2645+
return captureOpenTelemetryEvents;
2646+
}
2647+
26372648
/**
26382649
* Load the lazy fields. Useful to load in the background, so that results are already cached. DO
26392650
* NOT CALL THIS METHOD ON THE MAIN THREAD.
@@ -2927,7 +2938,9 @@ public void merge(final @NotNull ExternalOptions options) {
29272938
if (options.isSendDefaultPii() != null) {
29282939
setSendDefaultPii(options.isSendDefaultPii());
29292940
}
2930-
2941+
if (options.isCaptureOpenTelemetryEvents() != null) {
2942+
setCaptureOpenTelemetryEvents(options.isCaptureOpenTelemetryEvents());
2943+
}
29312944
if (options.isEnableSpotlight() != null) {
29322945
setEnableSpotlight(options.isEnableSpotlight());
29332946
}

0 commit comments

Comments
 (0)