Skip to content

Commit d5155a4

Browse files
authored
Merge cac8c1f into bbed2d2
2 parents bbed2d2 + cac8c1f commit d5155a4

8 files changed

Lines changed: 312 additions & 12 deletions

File tree

sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ public final class io/sentry/opentelemetry/SentrySpanProcessor : io/opentelemetr
149149

150150
public final class io/sentry/opentelemetry/SpanDescriptionExtractor {
151151
public fun <init> ()V
152-
public fun extractSpanInfo (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/opentelemetry/IOtelSpanWrapper;)Lio/sentry/opentelemetry/OtelSpanInfo;
152+
public fun extractSpanInfo (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/opentelemetry/IOtelSpanWrapper;Lio/sentry/SentryOptions;)Lio/sentry/opentelemetry/OtelSpanInfo;
153153
}
154154

155155
public final class io/sentry/opentelemetry/SpanNode {

sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import io.opentelemetry.sdk.trace.data.StatusData;
1313
import io.opentelemetry.sdk.trace.export.SpanExporter;
1414
import io.opentelemetry.semconv.HttpAttributes;
15+
import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes;
1516
import io.opentelemetry.semconv.incubating.ProcessIncubatingAttributes;
1617
import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes;
1718
import io.sentry.Baggage;
@@ -200,7 +201,7 @@ private void createAndFinishSpanForOtelSpan(
200201
final @Nullable IOtelSpanWrapper sentrySpanMaybe =
201202
spanStorage.getSentrySpan(spanData.getSpanContext());
202203
final @NotNull OtelSpanInfo spanInfo =
203-
spanDescriptionExtractor.extractSpanInfo(spanData, sentrySpanMaybe);
204+
spanDescriptionExtractor.extractSpanInfo(spanData, sentrySpanMaybe, scopes.getOptions());
204205

205206
scopes
206207
.getOptions()
@@ -294,7 +295,7 @@ private void transferSpanDetails(
294295
final @NotNull IScopes scopesToUse =
295296
scopesToUseBeforeForking.forkedCurrentScope("SentrySpanExporter.createTransaction");
296297
final @NotNull OtelSpanInfo spanInfo =
297-
spanDescriptionExtractor.extractSpanInfo(span, sentrySpanMaybe);
298+
spanDescriptionExtractor.extractSpanInfo(span, sentrySpanMaybe, scopesToUse.getOptions());
298299

299300
scopesToUse
300301
.getOptions()
@@ -361,6 +362,23 @@ private void transferSpanDetails(
361362
maybeTransferOtelAttribute(span, sentryTransaction, ThreadIncubatingAttributes.THREAD_ID);
362363
maybeTransferOtelAttribute(span, sentryTransaction, ThreadIncubatingAttributes.THREAD_NAME);
363364

365+
// Root transactions don't bulk-copy OTel attributes into span data (unlike child spans).
366+
// The Sentry Queues product reads `trace.data.messaging.*`, so messaging attributes must
367+
// be explicitly transferred for consumer root transactions to show up correctly. These are
368+
// operational metadata (no payload contents) and are safe to transfer unconditionally.
369+
maybeTransferOtelAttribute(
370+
span, sentryTransaction, MessagingIncubatingAttributes.MESSAGING_SYSTEM);
371+
maybeTransferOtelAttribute(
372+
span, sentryTransaction, MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME);
373+
maybeTransferOtelAttribute(
374+
span, sentryTransaction, MessagingIncubatingAttributes.MESSAGING_OPERATION_TYPE);
375+
maybeTransferOtelAttribute(
376+
span, sentryTransaction, MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID);
377+
maybeTransferOtelAttribute(
378+
span, sentryTransaction, MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE);
379+
maybeTransferOtelAttribute(
380+
span, sentryTransaction, MessagingIncubatingAttributes.MESSAGING_MESSAGE_ENVELOPE_SIZE);
381+
364382
scopesToUse.configureScope(
365383
ScopeType.CURRENT,
366384
scope -> attributesExtractor.extract(span, scope, scopesToUse.getOptions()));

sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ private boolean isSentryRequest(final @NotNull ReadableSpan otelSpan) {
297297
private void updateTransactionWithOtelData(
298298
final @NotNull ITransaction sentryTransaction, final @NotNull ReadableSpan otelSpan) {
299299
final @NotNull OtelSpanInfo otelSpanInfo =
300-
spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData(), null);
300+
spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData(), null, scopes.getOptions());
301301
sentryTransaction.setOperation(otelSpanInfo.getOp());
302302
String transactionName = otelSpanInfo.getDescription();
303303
sentryTransaction.setName(
@@ -334,7 +334,7 @@ private void updateSpanWithOtelData(
334334
});
335335

336336
final @NotNull OtelSpanInfo otelSpanInfo =
337-
spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData(), null);
337+
spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData(), null, scopes.getOptions());
338338
sentrySpan.setOperation(otelSpanInfo.getOp());
339339
sentrySpan.setDescription(otelSpanInfo.getDescription());
340340
}

sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import io.opentelemetry.semconv.UrlAttributes;
88
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
99
import io.opentelemetry.semconv.incubating.HttpIncubatingAttributes;
10+
import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes;
11+
import io.sentry.SentryOptions;
1012
import io.sentry.protocol.TransactionNameSource;
1113
import org.jetbrains.annotations.ApiStatus;
1214
import org.jetbrains.annotations.NotNull;
@@ -17,9 +19,19 @@ public final class SpanDescriptionExtractor {
1719

1820
@SuppressWarnings("deprecation")
1921
public @NotNull OtelSpanInfo extractSpanInfo(
20-
final @NotNull SpanData otelSpan, final @Nullable IOtelSpanWrapper sentrySpan) {
22+
final @NotNull SpanData otelSpan,
23+
final @Nullable IOtelSpanWrapper sentrySpan,
24+
final @NotNull SentryOptions options) {
2125
final @NotNull Attributes attributes = otelSpan.getAttributes();
2226

27+
if (options.isEnableQueueTracing()) {
28+
final @Nullable String messagingSystem =
29+
attributes.get(MessagingIncubatingAttributes.MESSAGING_SYSTEM);
30+
if (messagingSystem != null) {
31+
return descriptionForMessagingSystem(otelSpan);
32+
}
33+
}
34+
2335
final @Nullable String httpMethod = attributes.get(HttpAttributes.HTTP_REQUEST_METHOD);
2436
if (httpMethod != null) {
2537
return descriptionForHttpMethod(otelSpan, httpMethod);
@@ -91,6 +103,57 @@ private static boolean isRootSpan(SpanData otelSpan) {
91103
return !otelSpan.getParentSpanContext().isValid() || otelSpan.getParentSpanContext().isRemote();
92104
}
93105

106+
@SuppressWarnings("deprecation")
107+
private OtelSpanInfo descriptionForMessagingSystem(final @NotNull SpanData otelSpan) {
108+
final @NotNull Attributes attributes = otelSpan.getAttributes();
109+
final @NotNull String op = opForMessaging(otelSpan);
110+
final @Nullable String destination =
111+
attributes.get(MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME);
112+
final @NotNull String description = destination != null ? destination : otelSpan.getName();
113+
return new OtelSpanInfo(op, description, TransactionNameSource.TASK);
114+
}
115+
116+
@SuppressWarnings("deprecation")
117+
private @NotNull String opForMessaging(final @NotNull SpanData otelSpan) {
118+
final @NotNull Attributes attributes = otelSpan.getAttributes();
119+
// Prefer `messaging.operation.type` (current OTel semconv), fall back to legacy
120+
// `messaging.operation`. OTel's SpanKind.CONSUMER is overloaded for both `receive` and
121+
// `process`, so attribute-first mapping is required. SpanKind is used only as a last resort.
122+
@Nullable
123+
String operationType = attributes.get(MessagingIncubatingAttributes.MESSAGING_OPERATION_TYPE);
124+
if (operationType == null) {
125+
operationType = attributes.get(MessagingIncubatingAttributes.MESSAGING_OPERATION);
126+
}
127+
if (operationType != null) {
128+
switch (operationType) {
129+
case "publish":
130+
case "send":
131+
return "queue.publish";
132+
case "create":
133+
return "queue.create";
134+
case "receive":
135+
return "queue.receive";
136+
case "process":
137+
case "deliver":
138+
return "queue.process";
139+
case "settle":
140+
return "queue.settle";
141+
default:
142+
// fall through to SpanKind mapping
143+
break;
144+
}
145+
}
146+
147+
final @NotNull SpanKind kind = otelSpan.getKind();
148+
if (SpanKind.PRODUCER.equals(kind)) {
149+
return "queue.publish";
150+
}
151+
if (SpanKind.CONSUMER.equals(kind)) {
152+
return "queue.process";
153+
}
154+
return "queue";
155+
}
156+
94157
@SuppressWarnings("deprecation")
95158
private OtelSpanInfo descriptionForDbSystem(final @NotNull SpanData otelSpan) {
96159
final @NotNull Attributes attributes = otelSpan.getAttributes();

0 commit comments

Comments
 (0)