Skip to content

Commit

Permalink
Merge branch 'main' into feat/jul-ctor-external-options
Browse files Browse the repository at this point in the history
  • Loading branch information
adinauer authored Feb 26, 2025
2 parents 3853958 + 7d08e30 commit 05de50f
Show file tree
Hide file tree
Showing 18 changed files with 611 additions and 18 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@
### Fixes

- `SentryOptions.setTracePropagationTargets` is no longer marked internal ([#4170](https://github.com/getsentry/sentry-java/pull/4170))
- Session Replay: Fix crash when a navigation breadcrumb does not have "to" destination ([#4185](https://github.com/getsentry/sentry-java/pull/4185))
- Session Replay: Cap video segment duration to maximum 5 minutes to prevent endless video encoding in background ([#4185](https://github.com/getsentry/sentry-java/pull/4185))
- Check `tracePropagationTargets` in OpenTelemetry propagator ([#4191](https://github.com/getsentry/sentry-java/pull/4191))
- If a URL can be retrieved from OpenTelemetry span attributes, we check it against `tracePropagationTargets` before attaching `sentry-trace` and `baggage` headers to outgoing requests
- If no URL can be retrieved we always attach the headers
- Fix `ignoredErrors`, `ignoredTransactions` and `ignoredCheckIns` being unset by external options like `sentry.properties` or ENV vars ([#4207](https://github.com/getsentry/sentry-java/pull/4207))
- Whenever parsing of external options was enabled (`enableExternalConfiguration`), which is the default for many integrations, the values set on `SentryOptions` passed to `Sentry.init` would be lost
- Even if the value was not set in any external configuration it would still be set to an empty list

### Behavioural Changes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ internal interface CaptureStrategy {
companion object {
private const val BREADCRUMB_START_OFFSET = 100L

// 5 minutes, otherwise relay will just drop it. Can prevent the case where the device
// time is wrong and the segment is too long.
private const val MAX_SEGMENT_DURATION = 1000L * 60 * 5

fun createSegment(
scopes: IScopes?,
options: SentryOptions,
Expand All @@ -76,7 +80,7 @@ internal interface CaptureStrategy {
events: Deque<RRWebEvent>
): ReplaySegment {
val generatedVideo = cache?.createVideoOf(
duration,
minOf(duration, MAX_SEGMENT_DURATION),
currentSegmentTimestamp.time,
segmentId,
height,
Expand Down Expand Up @@ -179,7 +183,9 @@ internal interface CaptureStrategy {
recordingPayload += rrwebEvent

// fill in the urls array from navigation breadcrumbs
if ((rrwebEvent as? RRWebBreadcrumbEvent)?.category == "navigation") {
if ((rrwebEvent as? RRWebBreadcrumbEvent)?.category == "navigation" &&
rrwebEvent.data?.getOrElse("to", { null }) is String
) {
urls.add(rrwebEvent.data!!["to"] as String)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,30 @@ class SessionCaptureStrategyTest {
)
}

@Test
fun `does not throw when navigation destination is not a String`() {
val now =
System.currentTimeMillis() + (fixture.options.sessionReplay.sessionSegmentDuration * 5)
val strategy = fixture.getSut(dateProvider = { now })
strategy.start(fixture.recorderConfig)

fixture.scope.addBreadcrumb(Breadcrumb().apply { category = "navigation" })

strategy.onScreenshotRecorded(mock<Bitmap>()) {}

verify(fixture.scopes).captureReplay(
check {
assertNull(it.urls?.firstOrNull())
},
check {
val breadcrumbEvents =
it.replayRecording?.payload?.filterIsInstance<RRWebBreadcrumbEvent>()
assertEquals("navigation", breadcrumbEvents?.first()?.category)
assertNull(breadcrumbEvents?.first()?.data?.get("to"))
}
)
}

@Test
fun `sets screen from scope as replay url`() {
fixture.scope.screen = "MainActivity"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
public abstract interface class io/sentry/opentelemetry/IOtelSpanWrapper : io/sentry/ISpan {
public abstract fun getData ()Ljava/util/Map;
public abstract fun getMeasurements ()Ljava/util/Map;
public abstract fun getOpenTelemetrySpanAttributes ()Lio/opentelemetry/api/common/Attributes;
public abstract fun getScopes ()Lio/sentry/IScopes;
public abstract fun getTags ()Ljava/util/Map;
public abstract fun getTraceId ()Lio/sentry/protocol/SentryId;
Expand Down Expand Up @@ -51,6 +52,7 @@ public final class io/sentry/opentelemetry/OtelStrongRefSpanWrapper : io/sentry/
public fun getDescription ()Ljava/lang/String;
public fun getFinishDate ()Lio/sentry/SentryDate;
public fun getMeasurements ()Ljava/util/Map;
public fun getOpenTelemetrySpanAttributes ()Lio/opentelemetry/api/common/Attributes;
public fun getOperation ()Ljava/lang/String;
public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision;
public fun getScopes ()Lio/sentry/IScopes;
Expand Down Expand Up @@ -177,6 +179,7 @@ public final class io/sentry/opentelemetry/SentryOtelThreadLocalStorage : io/ope
}

public final class io/sentry/opentelemetry/SentryWeakSpanStorage {
public fun clear ()V
public static fun getInstance ()Lio/sentry/opentelemetry/SentryWeakSpanStorage;
public fun getSentrySpan (Lio/opentelemetry/api/trace/SpanContext;)Lio/sentry/opentelemetry/IOtelSpanWrapper;
public fun storeSentrySpan (Lio/opentelemetry/api/trace/SpanContext;Lio/sentry/opentelemetry/IOtelSpanWrapper;)V
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.opentelemetry;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.context.Context;
import io.sentry.IScopes;
import io.sentry.ISpan;
Expand Down Expand Up @@ -47,4 +48,8 @@ public interface IOtelSpanWrapper extends ISpan {

@NotNull
Context storeInContext(Context context);

@ApiStatus.Internal
@Nullable
Attributes getOpenTelemetrySpanAttributes();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.opentelemetry;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.sentry.BaggageHeader;
Expand Down Expand Up @@ -303,4 +304,10 @@ public void setContext(@NotNull String key, @NotNull Object context) {
public @NotNull ISentryLifecycleToken makeCurrent() {
return delegate.makeCurrent();
}

@ApiStatus.Internal
@Override
public @Nullable Attributes getOpenTelemetrySpanAttributes() {
return delegate.getOpenTelemetrySpanAttributes();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;

/**
* Weakly references wrappers for OpenTelemetry spans meaning they'll be cleaned up when the
Expand Down Expand Up @@ -44,4 +45,9 @@ public void storeSentrySpan(
final @NotNull SpanContext otelSpan, final @NotNull IOtelSpanWrapper sentrySpan) {
this.sentrySpans.put(otelSpan, sentrySpan);
}

@TestOnly
public void clear() {
sentrySpans.clear();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
public final class io/sentry/opentelemetry/OpenTelemetryAttributesExtractor {
public fun <init> ()V
public fun extract (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/ISpan;Lio/sentry/IScope;)V
public fun extractUrl (Lio/opentelemetry/api/common/Attributes;)Ljava/lang/String;
}

public final class io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor : io/sentry/EventProcessor {
Expand Down Expand Up @@ -60,6 +61,7 @@ public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/opentelem
public fun getDescription ()Ljava/lang/String;
public fun getFinishDate ()Lio/sentry/SentryDate;
public fun getMeasurements ()Ljava/util/Map;
public fun getOpenTelemetrySpanAttributes ()Lio/opentelemetry/api/common/Attributes;
public fun getOperation ()Ljava/lang/String;
public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision;
public fun getScopes ()Lio/sentry/IScopes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public void extract(
addRequestAttributesToScope(attributes, scope);
}

private void addRequestAttributesToScope(Attributes attributes, IScope scope) {
private void addRequestAttributesToScope(
final @NotNull Attributes attributes, final @NotNull IScope scope) {
if (scope.getRequest() == null) {
scope.setRequest(new Request());
}
Expand All @@ -36,20 +37,13 @@ private void addRequestAttributesToScope(Attributes attributes, IScope scope) {
}

if (request.getUrl() == null) {
final @Nullable String urlFull = attributes.get(UrlAttributes.URL_FULL);
if (urlFull != null) {
final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(urlFull);
final @Nullable String url = extractUrl(attributes);
if (url != null) {
final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(url);
urlDetails.applyToRequest(request);
}
}

if (request.getUrl() == null) {
final String urlString = buildUrlString(attributes);
if (!urlString.isEmpty()) {
request.setUrl(urlString);
}
}

if (request.getQueryString() == null) {
final @Nullable String query = attributes.get(UrlAttributes.URL_QUERY);
if (query != null) {
Expand All @@ -59,6 +53,20 @@ private void addRequestAttributesToScope(Attributes attributes, IScope scope) {
}
}

public @Nullable String extractUrl(final @NotNull Attributes attributes) {
final @Nullable String urlFull = attributes.get(UrlAttributes.URL_FULL);
if (urlFull != null) {
return urlFull;
}

final String urlString = buildUrlString(attributes);
if (!urlString.isEmpty()) {
return urlString;
}

return null;
}

private @NotNull String buildUrlString(final @NotNull Attributes attributes) {
final @Nullable String scheme = attributes.get(UrlAttributes.URL_SCHEME);
final @Nullable String serverAddress = attributes.get(ServerAttributes.SERVER_ADDRESS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static io.sentry.opentelemetry.SentryOtelKeys.SENTRY_SCOPES_KEY;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.TraceFlags;
Expand Down Expand Up @@ -32,6 +33,8 @@ public final class OtelSentryPropagator implements TextMapPropagator {
Arrays.asList(SentryTraceHeader.SENTRY_TRACE_HEADER, BaggageHeader.BAGGAGE_HEADER);
private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance();
private final @NotNull IScopes scopes;
private final @NotNull OpenTelemetryAttributesExtractor attributesExtractor =
new OpenTelemetryAttributesExtractor();

public OtelSentryPropagator() {
this(ScopesAdapter.getInstance());
Expand Down Expand Up @@ -73,9 +76,11 @@ public <C> void inject(final Context context, final C carrier, final TextMapSett
return;
}

// TODO can we use traceIfAllowed? do we have the URL here? need to access span attrs
final @Nullable String url = getUrl(sentrySpan);
final @Nullable TracingUtils.TracingHeaders tracingHeaders =
TracingUtils.trace(scopes, Collections.emptyList(), sentrySpan);
url == null
? TracingUtils.trace(scopes, Collections.emptyList(), sentrySpan)
: TracingUtils.traceIfAllowed(scopes, url, Collections.emptyList(), sentrySpan);

if (tracingHeaders != null) {
final @NotNull SentryTraceHeader sentryTraceHeader = tracingHeaders.getSentryTraceHeader();
Expand All @@ -87,6 +92,14 @@ public <C> void inject(final Context context, final C carrier, final TextMapSett
}
}

private @Nullable String getUrl(final @NotNull IOtelSpanWrapper sentrySpan) {
final @Nullable Attributes attributes = sentrySpan.getOpenTelemetrySpanAttributes();
if (attributes == null) {
return null;
}
return attributesExtractor.extractUrl(attributes);
}

@Override
public <C> Context extract(
final Context context, final C carrier, final TextMapGetter<C> getter) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.opentelemetry;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
Expand Down Expand Up @@ -198,6 +199,16 @@ public OtelSpanWrapper(
return span.get();
}

@ApiStatus.Internal
@Override
public @Nullable Attributes getOpenTelemetrySpanAttributes() {
final @Nullable ReadWriteSpan readWriteSpan = span.get();
if (readWriteSpan != null) {
return readWriteSpan.getAttributes();
}
return null;
}

@Override
public @Nullable TraceContext traceContext() {
if (scopes.getOptions().isTraceSampling()) {
Expand Down
Loading

0 comments on commit 05de50f

Please sign in to comment.