From 8ef382513c700a49f64129001aa66e3e0662f500 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 23 Jan 2025 11:58:48 +0100 Subject: [PATCH 01/15] Attach request object to event for OTel --- .../api/sentry-opentelemetry-core.api | 5 + .../OpenTelemetryAttributesExtractor.java | 88 +++++++++ .../opentelemetry/SentrySpanExporter.java | 10 +- .../OpenTelemetryAtrtibutesExtractorTest.kt | 182 ++++++++++++++++++ 4 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java create mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAtrtibutesExtractorTest.kt diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index fd2099a7303..1e9bb60416b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -1,3 +1,8 @@ +public final class io/sentry/opentelemetry/OpenTelemetryAttributesExtractor { + public fun ()V + public fun extract (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/ISpan;Lio/sentry/IScope;)V +} + public final class io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor : io/sentry/EventProcessor { public fun ()V public fun getOrder ()Ljava/lang/Long; diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java new file mode 100644 index 00000000000..cc4fcc5da71 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java @@ -0,0 +1,88 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.semconv.HttpAttributes; +import io.opentelemetry.semconv.ServerAttributes; +import io.opentelemetry.semconv.UrlAttributes; +import io.sentry.IScope; +import io.sentry.ISpan; +import io.sentry.protocol.Request; +import io.sentry.util.UrlUtils; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Internal +public final class OpenTelemetryAttributesExtractor { + + public void extract( + final @NotNull SpanData otelSpan, + final @NotNull ISpan sentrySpan, + final @NotNull IScope scope) { + final @NotNull Attributes attributes = otelSpan.getAttributes(); + addRequestAttributesToScope(attributes, scope); + } + + private void addRequestAttributesToScope(Attributes attributes, IScope scope) { + if (scope.getRequest() == null) { + scope.setRequest(new Request()); + } + final @Nullable Request request = scope.getRequest(); + if (request != null) { + final @Nullable String requestMethod = attributes.get(HttpAttributes.HTTP_REQUEST_METHOD); + if (requestMethod != null) { + request.setMethod(requestMethod); + } + + final @Nullable String urlFull = attributes.get(UrlAttributes.URL_FULL); + if (urlFull != null) { + final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(urlFull); + 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) { + request.setQueryString(query); + } + } + } + } + + 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); + final @Nullable Long serverPort = attributes.get(ServerAttributes.SERVER_PORT); + final @Nullable String path = attributes.get(UrlAttributes.URL_PATH); + + if (scheme == null || serverAddress == null) { + return ""; + } + + final @NotNull StringBuilder urlBuilder = new StringBuilder(); + urlBuilder.append(scheme); + urlBuilder.append("://"); + + if (serverAddress != null) { + urlBuilder.append(serverAddress); + if (serverPort != null) { + urlBuilder.append(":"); + urlBuilder.append(serverPort); + } + } + + if (path != null) { + urlBuilder.append(path); + } + + return urlBuilder.toString(); + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 7851f07550c..4d2e7545c64 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -19,6 +19,7 @@ import io.sentry.ISpan; import io.sentry.ITransaction; import io.sentry.Instrumenter; +import io.sentry.ScopeType; import io.sentry.ScopesAdapter; import io.sentry.SentryDate; import io.sentry.SentryInstantDate; @@ -50,6 +51,8 @@ public final class SentrySpanExporter implements SpanExporter { private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); private final @NotNull SpanDescriptionExtractor spanDescriptionExtractor = new SpanDescriptionExtractor(); + private final @NotNull OpenTelemetryAttributesExtractor attributesExtractor = + new OpenTelemetryAttributesExtractor(); private final @NotNull IScopes scopes; private final @NotNull List attributeKeysToRemove = @@ -267,8 +270,10 @@ private void transferSpanDetails( spanStorage.getSentrySpan(span.getSpanContext()); final @Nullable IScopes scopesMaybe = sentrySpanMaybe != null ? sentrySpanMaybe.getScopes() : null; - final @NotNull IScopes scopesToUse = + final @NotNull IScopes scopesToUseBeforeForking = scopesMaybe == null ? ScopesAdapter.getInstance() : scopesMaybe; + final @NotNull IScopes scopesToUse = + scopesToUseBeforeForking.forkedCurrentScope("SentrySpanExporter.createTransaction"); final @NotNull OtelSpanInfo spanInfo = spanDescriptionExtractor.extractSpanInfo(span, sentrySpanMaybe); @@ -331,6 +336,9 @@ private void transferSpanDetails( setOtelSpanKind(span, sentryTransaction); transferSpanDetails(sentrySpanMaybe, sentryTransaction); + scopesToUse.configureScope( + ScopeType.CURRENT, scope -> attributesExtractor.extract(span, sentryTransaction, scope)); + return sentryTransaction; } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAtrtibutesExtractorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAtrtibutesExtractorTest.kt new file mode 100644 index 00000000000..39756b7de1a --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAtrtibutesExtractorTest.kt @@ -0,0 +1,182 @@ +package io.sentry.opentelemetry + +import io.opentelemetry.api.common.AttributeKey +import io.opentelemetry.sdk.internal.AttributesMap +import io.opentelemetry.sdk.trace.data.SpanData +import io.opentelemetry.semconv.ServerAttributes +import io.opentelemetry.semconv.UrlAttributes +import io.sentry.ISpan +import io.sentry.Scope +import io.sentry.SentryOptions +import io.sentry.protocol.Request +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +class OpenTelemetryAtrtibutesExtractorTest { + + private class Fixture { + val spanData = mock() + val attributes = AttributesMap.create(100, 100) + val sentrySpan = mock() + val options = SentryOptions.empty() + val scope = Scope(options) + + init { + whenever(spanData.attributes).thenReturn(attributes) + } + } + + private val fixture = Fixture() + + @Test + fun `sets URL based on OTel attributes`() { + givenAttributes( + mapOf( + UrlAttributes.URL_SCHEME to "https", + UrlAttributes.URL_PATH to "/path/to/123", + UrlAttributes.URL_QUERY to "q=123456&b=X", + ServerAttributes.SERVER_ADDRESS to "io.sentry", + ServerAttributes.SERVER_PORT to 8081L + ) + ) + + whenExtractingAttributes() + + thenRequestIsSet() + thenUrlIsSetTo("https://io.sentry:8081/path/to/123") + thenQueryIsSetTo("q=123456&b=X") + } + + @Test + fun `when there is an existing request on scope it is filled with more details`() { + fixture.scope.request = Request().also { it.bodySize = 123L } + givenAttributes( + mapOf( + UrlAttributes.URL_SCHEME to "https", + UrlAttributes.URL_PATH to "/path/to/123", + UrlAttributes.URL_QUERY to "q=123456&b=X", + ServerAttributes.SERVER_ADDRESS to "io.sentry", + ServerAttributes.SERVER_PORT to 8081L + ) + ) + + whenExtractingAttributes() + + thenRequestIsSet() + thenUrlIsSetTo("https://io.sentry:8081/path/to/123") + thenQueryIsSetTo("q=123456&b=X") + assertEquals(123L, fixture.scope.request!!.bodySize) + } + + @Test + fun `when there is an existing request with url on scope it is kept`() { + fixture.scope.request = Request().also { + it.url = "http://docs.sentry.io:3000/platform" + it.queryString = "s=abc" + } + givenAttributes( + mapOf( + UrlAttributes.URL_SCHEME to "https", + UrlAttributes.URL_PATH to "/path/to/123", + UrlAttributes.URL_QUERY to "q=123456&b=X", + ServerAttributes.SERVER_ADDRESS to "io.sentry", + ServerAttributes.SERVER_PORT to 8081L + ) + ) + + whenExtractingAttributes() + + thenRequestIsSet() + thenUrlIsSetTo("http://docs.sentry.io:3000/platform") + thenQueryIsSetTo("s=abc") + } + + @Test + fun `sets URL based on OTel attributes without port`() { + givenAttributes( + mapOf( + UrlAttributes.URL_SCHEME to "https", + UrlAttributes.URL_PATH to "/path/to/123", + ServerAttributes.SERVER_ADDRESS to "io.sentry" + ) + ) + + whenExtractingAttributes() + + thenRequestIsSet() + thenUrlIsSetTo("https://io.sentry/path/to/123") + } + + @Test + fun `sets URL based on OTel attributes without path`() { + givenAttributes( + mapOf( + UrlAttributes.URL_SCHEME to "https", + ServerAttributes.SERVER_ADDRESS to "io.sentry" + ) + ) + + whenExtractingAttributes() + + thenRequestIsSet() + thenUrlIsSetTo("https://io.sentry") + } + + @Test + fun `does not set URL if server address is missing`() { + givenAttributes( + mapOf( + UrlAttributes.URL_SCHEME to "https" + ) + ) + + whenExtractingAttributes() + + thenRequestIsSet() + thenUrlIsNotSet() + } + + @Test + fun `does not set URL if scheme is missing`() { + givenAttributes( + mapOf( + ServerAttributes.SERVER_ADDRESS to "io.sentry" + ) + ) + + whenExtractingAttributes() + + thenRequestIsSet() + thenUrlIsNotSet() + } + + private fun givenAttributes(map: Map, Any>) { + map.forEach { k, v -> + fixture.attributes.put(k, v) + } + } + + private fun whenExtractingAttributes() { + OpenTelemetryAttributesExtractor().extract(fixture.spanData, fixture.sentrySpan, fixture.scope) + } + + private fun thenRequestIsSet() { + assertNotNull(fixture.scope.request) + } + + private fun thenUrlIsSetTo(expected: String) { + assertEquals(expected, fixture.scope.request!!.url) + } + + private fun thenUrlIsNotSet() { + assertNull(fixture.scope.request!!.url) + } + + private fun thenQueryIsSetTo(expected: String) { + assertEquals(expected, fixture.scope.request!!.queryString) + } +} From d3cab7c0de04d1d6b401f5ce8a74e4db8981c1b9 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 23 Jan 2025 15:25:27 +0100 Subject: [PATCH 02/15] fix test name --- ...esExtractorTest.kt => OpenTelemetryAttributesExtractorTest.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/{OpenTelemetryAtrtibutesExtractorTest.kt => OpenTelemetryAttributesExtractorTest.kt} (100%) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAtrtibutesExtractorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt similarity index 100% rename from sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAtrtibutesExtractorTest.kt rename to sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt From 9a183ab393e4ff1a4ea460c1335ab34298983fb1 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 23 Jan 2025 16:12:06 +0100 Subject: [PATCH 03/15] add http server request headers to sentry request in payload --- .../OpenTelemetryAttributesExtractor.java | 54 ++++++++++++++- .../OpenTelemetryAttributesExtractorTest.kt | 66 ++++++++++++++++++- 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java index cc4fcc5da71..412885f911f 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java @@ -7,8 +7,14 @@ import io.opentelemetry.semconv.UrlAttributes; import io.sentry.IScope; import io.sentry.ISpan; +import io.sentry.Sentry; import io.sentry.protocol.Request; +import io.sentry.util.HttpUtils; +import io.sentry.util.StringUtils; import io.sentry.util.UrlUtils; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -16,12 +22,16 @@ @ApiStatus.Internal public final class OpenTelemetryAttributesExtractor { + private static final String HTTP_REQUEST_HEADER_PREFIX = "http.request.header."; + public void extract( final @NotNull SpanData otelSpan, final @NotNull ISpan sentrySpan, final @NotNull IScope scope) { final @NotNull Attributes attributes = otelSpan.getAttributes(); - addRequestAttributesToScope(attributes, scope); + if (attributes.get(HttpAttributes.HTTP_REQUEST_METHOD) != null) { + addRequestAttributesToScope(attributes, scope); + } } private void addRequestAttributesToScope(Attributes attributes, IScope scope) { @@ -54,9 +64,51 @@ private void addRequestAttributesToScope(Attributes attributes, IScope scope) { request.setQueryString(query); } } + + if (request.getHeaders() == null) { + Map headers = collectHeaders(attributes); + if (!headers.isEmpty()) { + request.setHeaders(headers); + } + } } } + @SuppressWarnings("unchecked") + private static Map collectHeaders(Attributes attributes) { + Map headers = new HashMap<>(); + + attributes.forEach( + (key, value) -> { + final @NotNull String attributeKeyAsString = key.getKey(); + if (attributeKeyAsString.startsWith(HTTP_REQUEST_HEADER_PREFIX)) { + final @NotNull String headerName = + StringUtils.removePrefix(attributeKeyAsString, HTTP_REQUEST_HEADER_PREFIX); + // TODO pass in options + if (Sentry.getCurrentScopes().getOptions().isSendDefaultPii() + || !HttpUtils.containsSensitiveHeader(headerName)) { + if (value instanceof List) { + try { + final @NotNull List headerValues = (List) value; + headers.put( + headerName, + toString( + HttpUtils.filterOutSecurityCookiesFromHeader( + headerValues, headerName, null))); + } catch (Throwable t) { + // TODO log + } + } + } + } + }); + return headers; + } + + private static @Nullable String toString(final @Nullable List list) { + return list != null ? String.join(",", list) : 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); diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt index 39756b7de1a..f53ba9bb124 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt @@ -3,6 +3,7 @@ package io.sentry.opentelemetry import io.opentelemetry.api.common.AttributeKey import io.opentelemetry.sdk.internal.AttributesMap import io.opentelemetry.sdk.trace.data.SpanData +import io.opentelemetry.semconv.HttpAttributes import io.opentelemetry.semconv.ServerAttributes import io.opentelemetry.semconv.UrlAttributes import io.sentry.ISpan @@ -13,10 +14,11 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.whenever import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertFalse import kotlin.test.assertNotNull import kotlin.test.assertNull -class OpenTelemetryAtrtibutesExtractorTest { +class OpenTelemetryAttributesExtractorTest { private class Fixture { val spanData = mock() @@ -36,6 +38,7 @@ class OpenTelemetryAtrtibutesExtractorTest { fun `sets URL based on OTel attributes`() { givenAttributes( mapOf( + HttpAttributes.HTTP_REQUEST_METHOD to "GET", UrlAttributes.URL_SCHEME to "https", UrlAttributes.URL_PATH to "/path/to/123", UrlAttributes.URL_QUERY to "q=123456&b=X", @@ -56,6 +59,7 @@ class OpenTelemetryAtrtibutesExtractorTest { fixture.scope.request = Request().also { it.bodySize = 123L } givenAttributes( mapOf( + HttpAttributes.HTTP_REQUEST_METHOD to "GET", UrlAttributes.URL_SCHEME to "https", UrlAttributes.URL_PATH to "/path/to/123", UrlAttributes.URL_QUERY to "q=123456&b=X", @@ -80,6 +84,7 @@ class OpenTelemetryAtrtibutesExtractorTest { } givenAttributes( mapOf( + HttpAttributes.HTTP_REQUEST_METHOD to "GET", UrlAttributes.URL_SCHEME to "https", UrlAttributes.URL_PATH to "/path/to/123", UrlAttributes.URL_QUERY to "q=123456&b=X", @@ -99,6 +104,7 @@ class OpenTelemetryAtrtibutesExtractorTest { fun `sets URL based on OTel attributes without port`() { givenAttributes( mapOf( + HttpAttributes.HTTP_REQUEST_METHOD to "GET", UrlAttributes.URL_SCHEME to "https", UrlAttributes.URL_PATH to "/path/to/123", ServerAttributes.SERVER_ADDRESS to "io.sentry" @@ -115,6 +121,7 @@ class OpenTelemetryAtrtibutesExtractorTest { fun `sets URL based on OTel attributes without path`() { givenAttributes( mapOf( + HttpAttributes.HTTP_REQUEST_METHOD to "GET", UrlAttributes.URL_SCHEME to "https", ServerAttributes.SERVER_ADDRESS to "io.sentry" ) @@ -130,6 +137,7 @@ class OpenTelemetryAtrtibutesExtractorTest { fun `does not set URL if server address is missing`() { givenAttributes( mapOf( + HttpAttributes.HTTP_REQUEST_METHOD to "GET", UrlAttributes.URL_SCHEME to "https" ) ) @@ -144,6 +152,7 @@ class OpenTelemetryAtrtibutesExtractorTest { fun `does not set URL if scheme is missing`() { givenAttributes( mapOf( + HttpAttributes.HTTP_REQUEST_METHOD to "GET", ServerAttributes.SERVER_ADDRESS to "io.sentry" ) ) @@ -154,6 +163,49 @@ class OpenTelemetryAtrtibutesExtractorTest { thenUrlIsNotSet() } + @Test + fun `sets server request headers based on OTel attributes and merges list of values`() { + givenAttributes( + mapOf( + HttpAttributes.HTTP_REQUEST_METHOD to "GET", + AttributeKey.stringArrayKey("http.request.header.baggage") to listOf("sentry-environment=production,sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-sampled=true,sentry-trace_id=df71f5972f754b4c85af13ff5c07017d", "another-baggage=abc,more=def"), + AttributeKey.stringArrayKey("http.request.header.sentry-trace") to listOf("f9118105af4a2d42b4124532cd176588-4542d085bb0b4de5"), + AttributeKey.stringArrayKey("http.response.header.some-header") to listOf("some-value") + ) + ) + + whenExtractingAttributes() + + thenRequestIsSet() + thenHeaderIsPresentOnRequest("baggage", "sentry-environment=production,sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-sampled=true,sentry-trace_id=df71f5972f754b4c85af13ff5c07017d,another-baggage=abc,more=def") + thenHeaderIsPresentOnRequest("sentry-trace", "f9118105af4a2d42b4124532cd176588-4542d085bb0b4de5") + thenHeaderIsNotPresentOnRequest("some-header") + } + + @Test + fun `if there are no header attributes does not set headers on request`() { + givenAttributes(mapOf(HttpAttributes.HTTP_REQUEST_METHOD to "GET")) + + whenExtractingAttributes() + + thenRequestIsSet() + assertNull(fixture.scope.request!!.headers) + } + + @Test + fun `if there is no request method attribute does not set request on scope`() { + givenAttributes( + mapOf( + UrlAttributes.URL_SCHEME to "https", + ServerAttributes.SERVER_ADDRESS to "io.sentry" + ) + ) + + whenExtractingAttributes() + + thenRequestIsNotSet() + } + private fun givenAttributes(map: Map, Any>) { map.forEach { k, v -> fixture.attributes.put(k, v) @@ -168,6 +220,10 @@ class OpenTelemetryAtrtibutesExtractorTest { assertNotNull(fixture.scope.request) } + private fun thenRequestIsNotSet() { + assertNull(fixture.scope.request) + } + private fun thenUrlIsSetTo(expected: String) { assertEquals(expected, fixture.scope.request!!.url) } @@ -179,4 +235,12 @@ class OpenTelemetryAtrtibutesExtractorTest { private fun thenQueryIsSetTo(expected: String) { assertEquals(expected, fixture.scope.request!!.queryString) } + + private fun thenHeaderIsPresentOnRequest(headerName: String, expectedValue: String) { + assertEquals(expectedValue, fixture.scope.request!!.headers!!.get(headerName)) + } + + private fun thenHeaderIsNotPresentOnRequest(headerName: String) { + assertFalse(fixture.scope.request!!.headers!!.containsKey(headerName)) + } } From ecd604030fab91657ee1042c7afa0033ff03558a Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 23 Jan 2025 16:14:11 +0100 Subject: [PATCH 04/15] rename test class --- .../src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt index 39756b7de1a..1ba77f2454e 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt @@ -16,7 +16,7 @@ import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull -class OpenTelemetryAtrtibutesExtractorTest { +class OpenTelemetryAttributesExtractorTest { private class Fixture { val spanData = mock() From 6e0b24dbef365f04f1a17c997f2ee927e5a3a8eb Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 23 Jan 2025 16:16:37 +0100 Subject: [PATCH 05/15] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e4603cd4fa..f6c034762cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ ### Fixes - Avoid logging an error when a float is passed in the manifest ([#4031](https://github.com/getsentry/sentry-java/pull/4031)) +- Add `request` details to transactions created through OpenTelemetry ([#4098](https://github.com/getsentry/sentry-java/pull/4098)) + - We now add HTTP request method and URL where Sentry expects it to display it in Sentry UI ## 8.0.0 From 28a7e5661a050531a0bf1b8fbcc16f809d859e14 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 24 Jan 2025 06:51:37 +0100 Subject: [PATCH 06/15] do not override existing url on request even with full url --- .../OpenTelemetryAttributesExtractor.java | 10 ++++++---- .../OpenTelemetryAttributesExtractorTest.kt | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java index cc4fcc5da71..431b4d274e3 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java @@ -35,10 +35,12 @@ private void addRequestAttributesToScope(Attributes attributes, IScope scope) { request.setMethod(requestMethod); } - final @Nullable String urlFull = attributes.get(UrlAttributes.URL_FULL); - if (urlFull != null) { - final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(urlFull); - urlDetails.applyToRequest(request); + if (request.getUrl() == null) { + final @Nullable String urlFull = attributes.get(UrlAttributes.URL_FULL); + if (urlFull != null) { + final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(urlFull); + urlDetails.applyToRequest(request); + } } if (request.getUrl() == null) { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt index 1ba77f2454e..f962cfa594d 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt @@ -95,6 +95,25 @@ class OpenTelemetryAttributesExtractorTest { thenQueryIsSetTo("s=abc") } + @Test + fun `when there is an existing request with url on scope it is kept with URL_FULL`() { + fixture.scope.request = Request().also { + it.url = "http://docs.sentry.io:3000/platform" + it.queryString = "s=abc" + } + givenAttributes( + mapOf( + UrlAttributes.URL_FULL to "https://io.sentry:8081/path/to/123?q=123456&b=X" + ) + ) + + whenExtractingAttributes() + + thenRequestIsSet() + thenUrlIsSetTo("http://docs.sentry.io:3000/platform") + thenQueryIsSetTo("s=abc") + } + @Test fun `sets URL based on OTel attributes without port`() { givenAttributes( From 0f77ec24a6e0ff5c12fbfc134060eacede79b2b3 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 24 Jan 2025 10:42:16 +0100 Subject: [PATCH 07/15] pass in options and use them --- .../api/sentry-opentelemetry-core.api | 2 +- .../OpenTelemetryAttributesExtractor.java | 27 ++++++++++++------- .../opentelemetry/SentrySpanExporter.java | 4 ++- .../OpenTelemetryAttributesExtractorTest.kt | 2 +- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index 1e9bb60416b..5b4eb558638 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -1,6 +1,6 @@ public final class io/sentry/opentelemetry/OpenTelemetryAttributesExtractor { public fun ()V - public fun extract (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/ISpan;Lio/sentry/IScope;)V + public fun extract (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/ISpan;Lio/sentry/IScope;Lio/sentry/SentryOptions;)V } public final class io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor : io/sentry/EventProcessor { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java index aa834fc9183..44e7f1fed6a 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java @@ -7,7 +7,8 @@ import io.opentelemetry.semconv.UrlAttributes; import io.sentry.IScope; import io.sentry.ISpan; -import io.sentry.Sentry; +import io.sentry.SentryLevel; +import io.sentry.SentryOptions; import io.sentry.protocol.Request; import io.sentry.util.HttpUtils; import io.sentry.util.StringUtils; @@ -27,14 +28,18 @@ public final class OpenTelemetryAttributesExtractor { public void extract( final @NotNull SpanData otelSpan, final @NotNull ISpan sentrySpan, - final @NotNull IScope scope) { + final @NotNull IScope scope, + final @NotNull SentryOptions options) { final @NotNull Attributes attributes = otelSpan.getAttributes(); if (attributes.get(HttpAttributes.HTTP_REQUEST_METHOD) != null) { - addRequestAttributesToScope(attributes, scope); + addRequestAttributesToScope(attributes, scope, options); } } - private void addRequestAttributesToScope(Attributes attributes, IScope scope) { + private void addRequestAttributesToScope( + final @NotNull Attributes attributes, + final @NotNull IScope scope, + final @NotNull SentryOptions options) { if (scope.getRequest() == null) { scope.setRequest(new Request()); } @@ -68,7 +73,7 @@ private void addRequestAttributesToScope(Attributes attributes, IScope scope) { } if (request.getHeaders() == null) { - Map headers = collectHeaders(attributes); + Map headers = collectHeaders(attributes, options); if (!headers.isEmpty()) { request.setHeaders(headers); } @@ -77,7 +82,8 @@ private void addRequestAttributesToScope(Attributes attributes, IScope scope) { } @SuppressWarnings("unchecked") - private static Map collectHeaders(Attributes attributes) { + private static Map collectHeaders( + final @NotNull Attributes attributes, final @NotNull SentryOptions options) { Map headers = new HashMap<>(); attributes.forEach( @@ -86,9 +92,7 @@ private static Map collectHeaders(Attributes attributes) { if (attributeKeyAsString.startsWith(HTTP_REQUEST_HEADER_PREFIX)) { final @NotNull String headerName = StringUtils.removePrefix(attributeKeyAsString, HTTP_REQUEST_HEADER_PREFIX); - // TODO pass in options - if (Sentry.getCurrentScopes().getOptions().isSendDefaultPii() - || !HttpUtils.containsSensitiveHeader(headerName)) { + if (options.isSendDefaultPii() || !HttpUtils.containsSensitiveHeader(headerName)) { if (value instanceof List) { try { final @NotNull List headerValues = (List) value; @@ -97,8 +101,11 @@ private static Map collectHeaders(Attributes attributes) { toString( HttpUtils.filterOutSecurityCookiesFromHeader( headerValues, headerName, null))); + throw new RuntimeException("hey"); } catch (Throwable t) { - // TODO log + options + .getLogger() + .log(SentryLevel.WARNING, "Expected a List as header", t); } } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 4d2e7545c64..9b14f7e4691 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -337,7 +337,9 @@ private void transferSpanDetails( transferSpanDetails(sentrySpanMaybe, sentryTransaction); scopesToUse.configureScope( - ScopeType.CURRENT, scope -> attributesExtractor.extract(span, sentryTransaction, scope)); + ScopeType.CURRENT, + scope -> + attributesExtractor.extract(span, sentryTransaction, scope, scopesToUse.getOptions())); return sentryTransaction; } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt index eabb23fc504..8922a1f05b6 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt @@ -232,7 +232,7 @@ class OpenTelemetryAttributesExtractorTest { } private fun whenExtractingAttributes() { - OpenTelemetryAttributesExtractor().extract(fixture.spanData, fixture.sentrySpan, fixture.scope) + OpenTelemetryAttributesExtractor().extract(fixture.spanData, fixture.sentrySpan, fixture.scope, fixture.options) } private fun thenRequestIsSet() { From 66d3b7b576edbd755ea2e2e1b2107a420eed9928 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 24 Jan 2025 13:39:45 +0100 Subject: [PATCH 08/15] remove span param; remove test exception --- .../api/sentry-opentelemetry-core.api | 2 +- .../opentelemetry/OpenTelemetryAttributesExtractor.java | 3 --- .../main/java/io/sentry/opentelemetry/SentrySpanExporter.java | 3 +-- .../src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt | 4 +--- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index 5b4eb558638..d0b0d181f98 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -1,6 +1,6 @@ public final class io/sentry/opentelemetry/OpenTelemetryAttributesExtractor { public fun ()V - public fun extract (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/ISpan;Lio/sentry/IScope;Lio/sentry/SentryOptions;)V + public fun extract (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/IScope;Lio/sentry/SentryOptions;)V } public final class io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor : io/sentry/EventProcessor { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java index 44e7f1fed6a..9081a865365 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java @@ -6,7 +6,6 @@ import io.opentelemetry.semconv.ServerAttributes; import io.opentelemetry.semconv.UrlAttributes; import io.sentry.IScope; -import io.sentry.ISpan; import io.sentry.SentryLevel; import io.sentry.SentryOptions; import io.sentry.protocol.Request; @@ -27,7 +26,6 @@ public final class OpenTelemetryAttributesExtractor { public void extract( final @NotNull SpanData otelSpan, - final @NotNull ISpan sentrySpan, final @NotNull IScope scope, final @NotNull SentryOptions options) { final @NotNull Attributes attributes = otelSpan.getAttributes(); @@ -101,7 +99,6 @@ private static Map collectHeaders( toString( HttpUtils.filterOutSecurityCookiesFromHeader( headerValues, headerName, null))); - throw new RuntimeException("hey"); } catch (Throwable t) { options .getLogger() diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 9b14f7e4691..66af886bba6 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -338,8 +338,7 @@ private void transferSpanDetails( scopesToUse.configureScope( ScopeType.CURRENT, - scope -> - attributesExtractor.extract(span, sentryTransaction, scope, scopesToUse.getOptions())); + scope -> attributesExtractor.extract(span, scope, scopesToUse.getOptions())); return sentryTransaction; } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt index 8922a1f05b6..8df32b87120 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OpenTelemetryAttributesExtractorTest.kt @@ -6,7 +6,6 @@ import io.opentelemetry.sdk.trace.data.SpanData import io.opentelemetry.semconv.HttpAttributes import io.opentelemetry.semconv.ServerAttributes import io.opentelemetry.semconv.UrlAttributes -import io.sentry.ISpan import io.sentry.Scope import io.sentry.SentryOptions import io.sentry.protocol.Request @@ -23,7 +22,6 @@ class OpenTelemetryAttributesExtractorTest { private class Fixture { val spanData = mock() val attributes = AttributesMap.create(100, 100) - val sentrySpan = mock() val options = SentryOptions.empty() val scope = Scope(options) @@ -232,7 +230,7 @@ class OpenTelemetryAttributesExtractorTest { } private fun whenExtractingAttributes() { - OpenTelemetryAttributesExtractor().extract(fixture.spanData, fixture.sentrySpan, fixture.scope, fixture.options) + OpenTelemetryAttributesExtractor().extract(fixture.spanData, fixture.scope, fixture.options) } private fun thenRequestIsSet() { From 0d96359dda701b716351e8f62409a6694a6a8735 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 24 Jan 2025 13:45:22 +0100 Subject: [PATCH 09/15] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 249b605f5cc..2a24f25e41c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - Remove `java.lang.ClassNotFoundException` debug logs when searching for OpenTelemetry marker classes ([#4091](https://github.com/getsentry/sentry-java/pull/4091)) - There was up to three of these, one for `io.sentry.opentelemetry.agent.AgentMarker`, `io.sentry.opentelemetry.agent.AgentlessMarker` and `io.sentry.opentelemetry.agent.AgentlessSpringMarker`. - These were not indicators of something being wrong but rather the SDK looking at what is available at runtime to configure itself accordingly. +- Add HTTP server request headers from OpenTelemetry span attributes to sentry `request` in payload ([#4102](https://github.com/getsentry/sentry-java/pull/4102)) + - You have to explicitly enable each header by adding it to the [OpenTelemetry config](https://opentelemetry.io/docs/zero-code/java/agent/instrumentation/http/#capturing-http-request-and-response-headers) ## 8.0.0 From ba2cbb0c3af6b68436efb1671be9f6be7b05c758 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 24 Jan 2025 13:48:18 +0100 Subject: [PATCH 10/15] changelog pii --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a24f25e41c..a54e7a66b50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - These were not indicators of something being wrong but rather the SDK looking at what is available at runtime to configure itself accordingly. - Add HTTP server request headers from OpenTelemetry span attributes to sentry `request` in payload ([#4102](https://github.com/getsentry/sentry-java/pull/4102)) - You have to explicitly enable each header by adding it to the [OpenTelemetry config](https://opentelemetry.io/docs/zero-code/java/agent/instrumentation/http/#capturing-http-request-and-response-headers) + - Please only enable headers you actually want to send to Sentry. Some may contain sensitive data like PII, cookies, tokens etc. ## 8.0.0 From e1f7fec597a1c151f3526ea3b7f3bbe87cfb8f0c Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 30 Jan 2025 10:10:42 +0100 Subject: [PATCH 11/15] Use `java.net.URL` for combining url attributes (#4105) --- .../OpenTelemetryAttributesExtractor.java | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java index 9081a865365..e773fc3b891 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryAttributesExtractor.java @@ -12,6 +12,7 @@ import io.sentry.util.HttpUtils; import io.sentry.util.StringUtils; import io.sentry.util.UrlUtils; +import java.net.URL; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -57,7 +58,7 @@ private void addRequestAttributesToScope( } if (request.getUrl() == null) { - final String urlString = buildUrlString(attributes); + final String urlString = buildUrlString(attributes, options); if (!urlString.isEmpty()) { request.setUrl(urlString); } @@ -115,7 +116,8 @@ private static Map collectHeaders( return list != null ? String.join(",", list) : null; } - private @NotNull String buildUrlString(final @NotNull Attributes attributes) { + private @NotNull String buildUrlString( + final @NotNull Attributes attributes, final @NotNull SentryOptions options) { 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); @@ -125,22 +127,18 @@ private static Map collectHeaders( return ""; } - final @NotNull StringBuilder urlBuilder = new StringBuilder(); - urlBuilder.append(scheme); - urlBuilder.append("://"); - - if (serverAddress != null) { - urlBuilder.append(serverAddress); - if (serverPort != null) { - urlBuilder.append(":"); - urlBuilder.append(serverPort); + 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 ""; } - - if (path != null) { - urlBuilder.append(path); - } - - return urlBuilder.toString(); } } From d698a146b960ad9fe5b71d418f38ed31806b445a Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 3 Feb 2025 08:38:22 +0100 Subject: [PATCH 12/15] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2caa71f64bf..efae6c7e226 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Add HTTP server request headers from OpenTelemetry span attributes to sentry `request` in payload ([#4102](https://github.com/getsentry/sentry-java/pull/4102)) - You have to explicitly enable each header by adding it to the [OpenTelemetry config](https://opentelemetry.io/docs/zero-code/java/agent/instrumentation/http/#capturing-http-request-and-response-headers) - Please only enable headers you actually want to send to Sentry. Some may contain sensitive data like PII, cookies, tokens etc. + - We are no longer adding request headers to `contexts/otel/attributes` of the event. ## 8.0.0 From 12aca6ba6c13a81ad9d8ae08629f3e8b6c00a30f Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 3 Feb 2025 09:02:15 +0100 Subject: [PATCH 13/15] do not send request headers in contexts/otel/attributes --- .../sentry/opentelemetry/SentrySpanExporter.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 66af886bba6..3790f527289 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -67,6 +67,9 @@ public final class SentrySpanExporter implements SpanExporter { InternalSemanticAttributes.PARENT_SAMPLED.getKey(), ProcessIncubatingAttributes.PROCESS_COMMAND_ARGS.getKey() // can be very long ); + + private final @NotNull List attributeToRemoveByPrefix = + Arrays.asList("http.request.header."); private static final @NotNull Long SPAN_TIMEOUT = DateUtils.secondsToNanos(5 * 60); public static final String TRACE_ORIGIN = "auto.opentelemetry"; @@ -488,7 +491,17 @@ private SpanStatus mapOtelStatus( } private boolean shouldRemoveAttribute(final @NotNull String key) { - return attributeKeysToRemove.contains(key); + if (attributeKeysToRemove.contains(key)) { + return true; + } + + for (String prefix : attributeToRemoveByPrefix) { + if (key.startsWith(prefix)) { + return true; + } + } + + return false; } private void setOtelInstrumentationInfo( From 3233b3cabd5ba21f50e3b5d475218aea827dc64d Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 25 Feb 2025 12:25:44 +0100 Subject: [PATCH 14/15] also remove response headers from span attributes sent to Sentry --- .../main/java/io/sentry/opentelemetry/SentrySpanExporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 2780c94e414..6db21ff7979 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -70,7 +70,7 @@ public final class SentrySpanExporter implements SpanExporter { ); private final @NotNull List attributeToRemoveByPrefix = - Arrays.asList("http.request.header."); + Arrays.asList("http.request.header.", "http.response.header."); private static final @NotNull Long SPAN_TIMEOUT = DateUtils.secondsToNanos(5 * 60); public static final String TRACE_ORIGIN = "auto.opentelemetry"; From badf8bab05b2ea70e04dd836c51465c2360ec213 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 25 Feb 2025 12:27:44 +0100 Subject: [PATCH 15/15] Apply suggestions from code review --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f840e580b1..95777567572 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ - Add HTTP server request headers from OpenTelemetry span attributes to sentry `request` in payload ([#4102](https://github.com/getsentry/sentry-java/pull/4102)) - You have to explicitly enable each header by adding it to the [OpenTelemetry config](https://opentelemetry.io/docs/zero-code/java/agent/instrumentation/http/#capturing-http-request-and-response-headers) - Please only enable headers you actually want to send to Sentry. Some may contain sensitive data like PII, cookies, tokens etc. - - We are no longer adding request headers to `contexts/otel/attributes` of the event. + - We are no longer adding request/response headers to `contexts/otel/attributes` of the event. - The `ignoredErrors` option is now configurable via the manifest property `io.sentry.traces.ignored-errors` ([#4178](https://github.com/getsentry/sentry-java/pull/4178)) - A list of active Spring profiles is attached to payloads sent to Sentry (errors, traces, etc.) and displayed in the UI when using our Spring or Spring Boot integrations ([#4147](https://github.com/getsentry/sentry-java/pull/4147)) - This consists of an empty list when only the default profile is active