Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public final class io/sentry/opentelemetry/SentrySpanProcessor : io/opentelemetr

public final class io/sentry/opentelemetry/SpanDescriptionExtractor {
public fun <init> ()V
public fun extractSpanInfo (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/opentelemetry/IOtelSpanWrapper;)Lio/sentry/opentelemetry/OtelSpanInfo;
public fun extractSpanInfo (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/opentelemetry/IOtelSpanWrapper;Lio/sentry/SentryOptions;)Lio/sentry/opentelemetry/OtelSpanInfo;
}

public final class io/sentry/opentelemetry/SpanNode {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.opentelemetry.sdk.trace.data.StatusData;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.semconv.HttpAttributes;
import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes;
import io.opentelemetry.semconv.incubating.ProcessIncubatingAttributes;
import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes;
import io.sentry.Baggage;
Expand Down Expand Up @@ -200,7 +201,7 @@ private void createAndFinishSpanForOtelSpan(
final @Nullable IOtelSpanWrapper sentrySpanMaybe =
spanStorage.getSentrySpan(spanData.getSpanContext());
final @NotNull OtelSpanInfo spanInfo =
spanDescriptionExtractor.extractSpanInfo(spanData, sentrySpanMaybe);
spanDescriptionExtractor.extractSpanInfo(spanData, sentrySpanMaybe, scopes.getOptions());

scopes
.getOptions()
Expand Down Expand Up @@ -294,7 +295,7 @@ private void transferSpanDetails(
final @NotNull IScopes scopesToUse =
scopesToUseBeforeForking.forkedCurrentScope("SentrySpanExporter.createTransaction");
final @NotNull OtelSpanInfo spanInfo =
spanDescriptionExtractor.extractSpanInfo(span, sentrySpanMaybe);
spanDescriptionExtractor.extractSpanInfo(span, sentrySpanMaybe, scopesToUse.getOptions());

scopesToUse
.getOptions()
Expand Down Expand Up @@ -361,6 +362,23 @@ private void transferSpanDetails(
maybeTransferOtelAttribute(span, sentryTransaction, ThreadIncubatingAttributes.THREAD_ID);
maybeTransferOtelAttribute(span, sentryTransaction, ThreadIncubatingAttributes.THREAD_NAME);

// Root transactions don't bulk-copy OTel attributes into span data (unlike child spans).
// The Sentry Queues product reads `trace.data.messaging.*`, so messaging attributes must
// be explicitly transferred for consumer root transactions to show up correctly. These are
// operational metadata (no payload contents) and are safe to transfer unconditionally.
maybeTransferOtelAttribute(
span, sentryTransaction, MessagingIncubatingAttributes.MESSAGING_SYSTEM);
maybeTransferOtelAttribute(
span, sentryTransaction, MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME);
maybeTransferOtelAttribute(
span, sentryTransaction, MessagingIncubatingAttributes.MESSAGING_OPERATION_TYPE);
maybeTransferOtelAttribute(
span, sentryTransaction, MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID);
maybeTransferOtelAttribute(
span, sentryTransaction, MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE);
maybeTransferOtelAttribute(
span, sentryTransaction, MessagingIncubatingAttributes.MESSAGING_MESSAGE_ENVELOPE_SIZE);

scopesToUse.configureScope(
ScopeType.CURRENT,
scope -> attributesExtractor.extract(span, scope, scopesToUse.getOptions()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ private boolean isSentryRequest(final @NotNull ReadableSpan otelSpan) {
private void updateTransactionWithOtelData(
final @NotNull ITransaction sentryTransaction, final @NotNull ReadableSpan otelSpan) {
final @NotNull OtelSpanInfo otelSpanInfo =
spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData(), null);
spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData(), null, scopes.getOptions());
sentryTransaction.setOperation(otelSpanInfo.getOp());
String transactionName = otelSpanInfo.getDescription();
sentryTransaction.setName(
Expand Down Expand Up @@ -334,7 +334,7 @@ private void updateSpanWithOtelData(
});

final @NotNull OtelSpanInfo otelSpanInfo =
spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData(), null);
spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData(), null, scopes.getOptions());
sentrySpan.setOperation(otelSpanInfo.getOp());
sentrySpan.setDescription(otelSpanInfo.getDescription());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import io.opentelemetry.semconv.UrlAttributes;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import io.opentelemetry.semconv.incubating.HttpIncubatingAttributes;
import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes;
import io.sentry.SentryOptions;
import io.sentry.protocol.TransactionNameSource;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
Expand All @@ -17,9 +19,19 @@ public final class SpanDescriptionExtractor {

@SuppressWarnings("deprecation")
public @NotNull OtelSpanInfo extractSpanInfo(
final @NotNull SpanData otelSpan, final @Nullable IOtelSpanWrapper sentrySpan) {
final @NotNull SpanData otelSpan,
final @Nullable IOtelSpanWrapper sentrySpan,
final @NotNull SentryOptions options) {
final @NotNull Attributes attributes = otelSpan.getAttributes();

if (options.isEnableQueueTracing()) {
final @Nullable String messagingSystem =
attributes.get(MessagingIncubatingAttributes.MESSAGING_SYSTEM);
if (messagingSystem != null) {
return descriptionForMessagingSystem(otelSpan);
}
}

final @Nullable String httpMethod = attributes.get(HttpAttributes.HTTP_REQUEST_METHOD);
if (httpMethod != null) {
return descriptionForHttpMethod(otelSpan, httpMethod);
Expand Down Expand Up @@ -91,6 +103,57 @@ private static boolean isRootSpan(SpanData otelSpan) {
return !otelSpan.getParentSpanContext().isValid() || otelSpan.getParentSpanContext().isRemote();
}

@SuppressWarnings("deprecation")
private OtelSpanInfo descriptionForMessagingSystem(final @NotNull SpanData otelSpan) {
final @NotNull Attributes attributes = otelSpan.getAttributes();
final @NotNull String op = opForMessaging(otelSpan);
final @Nullable String destination =
attributes.get(MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME);
final @NotNull String description = destination != null ? destination : otelSpan.getName();
return new OtelSpanInfo(op, description, TransactionNameSource.TASK);
}

@SuppressWarnings("deprecation")
private @NotNull String opForMessaging(final @NotNull SpanData otelSpan) {
final @NotNull Attributes attributes = otelSpan.getAttributes();
// Prefer `messaging.operation.type` (current OTel semconv), fall back to legacy
// `messaging.operation`. OTel's SpanKind.CONSUMER is overloaded for both `receive` and
// `process`, so attribute-first mapping is required. SpanKind is used only as a last resort.
@Nullable
String operationType = attributes.get(MessagingIncubatingAttributes.MESSAGING_OPERATION_TYPE);
if (operationType == null) {
operationType = attributes.get(MessagingIncubatingAttributes.MESSAGING_OPERATION);
}
if (operationType != null) {
switch (operationType) {
case "publish":
case "send":
return "queue.publish";
case "create":
return "queue.create";
case "receive":
return "queue.receive";
case "process":
case "deliver":
return "queue.process";
case "settle":
return "queue.settle";
default:
// fall through to SpanKind mapping
break;
}
}

final @NotNull SpanKind kind = otelSpan.getKind();
if (SpanKind.PRODUCER.equals(kind)) {
return "queue.publish";
}
if (SpanKind.CONSUMER.equals(kind)) {
return "queue.process";
}
return "queue";
}

@SuppressWarnings("deprecation")
private OtelSpanInfo descriptionForDbSystem(final @NotNull SpanData otelSpan) {
final @NotNull Attributes attributes = otelSpan.getAttributes();
Expand Down
Loading
Loading