Skip to content

Commit 0c476a3

Browse files
committed
extract target url to match against tracePropagationTargets
1 parent f9a6c50 commit 0c476a3

3 files changed

Lines changed: 151 additions & 1 deletion

File tree

sentry-opentelemetry/sentry-opentelemetry-otlp/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ dependencies {
2121
api(libs.otel.extension.autoconfigure)
2222
api(libs.otel.exporter.otlp)
2323
compileOnly(libs.otel.extension.autoconfigure.spi)
24-
// compileOnly(libs.otel.semconv)
24+
implementation(libs.otel.semconv)
2525
// compileOnly(libs.otel.semconv.incubating)
2626

2727
compileOnly(libs.jetbrains.annotations)

sentry-opentelemetry/sentry-opentelemetry-otlp/src/main/java/io/sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static io.sentry.SentryTraceHeader.SENTRY_TRACE_HEADER;
44

5+
import io.opentelemetry.api.common.Attributes;
56
import io.opentelemetry.api.trace.Span;
67
import io.opentelemetry.api.trace.SpanContext;
78
import io.opentelemetry.api.trace.TraceFlags;
@@ -11,14 +12,20 @@
1112
import io.opentelemetry.context.propagation.TextMapGetter;
1213
import io.opentelemetry.context.propagation.TextMapPropagator;
1314
import io.opentelemetry.context.propagation.TextMapSetter;
15+
import io.opentelemetry.sdk.trace.ReadableSpan;
16+
import io.opentelemetry.semconv.ServerAttributes;
17+
import io.opentelemetry.semconv.UrlAttributes;
1418
import io.sentry.Baggage;
1519
import io.sentry.BaggageHeader;
1620
import io.sentry.IScopes;
1721
import io.sentry.ScopesAdapter;
1822
import io.sentry.SentryLevel;
23+
import io.sentry.SentryOptions;
1924
import io.sentry.SentryTraceHeader;
2025
import io.sentry.exception.InvalidSentryTraceHeaderException;
26+
import io.sentry.util.PropagationTargetsUtils;
2127
import io.sentry.util.TracingUtils;
28+
import java.net.URL;
2229
import java.util.Arrays;
2330
import java.util.Collection;
2431
import java.util.List;
@@ -61,6 +68,10 @@ public <C> void inject(final Context context, final C carrier, final TextMapSett
6168
return;
6269
}
6370

71+
if (!shouldInjectTracingHeaders(otelSpan)) {
72+
return;
73+
}
74+
6475
setter.set(
6576
carrier,
6677
SENTRY_TRACE_HEADER,
@@ -76,6 +87,53 @@ public <C> void inject(final Context context, final C carrier, final TextMapSett
7687
}
7788
}
7889

90+
private boolean shouldInjectTracingHeaders(final @NotNull Span otelSpan) {
91+
final @NotNull SentryOptions options = scopes.getOptions();
92+
final @Nullable String url = extractUrl(otelSpan, options);
93+
94+
if (url != null) {
95+
return PropagationTargetsUtils.contain(options.getTracePropagationTargets(), url);
96+
}
97+
98+
return options.getTracePropagationTargets().contains(SentryOptions.DEFAULT_PROPAGATION_TARGETS);
99+
}
100+
101+
private @Nullable String extractUrl(
102+
final @NotNull Span otelSpan, final @NotNull SentryOptions options) {
103+
if (!(otelSpan instanceof ReadableSpan)) {
104+
return null;
105+
}
106+
107+
final @NotNull Attributes attributes = ((ReadableSpan) otelSpan).getAttributes();
108+
final @Nullable String urlFull = attributes.get(UrlAttributes.URL_FULL);
109+
if (urlFull != null) {
110+
return urlFull;
111+
}
112+
113+
final @Nullable String scheme = attributes.get(UrlAttributes.URL_SCHEME);
114+
final @Nullable String serverAddress = attributes.get(ServerAttributes.SERVER_ADDRESS);
115+
final @Nullable Long serverPort = attributes.get(ServerAttributes.SERVER_PORT);
116+
final @Nullable String path = attributes.get(UrlAttributes.URL_PATH);
117+
118+
if (scheme == null || serverAddress == null) {
119+
return null;
120+
}
121+
122+
try {
123+
final @NotNull String pathToUse = path == null ? "" : path;
124+
if (serverPort == null) {
125+
return new URL(scheme, serverAddress, pathToUse).toString();
126+
} else {
127+
return new URL(scheme, serverAddress, serverPort.intValue(), pathToUse).toString();
128+
}
129+
} catch (Throwable t) {
130+
options
131+
.getLogger()
132+
.log(SentryLevel.WARNING, "Unable to combine URL span attributes into one.", t);
133+
return null;
134+
}
135+
}
136+
79137
@Override
80138
public <C> Context extract(
81139
final Context context, final C carrier, final TextMapGetter<C> getter) {

sentry-opentelemetry/sentry-opentelemetry-otlp/src/test/kotlin/OtelSentryPropagatorTest.kt

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import io.opentelemetry.api.trace.TraceState
77
import io.opentelemetry.context.Context
88
import io.opentelemetry.context.propagation.TextMapGetter
99
import io.opentelemetry.context.propagation.TextMapSetter
10+
import io.opentelemetry.sdk.trace.SdkTracerProvider
1011
import io.sentry.Baggage
1112
import io.sentry.Sentry
1213
import kotlin.test.AfterTest
@@ -171,6 +172,97 @@ class OpenTelemetryOtlpPropagatorTest {
171172
)
172173
}
173174

175+
@Test
176+
fun `injects headers if URL in span attributes matches tracePropagationTargets`() {
177+
Sentry.init { options ->
178+
options.dsn = "https://key@sentry.io/proj"
179+
options.setTracePropagationTargets(listOf("sentry.io"))
180+
}
181+
val propagator = OpenTelemetryOtlpPropagator()
182+
val carrier = mutableMapOf<String, String>()
183+
val tracerProvider = SdkTracerProvider.builder().build()
184+
val otelSpan =
185+
tracerProvider
186+
.get("test")
187+
.spanBuilder("test")
188+
.setAttribute("url.full", "https://sentry.io/api/0/")
189+
.startSpan()
190+
val baggage =
191+
Baggage.fromHeader(
192+
"sentry-environment=production,sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rand=0.456789,sentry-sample_rate=0.5,sentry-sampled=true,sentry-trace_id=df71f5972f754b4c85af13ff5c07017d"
193+
)
194+
195+
try {
196+
val context =
197+
Context.root().with(otelSpan).with(OpenTelemetryOtlpPropagator.SENTRY_BAGGAGE_KEY, baggage)
198+
199+
propagator.inject(context, carrier, MapSetter())
200+
} finally {
201+
otelSpan.end()
202+
tracerProvider.shutdown()
203+
}
204+
205+
assertEquals(
206+
"${otelSpan.spanContext.traceId}-${otelSpan.spanContext.spanId}-1",
207+
carrier["sentry-trace"],
208+
)
209+
assertEquals(
210+
"sentry-environment=production,sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rand=0.456789,sentry-sample_rate=0.5,sentry-sampled=true,sentry-trace_id=df71f5972f754b4c85af13ff5c07017d",
211+
carrier["baggage"],
212+
)
213+
}
214+
215+
@Test
216+
fun `does not inject headers if URL in span attributes does not match tracePropagationTargets`() {
217+
Sentry.init { options ->
218+
options.dsn = "https://key@sentry.io/proj"
219+
options.setTracePropagationTargets(listOf("github.com"))
220+
}
221+
val propagator = OpenTelemetryOtlpPropagator()
222+
val carrier = mutableMapOf<String, String>()
223+
val tracerProvider = SdkTracerProvider.builder().build()
224+
val otelSpan =
225+
tracerProvider
226+
.get("test")
227+
.spanBuilder("test")
228+
.setAttribute("url.full", "https://sentry.io/api/0/")
229+
.startSpan()
230+
231+
try {
232+
propagator.inject(Context.root().with(otelSpan), carrier, MapSetter())
233+
} finally {
234+
otelSpan.end()
235+
tracerProvider.shutdown()
236+
}
237+
238+
assertNull(carrier["sentry-trace"])
239+
assertNull(carrier["baggage"])
240+
}
241+
242+
@Test
243+
fun `does not inject headers if tracePropagationTargets is restricted and URL is unavailable`() {
244+
Sentry.init { options ->
245+
options.dsn = "https://key@sentry.io/proj"
246+
options.setTracePropagationTargets(listOf("sentry.io"))
247+
}
248+
val propagator = OpenTelemetryOtlpPropagator()
249+
val carrier = mutableMapOf<String, String>()
250+
251+
val otelSpanContext =
252+
SpanContext.create(
253+
"f9118105af4a2d42b4124532cd1065ff",
254+
"424cffc8f94feeee",
255+
TraceFlags.getSampled(),
256+
TraceState.getDefault(),
257+
)
258+
val otelSpan = Span.wrap(otelSpanContext)
259+
260+
propagator.inject(Context.root().with(otelSpan), carrier, MapSetter())
261+
262+
assertNull(carrier["sentry-trace"])
263+
assertNull(carrier["baggage"])
264+
}
265+
174266
@Test
175267
fun `does not inject headers when no span in context`() {
176268
val propagator = OpenTelemetryOtlpPropagator()

0 commit comments

Comments
 (0)