Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ public static DatabaseDetails parse(final @Nullable String databaseConnectionUrl
String pathWithoutProperties = StringUtils.substringBefore(path, ";");
return new DatabaseDetails(dbSystem, pathWithoutProperties);
} catch (Throwable t) {
System.out.println(t.getMessage());
// ignore
}
return new DatabaseDetails(dbSystem, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ dependencies {
api(libs.otel.extension.autoconfigure)
api(libs.otel.exporter.otlp)
compileOnly(libs.otel.extension.autoconfigure.spi)
// compileOnly(libs.otel.semconv)
implementation(libs.otel.semconv)
// compileOnly(libs.otel.semconv.incubating)

compileOnly(libs.jetbrains.annotations)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static io.sentry.SentryTraceHeader.SENTRY_TRACE_HEADER;

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 All @@ -11,14 +12,20 @@
import io.opentelemetry.context.propagation.TextMapGetter;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.context.propagation.TextMapSetter;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.semconv.ServerAttributes;
import io.opentelemetry.semconv.UrlAttributes;
import io.sentry.Baggage;
import io.sentry.BaggageHeader;
import io.sentry.IScopes;
import io.sentry.ScopesAdapter;
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.SentryTraceHeader;
import io.sentry.exception.InvalidSentryTraceHeaderException;
import io.sentry.util.PropagationTargetsUtils;
import io.sentry.util.TracingUtils;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
Expand Down Expand Up @@ -61,6 +68,10 @@ public <C> void inject(final Context context, final C carrier, final TextMapSett
return;
}

if (!shouldInjectTracingHeaders(otelSpan)) {
return;
}

setter.set(
carrier,
SENTRY_TRACE_HEADER,
Expand All @@ -76,6 +87,53 @@ public <C> void inject(final Context context, final C carrier, final TextMapSett
}
}

private boolean shouldInjectTracingHeaders(final @NotNull Span otelSpan) {
final @NotNull SentryOptions options = scopes.getOptions();
final @Nullable String url = extractUrl(otelSpan, options);

if (url != null) {
return PropagationTargetsUtils.contain(options.getTracePropagationTargets(), url);
}

return options.getTracePropagationTargets().contains(SentryOptions.DEFAULT_PROPAGATION_TARGETS);
}

private @Nullable String extractUrl(
final @NotNull Span otelSpan, final @NotNull SentryOptions options) {
if (!(otelSpan instanceof ReadableSpan)) {
return null;
}

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

final @Nullable String scheme = attributes.get(UrlAttributes.URL_SCHEME);
final @Nullable String serverAddress = attributes.get(ServerAttributes.SERVER_ADDRESS);
final @Nullable Long serverPort = attributes.get(ServerAttributes.SERVER_PORT);
final @Nullable String path = attributes.get(UrlAttributes.URL_PATH);

if (scheme == null || serverAddress == null) {
return null;
}

try {
final @NotNull String pathToUse = path == null ? "" : path;
if (serverPort == null) {
return new URL(scheme, serverAddress, pathToUse).toString();
} else {
return new URL(scheme, serverAddress, serverPort.intValue(), pathToUse).toString();
}
} catch (Throwable t) {
options
.getLogger()
.log(SentryLevel.WARNING, "Unable to combine URL span attributes into one.", t);
return null;
}
}

@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
Expand Up @@ -7,6 +7,7 @@ import io.opentelemetry.api.trace.TraceState
import io.opentelemetry.context.Context
import io.opentelemetry.context.propagation.TextMapGetter
import io.opentelemetry.context.propagation.TextMapSetter
import io.opentelemetry.sdk.trace.SdkTracerProvider
import io.sentry.Baggage
import io.sentry.Sentry
import kotlin.test.AfterTest
Expand Down Expand Up @@ -171,6 +172,97 @@ class OpenTelemetryOtlpPropagatorTest {
)
}

@Test
fun `injects headers if URL in span attributes matches tracePropagationTargets`() {
Sentry.init { options ->
options.dsn = "https://key@sentry.io/proj"
options.setTracePropagationTargets(listOf("sentry.io"))
}
val propagator = OpenTelemetryOtlpPropagator()
val carrier = mutableMapOf<String, String>()
val tracerProvider = SdkTracerProvider.builder().build()
val otelSpan =
tracerProvider
.get("test")
.spanBuilder("test")
.setAttribute("url.full", "https://sentry.io/api/0/")
.startSpan()
val baggage =
Baggage.fromHeader(
"sentry-environment=production,sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rand=0.456789,sentry-sample_rate=0.5,sentry-sampled=true,sentry-trace_id=df71f5972f754b4c85af13ff5c07017d"
)

try {
val context =
Context.root().with(otelSpan).with(OpenTelemetryOtlpPropagator.SENTRY_BAGGAGE_KEY, baggage)

propagator.inject(context, carrier, MapSetter())
} finally {
otelSpan.end()
tracerProvider.shutdown()
}

assertEquals(
"${otelSpan.spanContext.traceId}-${otelSpan.spanContext.spanId}-1",
carrier["sentry-trace"],
)
assertEquals(
"sentry-environment=production,sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rand=0.456789,sentry-sample_rate=0.5,sentry-sampled=true,sentry-trace_id=df71f5972f754b4c85af13ff5c07017d",
carrier["baggage"],
)
}

@Test
fun `does not inject headers if URL in span attributes does not match tracePropagationTargets`() {
Sentry.init { options ->
options.dsn = "https://key@sentry.io/proj"
options.setTracePropagationTargets(listOf("github.com"))
}
val propagator = OpenTelemetryOtlpPropagator()
val carrier = mutableMapOf<String, String>()
val tracerProvider = SdkTracerProvider.builder().build()
val otelSpan =
tracerProvider
.get("test")
.spanBuilder("test")
.setAttribute("url.full", "https://sentry.io/api/0/")
.startSpan()

try {
propagator.inject(Context.root().with(otelSpan), carrier, MapSetter())
} finally {
otelSpan.end()
tracerProvider.shutdown()
}

assertNull(carrier["sentry-trace"])
assertNull(carrier["baggage"])
}

@Test
fun `does not inject headers if tracePropagationTargets is restricted and URL is unavailable`() {
Sentry.init { options ->
options.dsn = "https://key@sentry.io/proj"
options.setTracePropagationTargets(listOf("sentry.io"))
}
val propagator = OpenTelemetryOtlpPropagator()
val carrier = mutableMapOf<String, String>()

val otelSpanContext =
SpanContext.create(
"f9118105af4a2d42b4124532cd1065ff",
"424cffc8f94feeee",
TraceFlags.getSampled(),
TraceState.getDefault(),
)
val otelSpan = Span.wrap(otelSpanContext)

propagator.inject(Context.root().with(otelSpan), carrier, MapSetter())

assertNull(carrier["sentry-trace"])
assertNull(carrier["baggage"])
}

@Test
fun `does not inject headers when no span in context`() {
val propagator = OpenTelemetryOtlpPropagator()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import io.sentry.util.Objects;
import io.sentry.util.SpanUtils;
import io.sentry.util.TracingUtils;
import io.sentry.util.UrlUtils;
import java.util.Locale;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -45,9 +46,11 @@ public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) {
final @NotNull SpanOptions spanOptions = new SpanOptions();
spanOptions.setOrigin(TRACE_ORIGIN);
final ISpan span = activeSpan.startChild("http.client", null, spanOptions);
final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(request.url().toString());
final @NotNull String method = request.method().name();
span.setDescription(method + " " + request.url());
span.setDescription(method + " " + urlDetails.getUrlOrFallback());
span.setData(SpanDataConvention.HTTP_METHOD_KEY, method.toUpperCase(Locale.ROOT));
urlDetails.applyToSpan(span);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to own issue, release in major


final @NotNull ClientRequest modifiedRequest = maybeAddTracingHeaders(request, span);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,8 @@ class SentrySpringIntegrationTest {
assertThat(transaction.spans).hasSize(1)
val span = transaction.spans.first()
assertThat(span.op).isEqualTo("http.client")
assertThat(span.description).isEqualTo("GET http://localhost:$port/hello")
assertThat(span.description)
.isEqualTo("GET http://[Filtered]:[Filtered]@localhost:$port/hello")
assertThat(span.data?.get(SpanDataConvention.HTTP_STATUS_CODE_KEY)).isEqualTo(200)
assertThat(span.status).isEqualTo(SpanStatus.OK)
},
Expand Down Expand Up @@ -519,7 +520,7 @@ class HelloController(private val webClient: WebClient, private val env: Environ
fun webClient(): String? {
return webClient
.get()
.uri("http://localhost:${env.getProperty("local.server.port")}/hello")
.uri("http://user:password@localhost:${env.getProperty("local.server.port")}/hello")
.retrieve()
.bodyToMono(String::class.java)
.block()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import io.sentry.util.Objects;
import io.sentry.util.SpanUtils;
import io.sentry.util.TracingUtils;
import io.sentry.util.UrlUtils;
import java.util.Locale;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -45,9 +46,11 @@ public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) {
final @NotNull SpanOptions spanOptions = new SpanOptions();
spanOptions.setOrigin(TRACE_ORIGIN);
final ISpan span = activeSpan.startChild("http.client", null, spanOptions);
final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(request.url().toString());
final @NotNull String method = request.method().name();
span.setDescription(method + " " + request.url());
span.setDescription(method + " " + urlDetails.getUrlOrFallback());
span.setData(SpanDataConvention.HTTP_METHOD_KEY, method.toUpperCase(Locale.ROOT));
urlDetails.applyToSpan(span);

final @NotNull ClientRequest modifiedRequest = maybeAddTracingHeaders(request, span);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,8 @@ class SentrySpringIntegrationTest {
assertThat(transaction.spans).hasSize(1)
val span = transaction.spans.first()
assertThat(span.op).isEqualTo("http.client")
assertThat(span.description).isEqualTo("GET http://localhost:$port/hello")
assertThat(span.description)
.isEqualTo("GET http://[Filtered]:[Filtered]@localhost:$port/hello")
assertThat(span.data?.get(SpanDataConvention.HTTP_STATUS_CODE_KEY)).isEqualTo(200)
assertThat(span.status).isEqualTo(SpanStatus.OK)
},
Expand Down Expand Up @@ -517,7 +518,7 @@ class HelloController(private val webClient: WebClient, private val env: Environ
fun webClient(): String? {
return webClient
.get()
.uri("http://localhost:${env.getProperty("local.server.port")}/hello")
.uri("http://user:password@localhost:${env.getProperty("local.server.port")}/hello")
.retrieve()
.bodyToMono(String::class.java)
.block()
Expand Down
Loading