From e94e24d50eac723b3853d6279833af445145c158 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Wed, 2 Jul 2025 16:31:55 +0200 Subject: [PATCH 1/4] Restore op and description from previous versions of OTel SDK --- .../SpanDescriptionExtractor.java | 18 ++++++++++++++++++ .../kotlin/SpanDescriptionExtractorTest.kt | 17 +++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java index b66555d68c9..056d40a5d6e 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java @@ -6,6 +6,7 @@ import io.opentelemetry.semconv.HttpAttributes; import io.opentelemetry.semconv.UrlAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; +import io.opentelemetry.semconv.incubating.GraphqlIncubatingAttributes; import io.opentelemetry.semconv.incubating.HttpIncubatingAttributes; import io.sentry.protocol.TransactionNameSource; import org.jetbrains.annotations.ApiStatus; @@ -30,6 +31,11 @@ public final class SpanDescriptionExtractor { return descriptionForDbSystem(otelSpan); } + final @Nullable String graphqlOperationType = attributes.get(GraphqlIncubatingAttributes.GRAPHQL_OPERATION_TYPE); + if (graphqlOperationType != null) { + return descriptionForGraphql(otelSpan); + } + final @NotNull String name = otelSpan.getName(); final @Nullable String maybeDescription = sentrySpan != null ? sentrySpan.getDescription() : name; @@ -105,4 +111,16 @@ private OtelSpanInfo descriptionForDbSystem(final @NotNull SpanData otelSpan) { return new OtelSpanInfo("db", otelSpan.getName(), TransactionNameSource.TASK); } + + private OtelSpanInfo descriptionForGraphql(final @NotNull SpanData otelSpan) { + final @NotNull Attributes attributes = otelSpan.getAttributes(); + @Nullable String graphqlOperationType = attributes.get(GraphqlIncubatingAttributes.GRAPHQL_OPERATION_TYPE); + @Nullable String graphqlOperationName = attributes.get(GraphqlIncubatingAttributes.GRAPHQL_OPERATION_NAME); + if (graphqlOperationType != null && graphqlOperationName != null) { + String description = graphqlOperationType + " " + graphqlOperationName; + return new OtelSpanInfo(description, description, TransactionNameSource.TASK); + } + + return new OtelSpanInfo(otelSpan.getName(), otelSpan.getName(), TransactionNameSource.TASK); + } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SpanDescriptionExtractorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SpanDescriptionExtractorTest.kt index af04914e278..726b027bda1 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SpanDescriptionExtractorTest.kt +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SpanDescriptionExtractorTest.kt @@ -10,6 +10,7 @@ import io.opentelemetry.sdk.trace.data.SpanData import io.opentelemetry.semconv.HttpAttributes import io.opentelemetry.semconv.UrlAttributes import io.opentelemetry.semconv.incubating.DbIncubatingAttributes +import io.opentelemetry.semconv.incubating.GraphqlIncubatingAttributes import io.opentelemetry.semconv.incubating.HttpIncubatingAttributes import io.sentry.protocol.TransactionNameSource import kotlin.test.Test @@ -253,6 +254,22 @@ class SpanDescriptionExtractorTest { assertEquals(TransactionNameSource.CUSTOM, info.transactionNameSource) } + @Test + fun `sets op to graphql for span with graphql operation type`() { + givenAttributes( + mapOf( + GraphqlIncubatingAttributes.GRAPHQL_OPERATION_TYPE to "query", + GraphqlIncubatingAttributes.GRAPHQL_OPERATION_NAME to "GreetingQuery", + ) + ) + + val info = whenExtractingSpanInfo() + + assertEquals("query GreetingQuery", info.op) + assertEquals("query GreetingQuery", info.description) + assertEquals(TransactionNameSource.TASK, info.transactionNameSource) + } + private fun createSpanContext( isRemote: Boolean, traceId: String = "f9118105af4a2d42b4124532cd1065ff", From 6ec8e1b9c612e3a2053bfe0f6bbdd3cb7301dfde Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Wed, 2 Jul 2025 14:51:30 +0000 Subject: [PATCH 2/4] Format code --- .../opentelemetry/SpanDescriptionExtractor.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java index 056d40a5d6e..9a55bfe5da7 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java @@ -31,7 +31,8 @@ public final class SpanDescriptionExtractor { return descriptionForDbSystem(otelSpan); } - final @Nullable String graphqlOperationType = attributes.get(GraphqlIncubatingAttributes.GRAPHQL_OPERATION_TYPE); + final @Nullable String graphqlOperationType = + attributes.get(GraphqlIncubatingAttributes.GRAPHQL_OPERATION_TYPE); if (graphqlOperationType != null) { return descriptionForGraphql(otelSpan); } @@ -114,8 +115,12 @@ private OtelSpanInfo descriptionForDbSystem(final @NotNull SpanData otelSpan) { private OtelSpanInfo descriptionForGraphql(final @NotNull SpanData otelSpan) { final @NotNull Attributes attributes = otelSpan.getAttributes(); - @Nullable String graphqlOperationType = attributes.get(GraphqlIncubatingAttributes.GRAPHQL_OPERATION_TYPE); - @Nullable String graphqlOperationName = attributes.get(GraphqlIncubatingAttributes.GRAPHQL_OPERATION_NAME); + @Nullable + String graphqlOperationType = + attributes.get(GraphqlIncubatingAttributes.GRAPHQL_OPERATION_TYPE); + @Nullable + String graphqlOperationName = + attributes.get(GraphqlIncubatingAttributes.GRAPHQL_OPERATION_NAME); if (graphqlOperationType != null && graphqlOperationName != null) { String description = graphqlOperationType + " " + graphqlOperationName; return new OtelSpanInfo(description, description, TransactionNameSource.TASK); From cfb4d8e0aef63fd9286ed7af8c42e1a211967720 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 3 Jul 2025 06:18:12 +0200 Subject: [PATCH 3/4] do not override span name if customized --- .../SpanDescriptionExtractor.java | 26 ++++++++++++++++--- .../kotlin/SpanDescriptionExtractorTest.kt | 17 ++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java index 9a55bfe5da7..8226a30b55e 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java @@ -13,12 +13,23 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; + @ApiStatus.Internal public final class SpanDescriptionExtractor { - @SuppressWarnings("deprecation") public @NotNull OtelSpanInfo extractSpanInfo( final @NotNull SpanData otelSpan, final @Nullable IOtelSpanWrapper sentrySpan) { + OtelSpanInfo spanInfo = extractSpanInfoInternal(otelSpan); + if (spanInfo != null) { + return spanInfo; + } + + return defaultInfo(otelSpan, sentrySpan); + } + + @SuppressWarnings("deprecation") + private @Nullable OtelSpanInfo extractSpanInfoInternal(final @NotNull SpanData otelSpan) { final @NotNull Attributes attributes = otelSpan.getAttributes(); final @Nullable String httpMethod = attributes.get(HttpAttributes.HTTP_REQUEST_METHOD); @@ -37,6 +48,10 @@ public final class SpanDescriptionExtractor { return descriptionForGraphql(otelSpan); } + return null; + } + + private @NotNull OtelSpanInfo defaultInfo(final @NotNull SpanData otelSpan, final @Nullable IOtelSpanWrapper sentrySpan) { final @NotNull String name = otelSpan.getName(); final @Nullable String maybeDescription = sentrySpan != null ? sentrySpan.getDescription() : name; @@ -113,7 +128,12 @@ private OtelSpanInfo descriptionForDbSystem(final @NotNull SpanData otelSpan) { return new OtelSpanInfo("db", otelSpan.getName(), TransactionNameSource.TASK); } - private OtelSpanInfo descriptionForGraphql(final @NotNull SpanData otelSpan) { + private @Nullable OtelSpanInfo descriptionForGraphql(final @NotNull SpanData otelSpan) { + final @NotNull String name = otelSpan.getName(); + // do not replace span name set by customer + if (!Arrays.asList("query", "mutation", "subscription").contains(name)) { + return null; + } final @NotNull Attributes attributes = otelSpan.getAttributes(); @Nullable String graphqlOperationType = @@ -126,6 +146,6 @@ private OtelSpanInfo descriptionForGraphql(final @NotNull SpanData otelSpan) { return new OtelSpanInfo(description, description, TransactionNameSource.TASK); } - return new OtelSpanInfo(otelSpan.getName(), otelSpan.getName(), TransactionNameSource.TASK); + return null; } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SpanDescriptionExtractorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SpanDescriptionExtractorTest.kt index 726b027bda1..fb5920b174d 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SpanDescriptionExtractorTest.kt +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SpanDescriptionExtractorTest.kt @@ -270,6 +270,23 @@ class SpanDescriptionExtractorTest { assertEquals(TransactionNameSource.TASK, info.transactionNameSource) } + @Test + fun `does not set op to graphql for span with graphql operation type if span name has been overridden`() { + givenAttributes( + mapOf( + GraphqlIncubatingAttributes.GRAPHQL_OPERATION_TYPE to "query", + GraphqlIncubatingAttributes.GRAPHQL_OPERATION_NAME to "GreetingQuery", + ) + ) + givenSpanName("my-custom-span-name") + + val info = whenExtractingSpanInfo() + + assertEquals("my-custom-span-name", info.op) + assertEquals("my-custom-span-name", info.description) + assertEquals(TransactionNameSource.CUSTOM, info.transactionNameSource) + } + private fun createSpanContext( isRemote: Boolean, traceId: String = "f9118105af4a2d42b4124532cd1065ff", From 8c0b509219ac0886e2162f9aea698d71bd39b041 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Thu, 3 Jul 2025 04:19:59 +0000 Subject: [PATCH 4/4] Format code --- .../io/sentry/opentelemetry/SpanDescriptionExtractor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java index 8226a30b55e..c9927ae6b3f 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java @@ -9,12 +9,11 @@ import io.opentelemetry.semconv.incubating.GraphqlIncubatingAttributes; import io.opentelemetry.semconv.incubating.HttpIncubatingAttributes; import io.sentry.protocol.TransactionNameSource; +import java.util.Arrays; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; - @ApiStatus.Internal public final class SpanDescriptionExtractor { @@ -51,7 +50,8 @@ public final class SpanDescriptionExtractor { return null; } - private @NotNull OtelSpanInfo defaultInfo(final @NotNull SpanData otelSpan, final @Nullable IOtelSpanWrapper sentrySpan) { + private @NotNull OtelSpanInfo defaultInfo( + final @NotNull SpanData otelSpan, final @Nullable IOtelSpanWrapper sentrySpan) { final @NotNull String name = otelSpan.getName(); final @Nullable String maybeDescription = sentrySpan != null ? sentrySpan.getDescription() : name;