diff --git a/CHANGELOG.md b/CHANGELOG.md index 0842780ca9..8f2bf98311 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixes - Fix `Gpu.vendorId` should be a String ([#2343](https://github.com/getsentry/sentry-java/pull/2343)) +- Do not attach empty `sentry-trace` and `baggage` headers ([#2385](https://github.com/getsentry/sentry-java/pull/2385)) ### Features diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index 4141ad9837..bbacd3150c 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -200,6 +200,7 @@ object Config { val SENTRY_SPRING_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring" val SENTRY_SPRING_BOOT_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring-boot" val SENTRY_SPRING_BOOT_JAKARTA_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring-boot.jakarta" + val SENTRY_OPENTELEMETRY_AGENT_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.opentelemetry.agent" val group = "io.sentry" val description = "SDK for sentry.io" val versionNameProp = "versionName" diff --git a/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt b/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt index 13fbe99dff..5cb9da2183 100644 --- a/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt +++ b/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt @@ -64,7 +64,7 @@ class SentryOkHttpInterceptor( var code: Int? = null try { val requestBuilder = request.newBuilder() - if (span != null && + if (span != null && !span.isNoOp && PropagationTargetsUtils.contain(hub.options.tracePropagationTargets, request.url.toString()) ) { span.toSentryTrace().let { diff --git a/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt b/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt index e04d1a4071..a3615d3289 100644 --- a/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt +++ b/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt @@ -33,7 +33,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor(private val hub: IH var cleanedHeaders = removeSentryInternalHeaders(request.headers).toMutableList() - if (PropagationTargetsUtils.contain(hub.options.tracePropagationTargets, request.url)) { + if (!span.isNoOp && PropagationTargetsUtils.contain(hub.options.tracePropagationTargets, request.url)) { val sentryTraceHeader = span.toSentryTrace() val baggageHeader = span.toBaggageHeader(request.headers.filter { it.name == BaggageHeader.BAGGAGE_HEADER }.map { it.value }) cleanedHeaders.add(HttpHeader(sentryTraceHeader.name, sentryTraceHeader.value)) diff --git a/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt b/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt index cd61d5367b..1ccb55ba5d 100644 --- a/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt +++ b/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt @@ -37,16 +37,23 @@ class SentryApolloInterceptor( chain.proceedAsync(request, dispatcher, callBack) } else { val span = startChild(request, activeSpan) - val sentryTraceHeader = span.toSentryTrace() - // we have no access to URI, no way to verify tracing origins - val requestHeaderBuilder = request.requestHeaders.toBuilder() - requestHeaderBuilder.addHeader(sentryTraceHeader.name, sentryTraceHeader.value) - span.toBaggageHeader(listOf(request.requestHeaders.headerValue(BaggageHeader.BAGGAGE_HEADER)))?.let { - requestHeaderBuilder.addHeader(it.name, it.value) + val requestWithHeader = if (span.isNoOp) { + request + } else { + val sentryTraceHeader = span.toSentryTrace() + + // we have no access to URI, no way to verify tracing origins + val requestHeaderBuilder = request.requestHeaders.toBuilder() + requestHeaderBuilder.addHeader(sentryTraceHeader.name, sentryTraceHeader.value) + span.toBaggageHeader(listOf(request.requestHeaders.headerValue(BaggageHeader.BAGGAGE_HEADER))) + ?.let { + requestHeaderBuilder.addHeader(it.name, it.value) + } + val headers = requestHeaderBuilder.build() + request.toBuilder().requestHeaders(headers).build() } - val headers = requestHeaderBuilder.build() - val requestWithHeader = request.toBuilder().requestHeaders(headers).build() + span.setData("operationId", requestWithHeader.operation.operationId()) span.setData("variables", requestWithHeader.operation.variables().valueMap().toString()) diff --git a/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java b/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java index b85af52774..f8062a097e 100644 --- a/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java +++ b/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java @@ -30,7 +30,7 @@ public SentryJdbcEventListener() { @Override public void onBeforeAnyExecute(final @NotNull StatementInformation statementInformation) { final ISpan parent = hub.getSpan(); - if (parent != null) { + if (parent != null && !parent.isNoOp()) { final ISpan span = parent.startChild("db.query", statementInformation.getSql()); CURRENT_SPAN.set(span); } diff --git a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java index 01c43c739b..c25feb57fa 100644 --- a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java +++ b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java @@ -55,7 +55,8 @@ public Response execute(final @NotNull Request request, final @NotNull Request.O final RequestWrapper requestWrapper = new RequestWrapper(request); - if (PropagationTargetsUtils.contain(hub.getOptions().getTracePropagationTargets(), url)) { + if (!span.isNoOp() + && PropagationTargetsUtils.contain(hub.getOptions().getTracePropagationTargets(), url)) { final SentryTraceHeader sentryTraceHeader = span.toSentryTrace(); final @Nullable Collection requestBaggageHeader = request.headers().get(BaggageHeader.BAGGAGE_HEADER); diff --git a/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts index ed0f93dace..495e200f24 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts @@ -145,8 +145,12 @@ tasks { attributes.put("Premain-Class", "io.opentelemetry.javaagent.OpenTelemetryAgent") attributes.put("Can-Redefine-Classes", "true") attributes.put("Can-Retransform-Classes", "true") - attributes.put("Implementation-Vendor", "Demo") - attributes.put("Implementation-Version", "demo-${project.version}-otel-${Config.Libs.otelJavaagentVersion}") + attributes.put("Implementation-Vendor", "Sentry") + attributes.put("Implementation-Version", "sentry-${project.version}-otel-${Config.Libs.otelJavaagentVersion}") + attributes.put("Sentry-Version-Name", project.version) + attributes.put("Sentry-Opentelemetry-SDK-Name", Config.Sentry.SENTRY_OPENTELEMETRY_AGENT_SDK_NAME) + attributes.put("Sentry-Opentelemetry-Version-Name", Config.Libs.otelVersion) + attributes.put("Sentry-Opentelemetry-Javaagent-Version-Name", Config.Libs.otelJavaagentVersion) } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/api/sentry-opentelemetry-agentcustomization.api b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/api/sentry-opentelemetry-agentcustomization.api index edc0693040..342f71b5bb 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/api/sentry-opentelemetry-agentcustomization.api +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/api/sentry-opentelemetry-agentcustomization.api @@ -8,3 +8,9 @@ public final class io/sentry/opentelemetry/SentryBootstrapPackagesProvider : io/ public fun configure (Lio/opentelemetry/javaagent/tooling/bootstrap/BootstrapPackagesBuilder;Lio/opentelemetry/sdk/autoconfigure/spi/ConfigProperties;)V } +public final class io/sentry/opentelemetry/SentryPropagatorProvider : io/opentelemetry/sdk/autoconfigure/spi/ConfigurablePropagatorProvider { + public fun ()V + public fun getName ()Ljava/lang/String; + public fun getPropagator (Lio/opentelemetry/sdk/autoconfigure/spi/ConfigProperties;)Lio/opentelemetry/context/propagation/TextMapPropagator; +} + diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java index 83b8d52635..9058ee7855 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java @@ -4,28 +4,97 @@ import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; +import io.sentry.Instrumenter; +import io.sentry.Sentry; +import io.sentry.SentryOptions; +import io.sentry.protocol.SdkVersion; +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public final class SentryAutoConfigurationCustomizerProvider implements AutoConfigurationCustomizerProvider { @Override public void customize(AutoConfigurationCustomizer autoConfiguration) { - autoConfiguration.addTracerProviderCustomizer(this::configureSdkTracerProvider); - // .addPropertiesSupplier(this::getDefaultProperties); + final @Nullable String sentryPropertiesFile = System.getenv("SENTRY_PROPERTIES_FILE"); + final @Nullable String sentryDsn = System.getenv("SENTRY_DSN"); + + if (sentryPropertiesFile != null || sentryDsn != null) { + Sentry.init( + options -> { + options.setEnableExternalConfiguration(true); + options.setInstrumenter(Instrumenter.OTEL); + final @Nullable SdkVersion sdkVersion = createSdkVersion(options); + if (sdkVersion != null) { + options.setSdkVersion(sdkVersion); + } + }); + } + + autoConfiguration + .addTracerProviderCustomizer(this::configureSdkTracerProvider) + .addPropertiesSupplier(this::getDefaultProperties); + } + + private @Nullable SdkVersion createSdkVersion(final @NotNull SentryOptions sentryOptions) { + SdkVersion sdkVersion = sentryOptions.getSdkVersion(); + + try { + final @NotNull Enumeration resources = + ClassLoader.getSystemClassLoader().getResources("META-INF/MANIFEST.MF"); + while (resources.hasMoreElements()) { + try { + final @NotNull Manifest manifest = new Manifest(resources.nextElement().openStream()); + final @Nullable Attributes mainAttributes = manifest.getMainAttributes(); + if (mainAttributes != null) { + final @Nullable String name = mainAttributes.getValue("Sentry-Opentelemetry-SDK-Name"); + final @Nullable String version = mainAttributes.getValue("Sentry-Version-Name"); + + if (name != null && version != null) { + sdkVersion = SdkVersion.updateSdkVersion(sdkVersion, name, version); + sdkVersion.addPackage("maven:io.sentry:sentry-opentelemetry-agent", version); + final @Nullable String otelVersion = + mainAttributes.getValue("Sentry-Opentelemetry-Version-Name"); + if (otelVersion != null) { + sdkVersion.addPackage("maven:io.opentelemetry:opentelemetry-sdk", otelVersion); + } + final @Nullable String otelJavaagentVersion = + mainAttributes.getValue("Sentry-Opentelemetry-Javaagent-Version-Name"); + if (otelJavaagentVersion != null) { + sdkVersion.addPackage( + "maven:io.opentelemetry.javaagent:opentelemetry-javaagent", + otelJavaagentVersion); + } + } + } + } catch (Exception e) { + // ignore + } + } + } catch (IOException e) { + // ignore + } + + return sdkVersion; } private SdkTracerProviderBuilder configureSdkTracerProvider( SdkTracerProviderBuilder tracerProvider, ConfigProperties config) { - return tracerProvider.addSpanProcessor(new SentrySpanProcessor()); } - // private Map getDefaultProperties() { - // Map properties = new HashMap<>(); - // properties.put("otel.exporter.otlp.endpoint", "http://backend:8080"); - // properties.put("otel.exporter.otlp.insecure", "true"); - // properties.put("otel.config.max.attrs", "16"); - // properties.put("otel.traces.sampler", "demo"); - // return properties; - // } + private Map getDefaultProperties() { + Map properties = new HashMap<>(); + properties.put("otel.traces.exporter", "none"); + properties.put("otel.metrics.exporter", "none"); + properties.put("otel.propagators", "sentry"); + return properties; + } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java new file mode 100644 index 0000000000..49acd725fb --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java @@ -0,0 +1,17 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider; + +public final class SentryPropagatorProvider implements ConfigurablePropagatorProvider { + @Override + public TextMapPropagator getPropagator(ConfigProperties config) { + return new SentryPropagator(); + } + + @Override + public String getName() { + return "sentry"; + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider new file mode 100644 index 0000000000..d29e01096b --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider @@ -0,0 +1 @@ +io.sentry.opentelemetry.SentryPropagatorProvider 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 3e870faea6..2347a99db7 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,23 @@ +public final class io/sentry/opentelemetry/OtelSpanInfo { + public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V + public fun getDescription ()Ljava/lang/String; + public fun getOp ()Ljava/lang/String; + public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; +} + +public final class io/sentry/opentelemetry/SentryOtelKeys { + public static final field SENTRY_BAGGAGE_KEY Lio/opentelemetry/context/ContextKey; + public static final field SENTRY_TRACE_KEY Lio/opentelemetry/context/ContextKey; + public fun ()V +} + +public final class io/sentry/opentelemetry/SentryPropagator : io/opentelemetry/context/propagation/TextMapPropagator { + public fun ()V + public fun extract (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapGetter;)Lio/opentelemetry/context/Context; + public fun fields ()Ljava/util/Collection; + public fun inject (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapSetter;)V +} + public final class io/sentry/opentelemetry/SentrySpanProcessor : io/opentelemetry/sdk/trace/SpanProcessor { public fun ()V public fun isEndRequired ()Z @@ -6,21 +26,23 @@ public final class io/sentry/opentelemetry/SentrySpanProcessor : io/opentelemetr public fun onStart (Lio/opentelemetry/context/Context;Lio/opentelemetry/sdk/trace/ReadWriteSpan;)V } -public final class io/sentry/opentelemetry/SpanDescription { - public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V - public fun getDescription ()Ljava/lang/String; - public fun getOp ()Ljava/lang/String; - public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; +public final class io/sentry/opentelemetry/SentrySpanStorage { + public fun get (Ljava/lang/String;)Lio/sentry/ISpan; + public static fun getInstance ()Lio/sentry/opentelemetry/SentrySpanStorage; + public fun removeAndGet (Ljava/lang/String;)Lio/sentry/ISpan; + public fun store (Ljava/lang/String;Lio/sentry/ISpan;)V } public final class io/sentry/opentelemetry/SpanDescriptionExtractor { public fun ()V - public fun extractSpanDescription (Lio/opentelemetry/sdk/trace/ReadableSpan;)Lio/sentry/opentelemetry/SpanDescription; + public fun extractSpanDescription (Lio/opentelemetry/sdk/trace/ReadableSpan;)Lio/sentry/opentelemetry/OtelSpanInfo; } public final class io/sentry/opentelemetry/TraceData { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryTraceHeader;Lio/sentry/Baggage;)V + public fun getBaggage ()Lio/sentry/Baggage; public fun getParentSpanId ()Ljava/lang/String; + public fun getSentryTraceHeader ()Lio/sentry/SentryTraceHeader; public fun getSpanId ()Ljava/lang/String; public fun getTraceId ()Ljava/lang/String; } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescription.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java similarity index 92% rename from sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescription.java rename to sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java index 4b5e33ac20..6ad39d3793 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescription.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java @@ -5,13 +5,13 @@ import org.jetbrains.annotations.NotNull; @ApiStatus.Internal -public final class SpanDescription { +public final class OtelSpanInfo { private final @NotNull String op; private final @NotNull String description; private final @NotNull TransactionNameSource transactionNameSource; - public SpanDescription( + public OtelSpanInfo( final @NotNull String op, final @NotNull String description, final @NotNull TransactionNameSource transactionNameSource) { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java new file mode 100644 index 0000000000..51ead00c6f --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java @@ -0,0 +1,16 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.context.ContextKey; +import io.sentry.Baggage; +import io.sentry.SentryTraceHeader; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +@ApiStatus.Internal +public final class SentryOtelKeys { + + public static final @NotNull ContextKey SENTRY_TRACE_KEY = + ContextKey.named("sentry.trace"); + public static final @NotNull ContextKey SENTRY_BAGGAGE_KEY = + ContextKey.named("sentry.baggage"); +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java new file mode 100644 index 0000000000..85ba4b3b30 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java @@ -0,0 +1,89 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.propagation.TextMapGetter; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.context.propagation.TextMapSetter; +import io.sentry.Baggage; +import io.sentry.BaggageHeader; +import io.sentry.ISpan; +import io.sentry.SentryTraceHeader; +import io.sentry.exception.InvalidSentryTraceHeaderException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class SentryPropagator implements TextMapPropagator { + + private static final @NotNull List FIELDS = + Arrays.asList(SentryTraceHeader.SENTRY_TRACE_HEADER, BaggageHeader.BAGGAGE_HEADER); + private final @NotNull SentrySpanStorage spanStorage = SentrySpanStorage.getInstance(); + + @Override + public Collection fields() { + return FIELDS; + } + + @Override + public void inject(final Context context, final C carrier, final TextMapSetter setter) { + final @NotNull Span otelSpan = Span.fromContext(context); + final @NotNull SpanContext otelSpanContext = otelSpan.getSpanContext(); + if (!otelSpanContext.isValid()) { + return; + } + final @Nullable ISpan sentrySpan = spanStorage.get(otelSpanContext.getSpanId()); + if (sentrySpan == null || sentrySpan.isNoOp()) { + return; + } + + final @NotNull SentryTraceHeader sentryTraceHeader = sentrySpan.toSentryTrace(); + setter.set(carrier, sentryTraceHeader.getName(), sentryTraceHeader.getValue()); + final @Nullable BaggageHeader baggageHeader = + sentrySpan.toBaggageHeader(Collections.emptyList()); + if (baggageHeader != null) { + setter.set(carrier, baggageHeader.getName(), baggageHeader.getValue()); + } + } + + @Override + public Context extract( + final Context context, final C carrier, final TextMapGetter getter) { + final @Nullable String sentryTraceString = + getter.get(carrier, SentryTraceHeader.SENTRY_TRACE_HEADER); + if (sentryTraceString == null) { + return context; + } + + try { + SentryTraceHeader sentryTraceHeader = new SentryTraceHeader(sentryTraceString); + + SpanContext otelSpanContext = + SpanContext.createFromRemoteParent( + sentryTraceHeader.getTraceId().toString(), + sentryTraceHeader.getSpanId().toString(), + TraceFlags.getSampled(), + TraceState.getDefault()); + + @NotNull + Context modifiedContext = context.with(SentryOtelKeys.SENTRY_TRACE_KEY, sentryTraceHeader); + + final @Nullable String baggageString = getter.get(carrier, BaggageHeader.BAGGAGE_HEADER); + Baggage baggage = Baggage.fromHeader(baggageString); + modifiedContext = modifiedContext.with(SentryOtelKeys.SENTRY_BAGGAGE_KEY, baggage); + + Span wrappedSpan = Span.wrap(otelSpanContext); + modifiedContext = modifiedContext.with(wrappedSpan); + + return modifiedContext; + } catch (InvalidSentryTraceHeaderException e) { + return context; + } + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java index fd1e73f6d5..f02d7db9c3 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java @@ -1,5 +1,6 @@ package io.sentry.opentelemetry; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.StatusCode; @@ -10,12 +11,14 @@ import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import io.sentry.Baggage; import io.sentry.DateUtils; import io.sentry.DsnUtil; import io.sentry.ISpan; import io.sentry.ITransaction; import io.sentry.Instrumenter; import io.sentry.Sentry; +import io.sentry.SentryTraceHeader; import io.sentry.SpanId; import io.sentry.SpanStatus; import io.sentry.TransactionContext; @@ -27,30 +30,20 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public final class SentrySpanProcessor implements SpanProcessor { - private final @NotNull Map spans = new ConcurrentHashMap<>(); private final @NotNull List spanKindsConsideredForSentryRequests = Arrays.asList(SpanKind.CLIENT, SpanKind.INTERNAL); private final @NotNull SpanDescriptionExtractor spanDescriptionExtractor = new SpanDescriptionExtractor(); + private final @NotNull SentrySpanStorage spanStorage = SentrySpanStorage.getInstance(); @Override public void onStart(final @NotNull Context parentContext, final @NotNull ReadWriteSpan otelSpan) { - if (!hasSentryBeenInitialized()) { - return; - } - - if (!Instrumenter.OTEL.equals(Sentry.getCurrentHub().getOptions().getInstrumenter())) { - return; - } - - final @NotNull SpanContext otelSpanContext = otelSpan.getSpanContext(); - if (!otelSpanContext.isValid()) { + if (!ensurePrerequisites(otelSpan)) { return; } @@ -58,29 +51,42 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri return; } - final @NotNull TraceData traceData = getTraceData(otelSpan); + final @NotNull TraceData traceData = getTraceData(otelSpan, parentContext); final @Nullable ISpan sentryParentSpan = - traceData.getParentSpanId() == null ? null : spans.get(traceData.getParentSpanId()); + traceData.getParentSpanId() == null ? null : spanStorage.get(traceData.getParentSpanId()); if (sentryParentSpan != null) { - System.out.println("found a parent span: " + traceData.getParentSpanId()); final @NotNull Date startDate = DateUtils.nanosToDate(otelSpan.toSpanData().getStartEpochNanos()); final @NotNull ISpan sentryChildSpan = sentryParentSpan.startChild( otelSpan.getName(), otelSpan.getName(), startDate, Instrumenter.OTEL); - spans.put(traceData.getSpanId(), sentryChildSpan); + spanStorage.store(traceData.getSpanId(), sentryChildSpan); } else { - TransactionContext transactionContext = - new TransactionContext( - otelSpan.getName(), - otelSpan.getName(), - new SentryId(traceData.getTraceId()), - new SpanId(traceData.getSpanId()), - TransactionNameSource.CUSTOM, - null, - null, - null); + final @NotNull String transactionName = otelSpan.getName(); + final @NotNull TransactionNameSource transactionNameSource = TransactionNameSource.CUSTOM; + final @Nullable String op = otelSpan.getName(); + final SpanId spanId = new SpanId(traceData.getSpanId()); + + final @NotNull TransactionContext transactionContext = + traceData.getSentryTraceHeader() == null + ? new TransactionContext( + transactionName, + op, + new SentryId(traceData.getTraceId()), + spanId, + transactionNameSource, + null, + null, + null) + : TransactionContext.fromSentryTrace( + transactionName, + transactionNameSource, + op, + traceData.getSentryTraceHeader(), + traceData.getBaggage(), + spanId); + ; transactionContext.setInstrumenter(Instrumenter.OTEL); TransactionOptions transactionOptions = new TransactionOptions(); @@ -88,7 +94,7 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri DateUtils.nanosToDate(otelSpan.toSpanData().getStartEpochNanos())); ISpan sentryTransaction = Sentry.startTransaction(transactionContext, transactionOptions); - spans.put(traceData.getSpanId(), sentryTransaction); + spanStorage.store(traceData.getSpanId(), sentryTransaction); } } @@ -99,21 +105,12 @@ public boolean isStartRequired() { @Override public void onEnd(final @NotNull ReadableSpan otelSpan) { - if (!hasSentryBeenInitialized()) { - return; - } - - if (!Instrumenter.OTEL.equals(Sentry.getCurrentHub().getOptions().getInstrumenter())) { + if (!ensurePrerequisites(otelSpan)) { return; } - final @NotNull SpanContext otelSpanContext = otelSpan.getSpanContext(); - if (!otelSpanContext.isValid()) { - return; - } - - final @NotNull TraceData traceData = getTraceData(otelSpan); - final @Nullable ISpan sentrySpan = spans.remove(traceData.getSpanId()); + final @NotNull TraceData traceData = getTraceData(otelSpan, null); + final @Nullable ISpan sentrySpan = spanStorage.removeAndGet(traceData.getSpanId()); if (sentrySpan == null) { return; @@ -141,7 +138,35 @@ public boolean isEndRequired() { return true; } - private @NotNull TraceData getTraceData(final @NotNull ReadableSpan otelSpan) { + private boolean ensurePrerequisites(final @NotNull ReadableSpan otelSpan) { + if (!hasSentryBeenInitialized()) { + return false; + } + + if (!Instrumenter.OTEL.equals(Sentry.getCurrentHub().getOptions().getInstrumenter())) { + return false; + } + + final @NotNull SpanContext otelSpanContext = otelSpan.getSpanContext(); + if (!otelSpanContext.isValid()) { + return false; + } + + return true; + } + + private boolean isSentryRequest(final @NotNull ReadableSpan otelSpan) { + final @NotNull SpanKind kind = otelSpan.getKind(); + if (!spanKindsConsideredForSentryRequests.contains(kind)) { + return false; + } + + final @Nullable String httpUrl = otelSpan.getAttribute(SemanticAttributes.HTTP_URL); + return DsnUtil.urlContainsDsnHost(Sentry.getCurrentHub().getOptions(), httpUrl); + } + + private @NotNull TraceData getTraceData( + final @NotNull ReadableSpan otelSpan, final @Nullable Context parentContext) { final @NotNull SpanContext otelSpanContext = otelSpan.getSpanContext(); final @NotNull String otelSpanId = otelSpanContext.getSpanId(); final @NotNull String otelParentSpanIdMaybeInvalid = @@ -152,40 +177,37 @@ public boolean isEndRequired() { ? otelParentSpanIdMaybeInvalid : null; - // TODO read parentSampled and baggage from context set by propagator - - return new TraceData(otelTraceId, otelSpanId, otelParentSpanId); - } + @Nullable SentryTraceHeader sentryTraceHeader = null; + @Nullable Baggage baggage = null; - private boolean isSentryRequest(final @NotNull ReadableSpan otelSpan) { - final @NotNull SpanKind kind = otelSpan.getKind(); - if (!spanKindsConsideredForSentryRequests.contains(kind)) { - return false; + if (parentContext != null) { + sentryTraceHeader = parentContext.get(SentryOtelKeys.SENTRY_TRACE_KEY); + if (sentryTraceHeader != null) { + baggage = parentContext.get(SentryOtelKeys.SENTRY_BAGGAGE_KEY); + } } - final @Nullable String httpUrl = otelSpan.getAttribute(SemanticAttributes.HTTP_URL); - return DsnUtil.urlContainsDsnHost(Sentry.getCurrentHub().getOptions(), httpUrl); + return new TraceData(otelTraceId, otelSpanId, otelParentSpanId, sentryTraceHeader, baggage); } private void updateTransactionWithOtelData( final @NotNull ITransaction sentryTransaction, final @NotNull ReadableSpan otelSpan) { - final @NotNull SpanDescription spanDescription = + final @NotNull OtelSpanInfo otelSpanInfo = spanDescriptionExtractor.extractSpanDescription(otelSpan); - sentryTransaction.setOperation(spanDescription.getOp()); + sentryTransaction.setOperation(otelSpanInfo.getOp()); sentryTransaction.setName( - spanDescription.getDescription(), spanDescription.getTransactionNameSource()); + otelSpanInfo.getDescription(), otelSpanInfo.getTransactionNameSource()); final @NotNull Map otelContext = toOtelContext(otelSpan); - System.out.println(otelContext); - // TODO set otel context on transaction + sentryTransaction.setContext("otel", otelContext); } private @NotNull Map toOtelContext(final @NotNull ReadableSpan otelSpan) { final @NotNull SpanData spanData = otelSpan.toSpanData(); final @NotNull Map context = new HashMap<>(); - context.put("attributes", spanData.getAttributes().asMap()); - context.put("resource", spanData.getResource().getAttributes().asMap()); + context.put("attributes", toMapWithStringKeys(spanData.getAttributes())); + context.put("resource", toMapWithStringKeys(spanData.getResource().getAttributes())); return context; } @@ -205,10 +227,10 @@ private void updateSpanWithOtelData( } }); - final @NotNull SpanDescription spanDescription = + final @NotNull OtelSpanInfo otelSpanInfo = spanDescriptionExtractor.extractSpanDescription(otelSpan); - sentrySpan.setOperation(spanDescription.getOp()); - sentrySpan.setDescription(spanDescription.getDescription()); + sentrySpan.setOperation(otelSpanInfo.getOp()); + sentrySpan.setDescription(otelSpanInfo.getDescription()); } private SpanStatus mapOtelStatus(final @NotNull ReadableSpan otelSpan) { @@ -234,4 +256,19 @@ private SpanStatus mapOtelStatus(final @NotNull ReadableSpan otelSpan) { private boolean hasSentryBeenInitialized() { return Sentry.isEnabled(); } + + private @NotNull Map toMapWithStringKeys(final @Nullable Attributes attributes) { + final @NotNull Map mapWithStringKeys = new HashMap<>(); + + if (attributes != null) { + attributes.forEach( + (key, value) -> { + if (key != null) { + mapWithStringKeys.put(key.getKey(), value); + } + }); + } + + return mapWithStringKeys; + } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanStorage.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanStorage.java new file mode 100644 index 0000000000..9898fc5c4c --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanStorage.java @@ -0,0 +1,41 @@ +package io.sentry.opentelemetry; + +import io.sentry.ISpan; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Internal +public final class SentrySpanStorage { + private static volatile @Nullable SentrySpanStorage INSTANCE; + + public static @NotNull SentrySpanStorage getInstance() { + if (INSTANCE == null) { + synchronized (SentrySpanStorage.class) { + if (INSTANCE == null) { + INSTANCE = new SentrySpanStorage(); + } + } + } + + return INSTANCE; + } + + private final @NotNull Map spans = new ConcurrentHashMap<>(); + + private SentrySpanStorage() {} + + public void store(final @NotNull String spanId, final @NotNull ISpan span) { + spans.put(spanId, span); + } + + public @Nullable ISpan get(final @Nullable String spanId) { + return spans.get(spanId); + } + + public @Nullable ISpan removeAndGet(final @Nullable String spanId) { + return spans.remove(spanId); + } +} 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 1fc16130e8..06fa945402 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 @@ -11,7 +11,7 @@ @ApiStatus.Internal public final class SpanDescriptionExtractor { - public @NotNull SpanDescription extractSpanDescription(final @NotNull ReadableSpan otelSpan) { + public @NotNull OtelSpanInfo extractSpanDescription(final @NotNull ReadableSpan otelSpan) { final @NotNull String name = otelSpan.getName(); final @Nullable String httpMethod = otelSpan.getAttribute(SemanticAttributes.HTTP_METHOD); @@ -24,10 +24,10 @@ public final class SpanDescriptionExtractor { return descriptionForDbSystem(otelSpan); } - return new SpanDescription(name, name, TransactionNameSource.CUSTOM); + return new OtelSpanInfo(name, name, TransactionNameSource.CUSTOM); } - private SpanDescription descriptionForHttpMethod( + private OtelSpanInfo descriptionForHttpMethod( final @NotNull ReadableSpan otelSpan, final @NotNull String httpMethod) { final @NotNull String name = otelSpan.getName(); final @NotNull SpanKind kind = otelSpan.getKind(); @@ -44,19 +44,19 @@ private SpanDescription descriptionForHttpMethod( final @NotNull String op = opBuilder.toString(); if (httpPath == null) { - return new SpanDescription(op, name, TransactionNameSource.CUSTOM); + return new OtelSpanInfo(op, name, TransactionNameSource.CUSTOM); } final @NotNull String description = httpMethod + " " + httpPath; final @NotNull TransactionNameSource transactionNameSource = httpRoute != null ? TransactionNameSource.ROUTE : TransactionNameSource.URL; - return new SpanDescription(op, description, transactionNameSource); + return new OtelSpanInfo(op, description, transactionNameSource); } - private SpanDescription descriptionForDbSystem(final @NotNull ReadableSpan otelSpan) { + private OtelSpanInfo descriptionForDbSystem(final @NotNull ReadableSpan otelSpan) { @Nullable String dbStatement = otelSpan.getAttribute(SemanticAttributes.DB_STATEMENT); @NotNull String description = dbStatement != null ? dbStatement : otelSpan.getName(); - return new SpanDescription("db", description, TransactionNameSource.TASK); + return new OtelSpanInfo("db", description, TransactionNameSource.TASK); } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/TraceData.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/TraceData.java index 4bd234885a..08751b5609 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/TraceData.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/TraceData.java @@ -1,5 +1,7 @@ package io.sentry.opentelemetry; +import io.sentry.Baggage; +import io.sentry.SentryTraceHeader; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -10,16 +12,20 @@ public final class TraceData { private final @NotNull String traceId; private final @NotNull String spanId; private final @Nullable String parentSpanId; - // TODO parentSampled - // TODO baggage + private final @Nullable SentryTraceHeader sentryTraceHeader; + private final @Nullable Baggage baggage; public TraceData( @NotNull final String traceId, @NotNull final String spanId, - @Nullable final String parentSpanId) { + @Nullable final String parentSpanId, + @Nullable final SentryTraceHeader sentryTraceHeader, + @Nullable final Baggage baggage) { this.traceId = traceId; this.spanId = spanId; this.parentSpanId = parentSpanId; + this.sentryTraceHeader = sentryTraceHeader; + this.baggage = baggage; } public @NotNull String getTraceId() { @@ -33,4 +39,12 @@ public TraceData( public @Nullable String getParentSpanId() { return parentSpanId; } + + public @Nullable SentryTraceHeader getSentryTraceHeader() { + return sentryTraceHeader; + } + + public @Nullable Baggage getBaggage() { + return baggage; + } } diff --git a/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/CustomEventProcessor.java b/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/CustomEventProcessor.java deleted file mode 100644 index b74310a676..0000000000 --- a/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/CustomEventProcessor.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.sentry.samples.spring.boot; - -import io.sentry.EventProcessor; -import io.sentry.Hint; -import io.sentry.SentryEvent; -import io.sentry.protocol.SentryRuntime; -import org.jetbrains.annotations.NotNull; -import org.springframework.boot.SpringBootVersion; -import org.springframework.stereotype.Component; - -/** - * Custom {@link EventProcessor} implementation lets modifying {@link SentryEvent}s before they are - * sent to Sentry. - */ -@Component -public class CustomEventProcessor implements EventProcessor { - private final String springBootVersion; - - public CustomEventProcessor(String springBootVersion) { - this.springBootVersion = springBootVersion; - } - - public CustomEventProcessor() { - this(SpringBootVersion.getVersion()); - } - - @Override - public @NotNull SentryEvent process(@NotNull SentryEvent event, @NotNull Hint hint) { - final SentryRuntime runtime = new SentryRuntime(); - runtime.setVersion(springBootVersion); - runtime.setName("Spring Boot"); - event.getContexts().setRuntime(runtime); - return event; - } -} diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java index cec8e9afd8..a181f5f538 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java @@ -31,7 +31,7 @@ public SentrySpanAdvice(final @NotNull IHub hub) { public Object invoke(final @NotNull MethodInvocation invocation) throws Throwable { final ISpan activeSpan = hub.getSpan(); - if (activeSpan == null) { + if (activeSpan == null || activeSpan.isNoOp()) { // there is no active transaction, we do not start new span return invocation.proceed(); } else { diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java index 37b04e2b7d..b3cf87bf2e 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java @@ -49,10 +49,9 @@ public SentrySpanClientHttpRequestInterceptor(final @NotNull IHub hub) { request.getMethod() != null ? request.getMethod().name() : "unknown"; span.setDescription(methodName + " " + request.getURI()); - final SentryTraceHeader sentryTraceHeader = span.toSentryTrace(); - - if (PropagationTargetsUtils.contain( + if (!span.isNoOp() && PropagationTargetsUtils.contain( hub.getOptions().getTracePropagationTargets(), request.getURI())) { + final SentryTraceHeader sentryTraceHeader = span.toSentryTrace(); request.getHeaders().add(sentryTraceHeader.getName(), sentryTraceHeader.getValue()); @Nullable BaggageHeader baggage = diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java index c5843701c9..0d44f45839 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java @@ -41,12 +41,11 @@ public SentrySpanClientWebRequestFilter(final @NotNull IHub hub) { final ISpan span = activeSpan.startChild("http.client"); span.setDescription(request.method().name() + " " + request.url()); - final SentryTraceHeader sentryTraceHeader = span.toSentryTrace(); - final ClientRequest.Builder requestBuilder = ClientRequest.from(request); - if (PropagationTargetsUtils.contain( + if (!span.isNoOp() && PropagationTargetsUtils.contain( hub.getOptions().getTracePropagationTargets(), request.url())) { + final SentryTraceHeader sentryTraceHeader = span.toSentryTrace(); requestBuilder.header(sentryTraceHeader.getName(), sentryTraceHeader.getValue()); final @Nullable BaggageHeader baggageHeader = diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java index d8956fa0ac..88bc989ce3 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java @@ -131,7 +131,9 @@ private ITransaction startTransaction( TransactionNameSource.URL, "http.server", new SentryTraceHeader(sentryTraceHeader), - baggage); + baggage, + null + ); final TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setCustomSamplingContext(customSamplingContext); diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java index a29108a21e..51a7e1fca0 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java @@ -31,7 +31,7 @@ public SentrySpanAdvice(final @NotNull IHub hub) { public Object invoke(final @NotNull MethodInvocation invocation) throws Throwable { final ISpan activeSpan = hub.getSpan(); - if (activeSpan == null) { + if (activeSpan == null || activeSpan.isNoOp()) { // there is no active transaction, we do not start new span return invocation.proceed(); } else { diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java index 4456ea8afc..9a445fb7e3 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java @@ -49,10 +49,10 @@ public SentrySpanClientHttpRequestInterceptor(final @NotNull IHub hub) { request.getMethod() != null ? request.getMethod().name() : "unknown"; span.setDescription(methodName + " " + request.getURI()); - final SentryTraceHeader sentryTraceHeader = span.toSentryTrace(); - - if (PropagationTargetsUtils.contain( - hub.getOptions().getTracePropagationTargets(), request.getURI())) { + if (!span.isNoOp() + && PropagationTargetsUtils.contain( + hub.getOptions().getTracePropagationTargets(), request.getURI())) { + final SentryTraceHeader sentryTraceHeader = span.toSentryTrace(); request.getHeaders().add(sentryTraceHeader.getName(), sentryTraceHeader.getValue()); @Nullable BaggageHeader baggage = diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java index 329fb1d28a..399e7044c9 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java @@ -41,12 +41,13 @@ public SentrySpanClientWebRequestFilter(final @NotNull IHub hub) { final ISpan span = activeSpan.startChild("http.client"); span.setDescription(request.method().name() + " " + request.url()); - final SentryTraceHeader sentryTraceHeader = span.toSentryTrace(); - final ClientRequest.Builder requestBuilder = ClientRequest.from(request); - if (PropagationTargetsUtils.contain( - hub.getOptions().getTracePropagationTargets(), request.url())) { + if (!span.isNoOp() + && PropagationTargetsUtils.contain( + hub.getOptions().getTracePropagationTargets(), request.url())) { + + final SentryTraceHeader sentryTraceHeader = span.toSentryTrace(); requestBuilder.header(sentryTraceHeader.getName(), sentryTraceHeader.getValue()); final @Nullable BaggageHeader baggageHeader = diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java index 238c73a891..fd5cd5f0f7 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java @@ -131,7 +131,8 @@ private ITransaction startTransaction( TransactionNameSource.URL, "http.server", new SentryTraceHeader(sentryTraceHeader), - baggage); + baggage, + null); final TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setCustomSamplingContext(customSamplingContext); diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index e3bd6f1380..30c3163353 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -499,6 +499,7 @@ public abstract interface class io/sentry/ISpan { public abstract fun getTag (Ljava/lang/String;)Ljava/lang/String; public abstract fun getThrowable ()Ljava/lang/Throwable; public abstract fun isFinished ()Z + public abstract fun isNoOp ()Z public abstract fun setData (Ljava/lang/String;Ljava/lang/Object;)V public abstract fun setDescription (Ljava/lang/String;)V public abstract fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V @@ -516,6 +517,7 @@ public abstract interface class io/sentry/ISpan { } public abstract interface class io/sentry/ITransaction : io/sentry/ISpan { + public abstract fun getContexts ()Lio/sentry/protocol/Contexts; public abstract fun getEventId ()Lio/sentry/protocol/SentryId; public abstract fun getLatestActiveSpan ()Lio/sentry/Span; public abstract fun getName ()Ljava/lang/String; @@ -525,6 +527,7 @@ public abstract interface class io/sentry/ITransaction : io/sentry/ISpan { public abstract fun isProfileSampled ()Ljava/lang/Boolean; public abstract fun isSampled ()Ljava/lang/Boolean; public abstract fun scheduleFinish ()V + public abstract fun setContext (Ljava/lang/String;Ljava/lang/Object;)V public abstract fun setName (Ljava/lang/String;)V public abstract fun setName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V } @@ -745,6 +748,7 @@ public final class io/sentry/NoOpSpan : io/sentry/ISpan { public fun getTag (Ljava/lang/String;)Ljava/lang/String; public fun getThrowable ()Ljava/lang/Throwable; public fun isFinished ()Z + public fun isNoOp ()Z public fun setData (Ljava/lang/String;Ljava/lang/Object;)V public fun setDescription (Ljava/lang/String;)V public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V @@ -765,6 +769,7 @@ public final class io/sentry/NoOpTransaction : io/sentry/ITransaction { public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V public fun finish (Lio/sentry/SpanStatus;Ljava/util/Date;)V + public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getData (Ljava/lang/String;)Ljava/lang/Object; public fun getDescription ()Ljava/lang/String; public fun getEventId ()Lio/sentry/protocol/SentryId; @@ -780,9 +785,11 @@ public final class io/sentry/NoOpTransaction : io/sentry/ITransaction { public fun getThrowable ()Ljava/lang/Throwable; public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; public fun isFinished ()Z + public fun isNoOp ()Z public fun isProfileSampled ()Ljava/lang/Boolean; public fun isSampled ()Ljava/lang/Boolean; public fun scheduleFinish ()V + public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V public fun setData (Ljava/lang/String;Ljava/lang/Object;)V public fun setDescription (Ljava/lang/String;)V public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V @@ -1564,6 +1571,7 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction { public fun finish (Lio/sentry/SpanStatus;)V public fun finish (Lio/sentry/SpanStatus;Ljava/util/Date;)V public fun getChildren ()Ljava/util/List; + public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getData ()Ljava/util/Map; public fun getData (Ljava/lang/String;)Ljava/lang/Object; public fun getDescription ()Ljava/lang/String; @@ -1582,9 +1590,11 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction { public fun getTimestamp ()Ljava/lang/Double; public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; public fun isFinished ()Z + public fun isNoOp ()Z public fun isProfileSampled ()Ljava/lang/Boolean; public fun isSampled ()Ljava/lang/Boolean; public fun scheduleFinish ()V + public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V public fun setData (Ljava/lang/String;Ljava/lang/Object;)V public fun setDescription (Ljava/lang/String;)V public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V @@ -1692,6 +1702,7 @@ public final class io/sentry/Span : io/sentry/ISpan { public fun getTimestamp ()Ljava/lang/Double; public fun getTraceId ()Lio/sentry/protocol/SentryId; public fun isFinished ()Z + public fun isNoOp ()Z public fun isProfileSampled ()Ljava/lang/Boolean; public fun isSampled ()Ljava/lang/Boolean; public fun setData (Ljava/lang/String;Ljava/lang/Object;)V @@ -1866,7 +1877,7 @@ public final class io/sentry/TransactionContext : io/sentry/SpanContext { public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/TracesSamplingDecision;)V public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Lio/sentry/protocol/TransactionNameSource;Lio/sentry/SpanId;Lio/sentry/TracesSamplingDecision;Lio/sentry/Baggage;)V public static fun fromSentryTrace (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;Ljava/lang/String;Lio/sentry/SentryTraceHeader;)Lio/sentry/TransactionContext; - public static fun fromSentryTrace (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;Ljava/lang/String;Lio/sentry/SentryTraceHeader;Lio/sentry/Baggage;)Lio/sentry/TransactionContext; + public static fun fromSentryTrace (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;Ljava/lang/String;Lio/sentry/SentryTraceHeader;Lio/sentry/Baggage;Lio/sentry/SpanId;)Lio/sentry/TransactionContext; public static fun fromSentryTrace (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryTraceHeader;)Lio/sentry/TransactionContext; public fun getBaggage ()Lio/sentry/Baggage; public fun getInstrumenter ()Lio/sentry/Instrumenter; diff --git a/sentry/src/main/java/io/sentry/Baggage.java b/sentry/src/main/java/io/sentry/Baggage.java index aca3fdf0fd..283274d4de 100644 --- a/sentry/src/main/java/io/sentry/Baggage.java +++ b/sentry/src/main/java/io/sentry/Baggage.java @@ -35,7 +35,7 @@ public final class Baggage { final @NotNull ILogger logger; @NotNull - public static Baggage fromHeader(final String headerValue) { + public static Baggage fromHeader(final @Nullable String headerValue) { return Baggage.fromHeader( headerValue, false, HubAdapter.getInstance().getOptions().getLogger()); } diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index c97cd98dc1..172b81dcad 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -753,7 +753,7 @@ public void flush(long timeoutMillis) { SentryLevel.WARNING, "Instance is disabled and this 'traceHeaders' call is a no-op."); } else { final ISpan span = stack.peek().getScope().getSpan(); - if (span != null) { + if (span != null && !span.isNoOp()) { traceHeader = span.toSentryTrace(); } } diff --git a/sentry/src/main/java/io/sentry/ISpan.java b/sentry/src/main/java/io/sentry/ISpan.java index 0332475682..70074101d9 100644 --- a/sentry/src/main/java/io/sentry/ISpan.java +++ b/sentry/src/main/java/io/sentry/ISpan.java @@ -205,4 +205,12 @@ ISpan startChild( * @param unit the unit the value is measured in */ void setMeasurement(@NotNull String name, @NotNull Number value, @NotNull MeasurementUnit unit); + + /** + * Whether this span instance is a NOOP that doesn't collect information + * + * @return true if NOOP + */ + @ApiStatus.Internal + boolean isNoOp(); } diff --git a/sentry/src/main/java/io/sentry/ITransaction.java b/sentry/src/main/java/io/sentry/ITransaction.java index bed79fec6b..6e1c525fe8 100644 --- a/sentry/src/main/java/io/sentry/ITransaction.java +++ b/sentry/src/main/java/io/sentry/ITransaction.java @@ -1,5 +1,6 @@ package io.sentry; +import io.sentry.protocol.Contexts; import io.sentry.protocol.SentryId; import io.sentry.protocol.TransactionNameSource; import java.util.List; @@ -77,4 +78,11 @@ public interface ITransaction extends ISpan { /** Schedules when transaction should be automatically finished. */ void scheduleFinish(); + + @ApiStatus.Internal + void setContext(@NotNull String key, @NotNull Object context); + + @ApiStatus.Internal + @NotNull + Contexts getContexts(); } diff --git a/sentry/src/main/java/io/sentry/NoOpSpan.java b/sentry/src/main/java/io/sentry/NoOpSpan.java index b3ad24e65f..2aaf3ffc95 100644 --- a/sentry/src/main/java/io/sentry/NoOpSpan.java +++ b/sentry/src/main/java/io/sentry/NoOpSpan.java @@ -47,8 +47,8 @@ public static NoOpSpan getInstance() { } @Override - public @NotNull BaggageHeader toBaggageHeader(@Nullable List thirdPartyBaggageHeaders) { - return new BaggageHeader(""); + public @Nullable BaggageHeader toBaggageHeader(@Nullable List thirdPartyBaggageHeaders) { + return null; } @Override @@ -124,4 +124,9 @@ public void setMeasurement(@NotNull String name, @NotNull Number value) {} @Override public void setMeasurement( @NotNull String name, @NotNull Number value, @NotNull MeasurementUnit unit) {} + + @Override + public boolean isNoOp() { + return true; + } } diff --git a/sentry/src/main/java/io/sentry/NoOpTransaction.java b/sentry/src/main/java/io/sentry/NoOpTransaction.java index bb731576a0..6c5037868a 100644 --- a/sentry/src/main/java/io/sentry/NoOpTransaction.java +++ b/sentry/src/main/java/io/sentry/NoOpTransaction.java @@ -1,5 +1,6 @@ package io.sentry; +import io.sentry.protocol.Contexts; import io.sentry.protocol.SentryId; import io.sentry.protocol.TransactionNameSource; import java.util.Collections; @@ -95,8 +96,8 @@ public boolean isFinished() { } @Override - public @NotNull BaggageHeader toBaggageHeader(@Nullable List thirdPartyBaggageHeaders) { - return new BaggageHeader(""); + public @Nullable BaggageHeader toBaggageHeader(@Nullable List thirdPartyBaggageHeaders) { + return null; } @Override @@ -177,4 +178,19 @@ public void setMeasurement(@NotNull String name, @NotNull Number value) {} @Override public void setMeasurement( @NotNull String name, @NotNull Number value, @NotNull MeasurementUnit unit) {} + + @ApiStatus.Internal + @Override + public void setContext(@NotNull String key, @NotNull Object context) {} + + @ApiStatus.Internal + @Override + public @NotNull Contexts getContexts() { + return new Contexts(); + } + + @Override + public boolean isNoOp() { + return true; + } } diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index 1d2c299314..74183b4924 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -1,5 +1,6 @@ package io.sentry; +import io.sentry.protocol.Contexts; import io.sentry.protocol.MeasurementValue; import io.sentry.protocol.SentryId; import io.sentry.protocol.SentryTransaction; @@ -78,6 +79,7 @@ public final class SentryTracer implements ITransaction { private @NotNull TransactionNameSource transactionNameSource; private final @NotNull Map measurements; private final @NotNull Instrumenter instrumenter; + private final @NotNull Contexts contexts = new Contexts(); public SentryTracer(final @NotNull TransactionContext context, final @NotNull IHub hub) { this(context, hub, null); @@ -661,6 +663,23 @@ Map getMeasurements() { return measurements; } + @ApiStatus.Internal + @Override + public void setContext(final @NotNull String key, final @NotNull Object context) { + contexts.put(key, context); + } + + @ApiStatus.Internal + @Override + public @NotNull Contexts getContexts() { + return contexts; + } + + @Override + public boolean isNoOp() { + return false; + } + private static final class FinishStatus { static final FinishStatus NOT_FINISHED = FinishStatus.notFinished(); diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index 5e0c2b87a9..c1d1ae0d09 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -340,6 +340,11 @@ Long getEndNanos() { return endNanos; } + @Override + public boolean isNoOp() { + return false; + } + void setSpanFinishedCallback(final @Nullable SpanFinishedCallback callback) { this.spanFinishedCallback = callback; } diff --git a/sentry/src/main/java/io/sentry/TransactionContext.java b/sentry/src/main/java/io/sentry/TransactionContext.java index 3633c2ffaa..4463f9ae4f 100644 --- a/sentry/src/main/java/io/sentry/TransactionContext.java +++ b/sentry/src/main/java/io/sentry/TransactionContext.java @@ -26,7 +26,7 @@ public final class TransactionContext extends SpanContext { final @NotNull String name, final @NotNull String operation, final @NotNull SentryTraceHeader sentryTrace) { - return fromSentryTrace(name, TransactionNameSource.CUSTOM, operation, sentryTrace, null); + return fromSentryTrace(name, TransactionNameSource.CUSTOM, operation, sentryTrace, null, null); } /** @@ -72,7 +72,8 @@ public final class TransactionContext extends SpanContext { final @NotNull TransactionNameSource transactionNameSource, final @NotNull String operation, final @NotNull SentryTraceHeader sentryTrace, - final @Nullable Baggage baggage) { + final @Nullable Baggage baggage, + final @Nullable SpanId spanId) { @Nullable Boolean parentSampled = sentryTrace.isSampled(); TracesSamplingDecision samplingDecision = parentSampled == null ? null : new TracesSamplingDecision(parentSampled); @@ -89,11 +90,13 @@ public final class TransactionContext extends SpanContext { } } + final @NotNull SpanId spanIdToUse = spanId == null ? new SpanId() : spanId; + return new TransactionContext( name, operation, sentryTrace.getTraceId(), - new SpanId(), + spanIdToUse, transactionNameSource, sentryTrace.getSpanId(), samplingDecision, diff --git a/sentry/src/main/java/io/sentry/config/ClasspathPropertiesLoader.java b/sentry/src/main/java/io/sentry/config/ClasspathPropertiesLoader.java index 56b2ce83c3..1a730e8f16 100644 --- a/sentry/src/main/java/io/sentry/config/ClasspathPropertiesLoader.java +++ b/sentry/src/main/java/io/sentry/config/ClasspathPropertiesLoader.java @@ -16,14 +16,18 @@ final class ClasspathPropertiesLoader implements PropertiesLoader { private final @NotNull ILogger logger; public ClasspathPropertiesLoader( - @NotNull String fileName, @NotNull ClassLoader classLoader, @NotNull ILogger logger) { + @NotNull String fileName, @Nullable ClassLoader classLoader, @NotNull ILogger logger) { this.fileName = fileName; - this.classLoader = classLoader; + // bootstrap classloader is represented as null, so using system classloader instead + if (classLoader == null) { + this.classLoader = ClassLoader.getSystemClassLoader(); + } else { + this.classLoader = classLoader; + } this.logger = logger; } public ClasspathPropertiesLoader(@NotNull ILogger logger) { - // TODO check not null this("sentry.properties", ClasspathPropertiesLoader.class.getClassLoader(), logger); } diff --git a/sentry/src/main/java/io/sentry/protocol/SentryTransaction.java b/sentry/src/main/java/io/sentry/protocol/SentryTransaction.java index a1e96fa360..fab4b2ae42 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryTransaction.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryTransaction.java @@ -67,6 +67,9 @@ public SentryTransaction(final @NotNull SentryTracer sentryTracer) { } } final Contexts contexts = this.getContexts(); + + contexts.putAll(sentryTracer.getContexts()); + final SpanContext tracerContext = sentryTracer.getSpanContext(); // tags must be placed on the root of the transaction instead of contexts.trace.tags contexts.setTrace( diff --git a/sentry/src/test/java/io/sentry/SentryTracerTest.kt b/sentry/src/test/java/io/sentry/SentryTracerTest.kt index 304fe75300..fed5bcadc6 100644 --- a/sentry/src/test/java/io/sentry/SentryTracerTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTracerTest.kt @@ -10,6 +10,8 @@ import org.mockito.kotlin.never import org.mockito.kotlin.spy import org.mockito.kotlin.times import org.mockito.kotlin.verify +import java.time.LocalDateTime +import java.time.ZoneOffset import java.util.Date import kotlin.test.Test import kotlin.test.assertEquals @@ -119,6 +121,15 @@ class SentryTracerTest { assertEquals(SpanStatus.ABORTED, tracer.status) } + @Test + fun `when transaction is finished with status and timestamp, timestamp and status are set`() { + val tracer = fixture.getSut() + val date = Date.from(LocalDateTime.of(2022, 12, 24, 23, 59, 58, 0).toInstant(ZoneOffset.UTC)) + tracer.finish(SpanStatus.ABORTED, date) + assertEquals(tracer.timestamp, DateUtils.dateToSeconds(date)) + assertEquals(SpanStatus.ABORTED, tracer.status) + } + @Test fun `when transaction is finished, transaction is captured`() { val tracer = fixture.getSut() @@ -207,6 +218,31 @@ class SentryTracerTest { ) } + @Test + fun `when transaction is finished, context is set`() { + val tracer = fixture.getSut() + val otelContext = mapOf( + "attributes" to mapOf( + "db.connection_string" to "hsqldb:mem:", + "db.statement" to "CREATE TABLE person ( id INTEGER IDENTITY PRIMARY KEY, firstName VARCHAR(?) NOT NULL, lastName VARCHAR(?) NOT NULL )" + ), + "resource" to mapOf( + "process.runtime.version" to "17.0.4.1+1", + "telemetry.auto.version" to "sentry-6.7.0-otel-1.19.2" + ) + ) + tracer.setContext("otel", otelContext) + tracer.finish() + + verify(fixture.hub).captureTransaction( + check { + assertEquals(otelContext, it.contexts["otel"]) + }, + anyOrNull(), + anyOrNull() + ) + } + @Test fun `returns sentry-trace header`() { val tracer = fixture.getSut() diff --git a/sentry/src/test/java/io/sentry/TransactionContextTest.kt b/sentry/src/test/java/io/sentry/TransactionContextTest.kt index 7eef9ce881..4c40a65272 100644 --- a/sentry/src/test/java/io/sentry/TransactionContextTest.kt +++ b/sentry/src/test/java/io/sentry/TransactionContextTest.kt @@ -33,7 +33,7 @@ class TransactionContextTest { fun `when context is created from trace header and baggage header, parent sampling decision of false is set from trace header`() { val traceHeader = SentryTraceHeader(SentryId(), SpanId(), false) val baggageHeader = Baggage.fromHeader("sentry-trace_id=a,sentry-transaction=sentryTransaction,sentry-sample_rate=0.3") - val context = TransactionContext.fromSentryTrace("name", TransactionNameSource.CUSTOM, "op", traceHeader, baggageHeader) + val context = TransactionContext.fromSentryTrace("name", TransactionNameSource.CUSTOM, "op", traceHeader, baggageHeader, null) assertNull(context.sampled) assertNull(context.profileSampled) assertFalse(context.parentSampled!!) @@ -44,7 +44,7 @@ class TransactionContextTest { fun `when context is created from trace header and baggage header, parent sampling decision of false is set from trace header if no sample rate is available`() { val traceHeader = SentryTraceHeader(SentryId(), SpanId(), false) val baggageHeader = Baggage.fromHeader("sentry-trace_id=a,sentry-transaction=sentryTransaction") - val context = TransactionContext.fromSentryTrace("name", TransactionNameSource.CUSTOM, "op", traceHeader, baggageHeader) + val context = TransactionContext.fromSentryTrace("name", TransactionNameSource.CUSTOM, "op", traceHeader, baggageHeader, null) assertNull(context.sampled) assertNull(context.profileSampled) assertFalse(context.parentSampled!!) @@ -55,7 +55,7 @@ class TransactionContextTest { fun `when context is created from trace header and baggage header, parent sampling decision of true is set from trace header`() { val traceHeader = SentryTraceHeader(SentryId(), SpanId(), true) val baggageHeader = Baggage.fromHeader("sentry-trace_id=a,sentry-transaction=sentryTransaction,sentry-sample_rate=0.3") - val context = TransactionContext.fromSentryTrace("name", TransactionNameSource.CUSTOM, "op", traceHeader, baggageHeader) + val context = TransactionContext.fromSentryTrace("name", TransactionNameSource.CUSTOM, "op", traceHeader, baggageHeader, null) assertNull(context.sampled) assertNull(context.profileSampled) assertTrue(context.parentSampled!!) @@ -66,7 +66,7 @@ class TransactionContextTest { fun `when context is created from trace header and baggage header, parent sampling decision of true is set from trace header if no sample rate is available`() { val traceHeader = SentryTraceHeader(SentryId(), SpanId(), true) val baggageHeader = Baggage.fromHeader("sentry-trace_id=a,sentry-transaction=sentryTransaction") - val context = TransactionContext.fromSentryTrace("name", TransactionNameSource.CUSTOM, "op", traceHeader, baggageHeader) + val context = TransactionContext.fromSentryTrace("name", TransactionNameSource.CUSTOM, "op", traceHeader, baggageHeader, null) assertNull(context.sampled) assertNull(context.profileSampled) assertTrue(context.parentSampled!!)