diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs
index e53d2b20b2..af8f23bc21 100644
--- a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs
+++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs
@@ -7,6 +7,14 @@
var builder = WebApplication.CreateBuilder(args);
+// Read the Sentry DSN from the environment variable if it's not already set in code.
+#if SENTRY_DSN_DEFINED_IN_ENV
+var dsn = Environment.GetEnvironmentVariable("SENTRY_DSN")
+ ?? throw new InvalidOperationException("SENTRY_DSN environment variable is not set");
+#else
+var dsn = SamplesShared.Dsn;
+#endif
+
// OpenTelemetry Configuration
// See https://opentelemetry.io/docs/instrumentation/net/getting-started/
builder.Services.AddOpenTelemetry()
@@ -20,8 +28,8 @@
// The two lines below take care of configuring sources for ASP.NET Core and HttpClient
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
- // Finally we configure OpenTelemetry to send traces to Sentry
- .AddSentry()
+ // Finally, we configure OpenTelemetry over OTLP to send traces to Sentry
+ .AddSentryOtlp(dsn)
);
builder.WebHost.UseSentry(options =>
@@ -35,7 +43,7 @@
options.Debug = builder.Environment.IsDevelopment();
options.SendDefaultPii = true;
options.TracesSampleRate = 1.0;
- options.UseOpenTelemetry(); // <-- Configure Sentry to use OpenTelemetry trace information
+ options.UseOtlp(); // <-- Configure Sentry to use OpenTelemetry trace information
});
builder.Services
diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Sentry.Samples.OpenTelemetry.AspNetCore.csproj b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Sentry.Samples.OpenTelemetry.AspNetCore.csproj
index a07956f015..4b2791c341 100644
--- a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Sentry.Samples.OpenTelemetry.AspNetCore.csproj
+++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Sentry.Samples.OpenTelemetry.AspNetCore.csproj
@@ -11,6 +11,7 @@
+
diff --git a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryDiagnosticListenerIntegration.cs b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryDiagnosticListenerIntegration.cs
index 39ced41aa5..45da25e6bc 100644
--- a/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryDiagnosticListenerIntegration.cs
+++ b/src/Sentry.DiagnosticSource/Internal/DiagnosticSource/SentryDiagnosticListenerIntegration.cs
@@ -3,7 +3,7 @@
namespace Sentry.Internal.DiagnosticSource;
-internal class SentryDiagnosticListenerIntegration : ISdkIntegration
+internal class SentryDiagnosticListenerIntegration : ISdkIntegration, ISentryTracingIntegration
{
public void Register(IHub hub, SentryOptions options)
{
diff --git a/src/Sentry.EntityFramework/DbInterceptionIntegration.cs b/src/Sentry.EntityFramework/DbInterceptionIntegration.cs
index bf561ef039..a75b873625 100644
--- a/src/Sentry.EntityFramework/DbInterceptionIntegration.cs
+++ b/src/Sentry.EntityFramework/DbInterceptionIntegration.cs
@@ -1,6 +1,6 @@
namespace Sentry.EntityFramework;
-internal class DbInterceptionIntegration : ISdkIntegration
+internal class DbInterceptionIntegration : ISdkIntegration, ISentryTracingIntegration
{
// Internal for testing.
internal IDbInterceptor? SqlInterceptor { get; private set; }
diff --git a/src/Sentry.Maui.CommunityToolkit.Mvvm/SentryOptionsExtensions.cs b/src/Sentry.Maui.CommunityToolkit.Mvvm/SentryOptionsExtensions.cs
index 2f22bcbbbd..eebe40e6d6 100644
--- a/src/Sentry.Maui.CommunityToolkit.Mvvm/SentryOptionsExtensions.cs
+++ b/src/Sentry.Maui.CommunityToolkit.Mvvm/SentryOptionsExtensions.cs
@@ -1,3 +1,4 @@
+using Sentry.Extensibility;
using Sentry.Maui.CommunityToolkit.Mvvm;
namespace Sentry.Maui;
@@ -12,7 +13,14 @@ public static class SentryOptionsExtensions
///
public static SentryMauiOptions AddCommunityToolkitIntegration(this SentryMauiOptions options)
{
- options.AddIntegrationEventBinder();
+ if (options.DisableSentryTracing)
+ {
+ options.LogWarning("Skipping CommunityToolkit.Mvvm integration because OpenTelemetry is enabled.");
+ }
+ else
+ {
+ options.AddIntegrationEventBinder();
+ }
return options;
}
}
diff --git a/src/Sentry.OpenTelemetry/OpenTelemetryTransactionProcessor.cs b/src/Sentry.OpenTelemetry/OpenTelemetryTransactionProcessor.cs
index 4ab485dde1..a6423ef254 100644
--- a/src/Sentry.OpenTelemetry/OpenTelemetryTransactionProcessor.cs
+++ b/src/Sentry.OpenTelemetry/OpenTelemetryTransactionProcessor.cs
@@ -1,4 +1,5 @@
using Sentry.Extensibility;
+using Sentry.Internal.OpenTelemetry;
namespace Sentry.OpenTelemetry;
diff --git a/src/Sentry.OpenTelemetry/OtelPropagationContext.cs b/src/Sentry.OpenTelemetry/OtelPropagationContext.cs
new file mode 100644
index 0000000000..73839612ff
--- /dev/null
+++ b/src/Sentry.OpenTelemetry/OtelPropagationContext.cs
@@ -0,0 +1,28 @@
+using Sentry.Extensibility;
+using Sentry.Internal;
+
+namespace Sentry.OpenTelemetry;
+
+internal class OtelPropagationContext : IPropagationContext
+{
+ public DynamicSamplingContext? DynamicSamplingContext { get; private set; }
+
+ public SentryId TraceId => Activity.Current?.TraceId.AsSentryId() ?? default;
+ public SpanId SpanId => Activity.Current?.SpanId.AsSentrySpanId() ?? default;
+ public SpanId? ParentSpanId => Activity.Current?.ParentSpanId.AsSentrySpanId();
+
+ ///
+ /// Warning: this method may throw an exception if Activity.Current is null.
+ /// This method should not be used when instrumenting with OTEL.
+ ///
+ public DynamicSamplingContext GetOrCreateDynamicSamplingContext(SentryOptions options, IReplaySession replaySession)
+ {
+ if (DynamicSamplingContext is null)
+ {
+ options.LogDebug("Creating the Dynamic Sampling Context from the Propagation Context.");
+ DynamicSamplingContext = this.CreateDynamicSamplingContext(options, replaySession);
+ }
+
+ return DynamicSamplingContext;
+ }
+}
diff --git a/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj b/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj
index 6067738ff9..ef2d1fb369 100644
--- a/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj
+++ b/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj
@@ -5,6 +5,8 @@
$(PackageTags);OpenTelemetry
$(CurrentTfms);netstandard2.1;netstandard2.0;net462
enable
+
+ $(NoWarn);AD0001
@@ -17,7 +19,7 @@
-
+
diff --git a/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs b/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs
index 2035787a19..e98c39b782 100644
--- a/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs
+++ b/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs
@@ -9,11 +9,11 @@ namespace Sentry.OpenTelemetry;
public static class SentryOptionsExtensions
{
///
- /// Enables OpenTelemetry instrumentation with Sentry
+ /// Configures Sentry to use OpenTelemetry for distributed tracing.
///
- /// instance
- ///
- ///
+ /// The instance.
+ ///
+ ///
/// The default TextMapPropagator to be used by OpenTelemetry.
///
/// If this parameter is not supplied, the will be used, which propagates the
@@ -24,34 +24,98 @@ public static class SentryOptionsExtensions
/// could wrap this in a if you needed other propagators as well.
///
///
- public static void UseOpenTelemetry(
- this SentryOptions options,
- TracerProviderBuilder traceProviderBuilder,
- TextMapPropagator? defaultTextMapPropagator = null
- )
+ /// Whether to disable traces created using Sentry's tracing instrumentation.
+ /// It's recommended that you set this to true since mixing OpenTelemetry and Sentry traces may yield
+ /// unexpected results. It is false by default for backward compatibility only.
+ ///
+ ///
+ /// This method of initialising the Sentry OpenTelemetry integration will be depricated in a future major release.
+ /// We recommend you use instead.
+ ///
+ public static void UseOpenTelemetry(this SentryOptions options, TracerProviderBuilder builder,
+ TextMapPropagator? textMapPropagator = null, bool disableSentryTracing = false)
+ {
+ options.UseOpenTelemetry(disableSentryTracing);
+ builder.AddSentry(textMapPropagator);
+ }
+
+ ///
+ /// Configures Sentry to use OpenTelemetry for distributed tracing.
+ ///
+ ///
+ /// Note: if you are using this overload to configure Sentry to work with OpenTelemetry, you will also have to call
+ /// when building your
+ /// to ensure OpenTelemetry sends trace information to Sentry.
+ ///
+ ///
+ /// The instance.
+ /// Whether to disable traces created using Sentry's tracing instrumentation.
+ /// It's recommended that you set this to true since mixing OpenTelemetry and Sentry traces may yield
+ /// unexpected results. It is false by default for backward compatibility only.
+ ///
+ ///
+ /// This method of initialising the Sentry OpenTelemetry integration will be depricated in a future major release.
+ /// We recommend you use instead.
+ ///
+ public static void UseOpenTelemetry(this SentryOptions options, bool disableSentryTracing = false)
{
options.Instrumenter = Instrumenter.OpenTelemetry;
+ options.DisableSentryTracing = disableSentryTracing;
+ options.PropagationContextFactory = _ => new OtelPropagationContext();
options.AddTransactionProcessor(
new OpenTelemetryTransactionProcessor()
- );
+ );
+ }
- traceProviderBuilder.AddSentry(defaultTextMapPropagator);
+ ///
+ /// Configures Sentry to use OpenTelemetry for distributed tracing. Sentry instrumented traces will be
+ /// disabled (so all tracing instrumentation must be done using the OpenTelemetry classes).
+ ///
+ ///
+ /// This is the recommended way to set up Sentry's OpenTelemetry integration.
+ ///
+ ///
+ /// The instance.
+ ///
+ ///
+ /// The default TextMapPropagator to be used by OpenTelemetry.
+ ///
+ /// If this parameter is not supplied, the will be used, which propagates the
+ /// baggage header as well as Sentry trace headers.
+ ///
+ ///
+ /// The is required for Sentry's OpenTelemetry integration to work but you
+ /// could wrap this in a if you needed other propagators as well.
+ ///
+ ///
+ public static void UseOtlp(this SentryOptions options, TracerProviderBuilder builder, TextMapPropagator? textMapPropagator = null)
+ {
+ if (string.IsNullOrWhiteSpace(options.Dsn))
+ {
+ throw new ArgumentException("Sentry DSN must be set before calling `SentryOptions.UseOTLP`", nameof(options.Dsn));
+ }
+ builder.AddSentryOtlp(options.Dsn, textMapPropagator);
+ options.UseOtlp();
}
///
- /// Configures Sentry to use OpenTelemetry for distributed tracing.
+ /// Configures Sentry to use OpenTelemetry for distributed tracing. Sentry instrumented traces will be
+ /// disabled (so all tracing instrumentation must be done using the OpenTelemetry classes).
+ ///
///
- /// Note: if you are using this method to configure Sentry to work with OpenTelemetry you will also have to call
- /// when building your
- /// to ensure OpenTelemetry sends trace information to Sentry.
+ /// This is the recommended way to set up Sentry's OpenTelemetry integration.
///
///
- /// instance
- public static void UseOpenTelemetry(this SentryOptions options)
+ ///
+ /// Note: if you are using this overload to configure Sentry to work with OpenTelemetry, you will also have to call
+ /// , when building your
+ /// to ensure OpenTelemetry sends trace information to Sentry.
+ ///
+ /// The instance.
+ public static void UseOtlp(this SentryOptions options)
{
options.Instrumenter = Instrumenter.OpenTelemetry;
- options.AddTransactionProcessor(
- new OpenTelemetryTransactionProcessor()
- );
+ options.DisableSentryTracing = true;
+ options.PropagationContextFactory = _ => new OtelPropagationContext();
}
}
diff --git a/src/Sentry.OpenTelemetry/SentryPropagator.cs b/src/Sentry.OpenTelemetry/SentryPropagator.cs
index 78fd960d6b..f8b4696c21 100644
--- a/src/Sentry.OpenTelemetry/SentryPropagator.cs
+++ b/src/Sentry.OpenTelemetry/SentryPropagator.cs
@@ -2,6 +2,7 @@
using OpenTelemetry;
using OpenTelemetry.Context.Propagation;
using Sentry.Extensibility;
+using Sentry.Internal.OpenTelemetry;
namespace Sentry.OpenTelemetry;
@@ -54,7 +55,7 @@ public override PropagationContext Extract(PropagationContext context, T carr
Options?.LogDebug("SentryPropagator.Extract");
var result = base.Extract(context, carrier, getter);
- var baggage = result.Baggage; // The Otel .NET SDK takes care of baggage headers alread
+ var baggage = result.Baggage; // The Otel .NET SDK takes care of baggage headers already
Options?.LogDebug("Baggage");
foreach (var entry in baggage)
diff --git a/src/Sentry.OpenTelemetry/TracerProviderBuilderExtensions.cs b/src/Sentry.OpenTelemetry/TracerProviderBuilderExtensions.cs
index 96472eda98..c57d23003f 100644
--- a/src/Sentry.OpenTelemetry/TracerProviderBuilderExtensions.cs
+++ b/src/Sentry.OpenTelemetry/TracerProviderBuilderExtensions.cs
@@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry;
using OpenTelemetry.Context.Propagation;
+using OpenTelemetry.Exporter;
using OpenTelemetry.Trace;
using Sentry.Extensibility;
@@ -11,10 +12,22 @@ namespace Sentry.OpenTelemetry;
///
public static class TracerProviderBuilderExtensions
{
+ internal const string MissingDsnWarning = "Invalid DSN passed to AddSentryOTLP";
+
///
- /// Ensures OpenTelemetry trace information is sent to Sentry.
+ ///
+ /// Ensures OpenTelemetry trace information is sent to Sentry. OpenTelemetry spans will be converted to Sentry spans
+ /// using a span processor. This is no longer recommended. SDK users should consider using
+ /// instead, which is the recommended
+ /// way to send OpenTelemetry trace information to Sentry moving forward.
+ ///
+ ///
+ /// Note that if you use this method to configure the trace builder, you will also need to call
+ /// when initialising Sentry, for Sentry
+ /// to work properly with OpenTelemetry.
+ ///
///
- /// .
+ /// The .
///
/// The default TextMapPropagator to be used by OpenTelemetry.
///
@@ -27,7 +40,8 @@ public static class TracerProviderBuilderExtensions
///
///
/// The supplied for chaining.
- public static TracerProviderBuilder AddSentry(this TracerProviderBuilder tracerProviderBuilder, TextMapPropagator? defaultTextMapPropagator = null)
+ public static TracerProviderBuilder AddSentry(this TracerProviderBuilder tracerProviderBuilder,
+ TextMapPropagator? defaultTextMapPropagator = null)
{
defaultTextMapPropagator ??= new SentryPropagator();
Sdk.SetDefaultTextMapPropagator(defaultTextMapPropagator);
@@ -36,7 +50,7 @@ public static TracerProviderBuilder AddSentry(this TracerProviderBuilder tracerP
internal static BaseProcessor ImplementationFactory(IServiceProvider services)
{
- List enrichers = new();
+ List enrichers = [];
// AspNetCoreEnricher
var userFactory = services.GetService();
@@ -55,4 +69,56 @@ internal static BaseProcessor ImplementationFactory(IServiceProvider s
logger?.LogWarning("Sentry is disabled so no OpenTelemetry spans will be sent to Sentry.");
return DisabledSpanProcessor.Instance;
}
+
+ ///
+ ///
+ /// Ensures OpenTelemetry trace information is sent to the Sentry OTLP endpoint.
+ ///
+ ///
+ /// Note that if you use this method to configure the trace builder, you will also need to call
+ /// when initialising Sentry, for Sentry to work
+ /// properly with OpenTelemetry.
+ ///
+ ///
+ /// The .
+ /// The DSN for your Sentry project
+ ///
+ /// The default TextMapPropagator to be used by OpenTelemetry.
+ ///
+ /// If this parameter is not supplied, the will be used, which propagates the
+ /// baggage header as well as Sentry trace headers.
+ ///
+ ///
+ /// The is required for Sentry's OpenTelemetry integration to work but you
+ /// could wrap this in a if you needed other propagators as well.
+ ///
+ ///
+ /// The supplied for chaining.
+ public static TracerProviderBuilder AddSentryOtlp(this TracerProviderBuilder tracerProviderBuilder, string dsnString,
+ TextMapPropagator? defaultTextMapPropagator = null)
+ {
+ if (Dsn.TryParse(dsnString) is not { } dsn)
+ {
+ throw new ArgumentException("Invalid DSN passed to AddSentryOTLP", nameof(dsnString));
+ }
+
+ defaultTextMapPropagator ??= new SentryPropagator();
+ Sdk.SetDefaultTextMapPropagator(defaultTextMapPropagator);
+
+ tracerProviderBuilder.AddOtlpExporter(options => OtlpConfigurationCallback(options, dsn));
+ return tracerProviderBuilder;
+ }
+
+ // Internal helper method for testing purposes
+ internal static void OtlpConfigurationCallback(OtlpExporterOptions options, Dsn dsn)
+ {
+ options.Endpoint = dsn.GetOtlpTracesEndpointUri();
+ options.Protocol = OtlpExportProtocol.HttpProtobuf;
+ options.HttpClientFactory = () =>
+ {
+ var client = new HttpClient();
+ client.DefaultRequestHeaders.Add("X-Sentry-Auth", $"sentry sentry_key={dsn.PublicKey}");
+ return client;
+ };
+ }
}
diff --git a/src/Sentry/Dsn.cs b/src/Sentry/Dsn.cs
index 011af39169..a132d89945 100644
--- a/src/Sentry/Dsn.cs
+++ b/src/Sentry/Dsn.cs
@@ -58,6 +58,8 @@ private Dsn(
public Uri GetEnvelopeEndpointUri() => new(ApiBaseUri, "envelope/");
+ public Uri GetOtlpTracesEndpointUri() => new(ApiBaseUri, "integration/otlp/v1/traces");
+
public override string ToString() => Source;
public static bool IsDisabled(string? dsn) =>
diff --git a/src/Sentry/DynamicSamplingContext.cs b/src/Sentry/DynamicSamplingContext.cs
index f39d7fa5a7..3f9b82ee0e 100644
--- a/src/Sentry/DynamicSamplingContext.cs
+++ b/src/Sentry/DynamicSamplingContext.cs
@@ -1,5 +1,4 @@
using Sentry.Internal;
-using Sentry.Internal.Extensions;
namespace Sentry;
@@ -228,7 +227,13 @@ public static DynamicSamplingContext CreateFromUnsampledTransaction(UnsampledTra
replaySession);
}
- public static DynamicSamplingContext CreateFromPropagationContext(SentryPropagationContext propagationContext, SentryOptions options, IReplaySession? replaySession)
+ ///
+ /// Creates a from the given .
+ ///
+ /// Can be thrown when using OpenTelemetry instrumentation and
+ /// System.Diagnostics.Activity.Current is null. This method should not be used when instrumenting with OTEL.
+ ///
+ public static DynamicSamplingContext CreateFromPropagationContext(IPropagationContext propagationContext, SentryOptions options, IReplaySession? replaySession)
{
var traceId = propagationContext.TraceId;
var publicKey = options.ParsedDsn.PublicKey;
@@ -257,6 +262,12 @@ public static DynamicSamplingContext CreateDynamicSamplingContext(this Transacti
public static DynamicSamplingContext CreateDynamicSamplingContext(this UnsampledTransaction transaction, SentryOptions options, IReplaySession? replaySession)
=> DynamicSamplingContext.CreateFromUnsampledTransaction(transaction, options, replaySession);
- public static DynamicSamplingContext CreateDynamicSamplingContext(this SentryPropagationContext propagationContext, SentryOptions options, IReplaySession? replaySession)
+ ///
+ /// Creates a from the given .
+ ///
+ /// Can be thrown when using OpenTelemetry instrumentation and
+ /// System.Diagnostics.Activity.Current is null. This method should not be used when instrumenting with OTEL.
+ ///
+ public static DynamicSamplingContext CreateDynamicSamplingContext(this IPropagationContext propagationContext, SentryOptions options, IReplaySession? replaySession)
=> DynamicSamplingContext.CreateFromPropagationContext(propagationContext, options, replaySession);
}
diff --git a/src/Sentry/IHub.cs b/src/Sentry/IHub.cs
index a5a7afd9c9..c42a1f784d 100644
--- a/src/Sentry/IHub.cs
+++ b/src/Sentry/IHub.cs
@@ -70,6 +70,9 @@ public ITransactionTracer StartTransaction(
///
/// Gets the Sentry baggage header that allows tracing across services
///
+ /// Can be thrown when using OpenTelemetry instrumentation and
+ /// System.Diagnostics.Activity.Current is null. This method should not be used when instrumenting with OTEL.
+ ///
public BaggageHeader? GetBaggage();
///
diff --git a/src/Sentry/Integrations/ISdkIntegration.cs b/src/Sentry/Integrations/ISdkIntegration.cs
index 898e5b39db..1a1eadc558 100644
--- a/src/Sentry/Integrations/ISdkIntegration.cs
+++ b/src/Sentry/Integrations/ISdkIntegration.cs
@@ -15,3 +15,11 @@ public interface ISdkIntegration
/// The options.
public void Register(IHub hub, SentryOptions options);
}
+
+///
+/// Marker interface to indicate that an integration provides native Sentry tracing capabilities. We do NOT initialise
+/// these integrations when using OTEL instrumentation.
+///
+internal interface ISentryTracingIntegration
+{
+}
diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs
index be71b2f1d3..def0ca82d1 100644
--- a/src/Sentry/Internal/Hub.cs
+++ b/src/Sentry/Internal/Hub.cs
@@ -185,6 +185,12 @@ internal ITransactionTracer StartTransaction(
return NoOpTransaction.Instance;
}
+ if (_options.DisableSentryTracing)
+ {
+ _options.LogWarning("Sentry transaction dropped because OpenTelemetry is enabled");
+ return NoOpTransaction.Instance;
+ }
+
bool? isSampled = null;
double? sampleRate = null;
DiscardReason? discardReason = null;
@@ -306,6 +312,11 @@ public SentryTraceHeader GetTraceHeader()
public BaggageHeader GetBaggage()
{
+ if (_options.Instrumenter is Instrumenter.OpenTelemetry)
+ {
+ _options.LogWarning("GetBaggage should not be called when using OpenTelemetry - it may throw an exception");
+ }
+
var span = GetSpan();
if (span?.GetTransaction().GetDynamicSamplingContext() is { IsEmpty: false } dsc)
{
@@ -478,12 +489,15 @@ private void ApplyTraceContextToEvent(SentryEvent evt, ISpan span)
}
}
- private void ApplyTraceContextToEvent(SentryEvent evt, SentryPropagationContext propagationContext)
+ private void ApplyTraceContextToEvent(SentryEvent evt, IPropagationContext propagationContext)
{
evt.Contexts.Trace.TraceId = propagationContext.TraceId;
evt.Contexts.Trace.SpanId = propagationContext.SpanId;
evt.Contexts.Trace.ParentSpanId = propagationContext.ParentSpanId;
- evt.DynamicSamplingContext = propagationContext.GetOrCreateDynamicSamplingContext(_options, _replaySession);
+ if (_options.Instrumenter is Instrumenter.Sentry)
+ {
+ evt.DynamicSamplingContext = propagationContext.GetOrCreateDynamicSamplingContext(_options, _replaySession);
+ }
}
public bool CaptureEnvelope(Envelope envelope) => CurrentClient.CaptureEnvelope(envelope);
diff --git a/src/Sentry/Internal/IPropagationContext.cs b/src/Sentry/Internal/IPropagationContext.cs
new file mode 100644
index 0000000000..00f2c781af
--- /dev/null
+++ b/src/Sentry/Internal/IPropagationContext.cs
@@ -0,0 +1,12 @@
+namespace Sentry.Internal;
+
+internal interface IPropagationContext
+{
+ public DynamicSamplingContext? DynamicSamplingContext { get; }
+
+ public SentryId TraceId { get; }
+ public SpanId SpanId { get; }
+ public SpanId? ParentSpanId { get; }
+
+ public DynamicSamplingContext GetOrCreateDynamicSamplingContext(SentryOptions options, IReplaySession replaySession);
+}
diff --git a/src/Sentry/Scope.cs b/src/Sentry/Scope.cs
index 04fab75ff1..4638a4255a 100644
--- a/src/Sentry/Scope.cs
+++ b/src/Sentry/Scope.cs
@@ -1,11 +1,7 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
using Sentry.Extensibility;
using Sentry.Internal;
using Sentry.Internal.Extensions;
+using Sentry.Internal.OpenTelemetry;
namespace Sentry;
@@ -249,7 +245,7 @@ public ITransactionTracer? Transaction
}
}
- internal SentryPropagationContext PropagationContext { get; private set; }
+ internal IPropagationContext PropagationContext { get; private set; }
internal SessionUpdate? SessionUpdate { get; set; }
@@ -297,10 +293,10 @@ public Scope(SentryOptions? options)
{
}
- internal Scope(SentryOptions? options, SentryPropagationContext? propagationContext)
+ internal Scope(SentryOptions? options, IPropagationContext? propagationContext)
{
Options = options ?? new SentryOptions();
- PropagationContext = new SentryPropagationContext(propagationContext);
+ PropagationContext = Options.PropagationContextFactory(propagationContext);
}
// For testing. Should explicitly require SentryOptions.
@@ -420,7 +416,7 @@ public void Clear()
_extra.Clear();
_tags.Clear();
ClearAttachments();
- PropagationContext = new();
+ PropagationContext = Options.PropagationContextFactory(null);
}
///
diff --git a/src/Sentry/SentryGraphQLHttpMessageHandler.cs b/src/Sentry/SentryGraphQLHttpMessageHandler.cs
index 7666e46455..229ddafd5b 100644
--- a/src/Sentry/SentryGraphQLHttpMessageHandler.cs
+++ b/src/Sentry/SentryGraphQLHttpMessageHandler.cs
@@ -50,6 +50,12 @@ internal SentryGraphQLHttpMessageHandler(IHub? hub, SentryOptions? options,
}
request.SetFused(graphQlRequestContent);
+ if (_options?.DisableSentryTracing ?? false)
+ {
+ _options.LogDebug("Skipping span creation in SentryGraphQLHttpMessageHandler because OpenTelemetry is enabled");
+ return null;
+ }
+
// Start a span that tracks this request
// (may be null if transaction is not set on the scope)
var span = _hub.GetSpan()?.StartChild(
diff --git a/src/Sentry/SentryHttpMessageHandler.cs b/src/Sentry/SentryHttpMessageHandler.cs
index f75f958b8a..eb9d62f8f2 100644
--- a/src/Sentry/SentryHttpMessageHandler.cs
+++ b/src/Sentry/SentryHttpMessageHandler.cs
@@ -65,6 +65,12 @@ internal SentryHttpMessageHandler(IHub? hub, SentryOptions? options, HttpMessage
///
protected internal override ISpan? ProcessRequest(HttpRequestMessage request, string method, string url)
{
+ if (_options?.DisableSentryTracing ?? false)
+ {
+ _options.LogDebug("Skipping span creation in SentryHttpMessageHandler because OpenTelemetry is enabled");
+ return null;
+ }
+
// Start a span that tracks this request
// (may be null if transaction is not set on the scope)
var span = _hub.GetSpan()?.StartChild(
diff --git a/src/Sentry/SentryMessageHandler.cs b/src/Sentry/SentryMessageHandler.cs
index 9c8515e93e..72c0247a08 100644
--- a/src/Sentry/SentryMessageHandler.cs
+++ b/src/Sentry/SentryMessageHandler.cs
@@ -133,6 +133,15 @@ private void PropagateTraceHeaders(HttpRequestMessage request, string url, ISpan
}
}
+ // We only propogate trace headers for Sentry's native intstumentation. It isn't possible to propogate
+ // headers when OTEL instrumentation is used since the traceId can be SentryId.Empty if there is no active
+ // OTEL span... which would result in an exception being thrown when trying to create the
+ // DynamicSamplingContext.
+ if (_options?.Instrumenter is Instrumenter.Sentry)
+ {
+ return;
+ }
+
if (_options?.TracePropagationTargets.MatchesSubstringOrRegex(url) is true or null)
{
AddSentryTraceHeader(request, parentSpan);
diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs
index 7fc69a600f..bedcc39bf1 100644
--- a/src/Sentry/SentryOptions.cs
+++ b/src/Sentry/SentryOptions.cs
@@ -206,7 +206,7 @@ internal IEnumerable Integrations
#endif
#if HAS_DIAGNOSTIC_INTEGRATION
- if ((_defaultIntegrations & DefaultIntegrations.SentryDiagnosticListenerIntegration) != 0)
+ if (!DisableSentryTracing && (_defaultIntegrations & DefaultIntegrations.SentryDiagnosticListenerIntegration) != 0)
{
yield return new SentryDiagnosticListenerIntegration();
}
@@ -222,6 +222,10 @@ internal IEnumerable Integrations
foreach (var integration in _integrations)
{
+ if (DisableSentryTracing && integration is ISentryTracingIntegration)
+ {
+ continue;
+ }
yield return integration;
}
}
@@ -1156,6 +1160,20 @@ public StackTraceMode StackTraceMode
///
internal Instrumenter Instrumenter { get; set; } = Instrumenter.Sentry;
+ ///
+ /// During the transition period to OTLP we give SDK users the option to keep using Sentry's tracing in conjunction
+ /// with OTEL instrumentation. Setting this to true will disable Sentry's tracing entirely, which is the recommended
+ /// setting but would be a moajor change in behaviour, so we've made it opt-in for now.
+ /// TODO: Remove this option in a future major release and make it true / non-optional when using OTEL (i.e. implied by the Instrumenter)
+ ///
+ internal bool DisableSentryTracing { get; set; } = false;
+
+ ///
+ /// The default factory creates SentryPropagationContext instances... this should be replaced when using OTEL
+ ///
+ internal Func PropagationContextFactory { get; set; } = sourceContext =>
+ new SentryPropagationContext(sourceContext);
+
///
///
/// Set to `true` to prevents Sentry from automatically registering .
diff --git a/src/Sentry/SentryPropagationContext.cs b/src/Sentry/SentryPropagationContext.cs
index 6183262241..2ae4adba09 100644
--- a/src/Sentry/SentryPropagationContext.cs
+++ b/src/Sentry/SentryPropagationContext.cs
@@ -3,23 +3,23 @@
namespace Sentry;
-internal class SentryPropagationContext
+internal class SentryPropagationContext : IPropagationContext
{
public SentryId TraceId { get; }
public SpanId SpanId { get; }
public SpanId? ParentSpanId { get; }
- internal DynamicSamplingContext? _dynamicSamplingContext;
+ public DynamicSamplingContext? DynamicSamplingContext { get; private set; }
public DynamicSamplingContext GetOrCreateDynamicSamplingContext(SentryOptions options, IReplaySession replaySession)
{
- if (_dynamicSamplingContext is null)
+ if (DynamicSamplingContext is null)
{
options.LogDebug("Creating the Dynamic Sampling Context from the Propagation Context.");
- _dynamicSamplingContext = this.CreateDynamicSamplingContext(options, replaySession);
+ DynamicSamplingContext = this.CreateDynamicSamplingContext(options, replaySession);
}
- return _dynamicSamplingContext;
+ return DynamicSamplingContext;
}
internal SentryPropagationContext(
@@ -30,7 +30,7 @@ internal SentryPropagationContext(
TraceId = traceId;
SpanId = SpanId.Create();
ParentSpanId = parentSpanId;
- _dynamicSamplingContext = dynamicSamplingContext;
+ DynamicSamplingContext = dynamicSamplingContext;
}
public SentryPropagationContext()
@@ -39,13 +39,13 @@ public SentryPropagationContext()
SpanId = SpanId.Create();
}
- public SentryPropagationContext(SentryPropagationContext? other)
+ public SentryPropagationContext(IPropagationContext? other)
{
TraceId = other?.TraceId ?? SentryId.Create();
SpanId = other?.SpanId ?? SpanId.Create();
ParentSpanId = other?.ParentSpanId;
- _dynamicSamplingContext = other?._dynamicSamplingContext;
+ DynamicSamplingContext = other?.DynamicSamplingContext;
}
public static SentryPropagationContext CreateFromHeaders(IDiagnosticLogger? logger, SentryTraceHeader? traceHeader, BaggageHeader? baggageHeader, IReplaySession replaySession)
diff --git a/test/Sentry.OpenTelemetry.Tests/OpenTelemetryExtensionsTests.cs b/test/Sentry.OpenTelemetry.Tests/OpenTelemetryExtensionsTests.cs
index 636a54f9fb..580002204d 100644
--- a/test/Sentry.OpenTelemetry.Tests/OpenTelemetryExtensionsTests.cs
+++ b/test/Sentry.OpenTelemetry.Tests/OpenTelemetryExtensionsTests.cs
@@ -1,3 +1,5 @@
+using Sentry.Internal.OpenTelemetry;
+
namespace Sentry.OpenTelemetry.Tests;
public class OpenTelemetryExtensionsTests
diff --git a/test/Sentry.OpenTelemetry.Tests/OpenTelemetryTransactionProcessorTests.cs b/test/Sentry.OpenTelemetry.Tests/OpenTelemetryTransactionProcessorTests.cs
index 714caf1cb7..cffe5eb6c4 100644
--- a/test/Sentry.OpenTelemetry.Tests/OpenTelemetryTransactionProcessorTests.cs
+++ b/test/Sentry.OpenTelemetry.Tests/OpenTelemetryTransactionProcessorTests.cs
@@ -1,3 +1,5 @@
+using Sentry.Internal.OpenTelemetry;
+
namespace Sentry.OpenTelemetry.Tests;
public class OpenTelemetryTransactionProcessorTests : ActivitySourceTests
diff --git a/test/Sentry.OpenTelemetry.Tests/OtelPropagationContextTests.cs b/test/Sentry.OpenTelemetry.Tests/OtelPropagationContextTests.cs
new file mode 100644
index 0000000000..1d12319f12
--- /dev/null
+++ b/test/Sentry.OpenTelemetry.Tests/OtelPropagationContextTests.cs
@@ -0,0 +1,167 @@
+namespace Sentry.OpenTelemetry.Tests;
+
+public class OtelPropagationContextTests
+{
+ private class Fixture
+ {
+ public SentryId ActiveReplayId { get; } = SentryId.Create();
+ public IReplaySession ActiveReplaySession { get; }
+ public SentryOptions SentryOptions { get; }
+
+ public Fixture()
+ {
+ ActiveReplaySession = Substitute.For();
+ ActiveReplaySession.ActiveReplayId.Returns(ActiveReplayId);
+
+ SentryOptions = new SentryOptions { Dsn = "https://examplePublicKey@o0.ingest.sentry.io/123456" };
+ }
+ }
+
+ private readonly Fixture _fixture = new();
+
+ [Fact]
+ public void TraceId_NoActivityCurrent_ReturnsDefault()
+ {
+ // Arrange
+ var sut = new OtelPropagationContext();
+ Activity.Current = null;
+
+ // Act
+ var traceId = sut.TraceId;
+
+ // Assert
+ traceId.Should().Be(default(SentryId));
+ }
+
+ [Fact]
+ public void TraceId_WithActivityCurrent_ReturnsSentryIdFromActivityTraceId()
+ {
+ // Arrange
+ using var activity = new Activity("test").Start();
+ var sut = new OtelPropagationContext();
+
+ // Act
+ var traceId = sut.TraceId;
+
+ // Assert
+ traceId.Should().NotBe(default(SentryId));
+ traceId.Should().Be(activity.TraceId.AsSentryId());
+ }
+
+ [Fact]
+ public void SpanId_NoActivityCurrent_ReturnsDefault()
+ {
+ // Arrange
+ var sut = new OtelPropagationContext();
+ Activity.Current = null;
+
+ // Act
+ var spanId = sut.SpanId;
+
+ // Assert
+ spanId.Should().Be(default(SpanId));
+ }
+
+ [Fact]
+ public void SpanId_WithActivityCurrent_ReturnsSpanIdFromActivitySpanId()
+ {
+ // Arrange
+ using var activity = new Activity("test").Start();
+ var sut = new OtelPropagationContext();
+
+ // Act
+ var spanId = sut.SpanId;
+
+ // Assert
+ spanId.Should().NotBe(default(SpanId));
+ spanId.Should().Be(activity.SpanId.AsSentrySpanId());
+ }
+
+ [Fact]
+ public void ParentSpanId_NoActivityCurrent_ReturnsNull()
+ {
+ // Arrange
+ var sut = new OtelPropagationContext();
+ Activity.Current = null;
+
+ // Act
+ var parentSpanId = sut.ParentSpanId;
+
+ // Assert
+ parentSpanId.Should().BeNull();
+ }
+
+ [Fact]
+ public void ParentSpanId_WithActivityCurrent_ReturnsParentSpanIdFromActivity()
+ {
+ // Arrange
+ using var parentActivity = new Activity("parent").Start();
+ using var childActivity = new Activity("child").Start();
+ var sut = new OtelPropagationContext();
+
+ // Act
+ var parentSpanId = sut.ParentSpanId;
+
+ // Assert
+ parentSpanId.Should().NotBeNull();
+ parentSpanId.Should().Be(parentActivity.SpanId.AsSentrySpanId());
+ }
+
+ [Fact]
+ public void DynamicSamplingContext_ByDefault_IsNull()
+ {
+ // Arrange & Act
+ var sut = new OtelPropagationContext();
+
+ // Assert
+ sut.DynamicSamplingContext.Should().BeNull();
+ }
+
+ [Fact]
+ public void GetOrCreateDynamicSamplingContext_DynamicSamplingContextIsNull_CreatesDynamicSamplingContext()
+ {
+ // Arrange
+ using var activity = new Activity("test").Start();
+ var sut = new OtelPropagationContext();
+ sut.DynamicSamplingContext.Should().BeNull();
+
+ // Act
+ var result = sut.GetOrCreateDynamicSamplingContext(_fixture.SentryOptions, _fixture.ActiveReplaySession);
+
+ // Assert
+ result.Should().NotBeNull();
+ sut.DynamicSamplingContext.Should().NotBeNull();
+ sut.DynamicSamplingContext.Should().BeSameAs(result);
+ }
+
+ [Fact]
+ public void GetOrCreateDynamicSamplingContext_DynamicSamplingContextIsNotNull_ReturnsSameDynamicSamplingContext()
+ {
+ // Arrange
+ using var activity = new Activity("test").Start();
+ var sut = new OtelPropagationContext();
+ var firstResult = sut.GetOrCreateDynamicSamplingContext(_fixture.SentryOptions, _fixture.ActiveReplaySession);
+
+ // Act
+ var secondResult = sut.GetOrCreateDynamicSamplingContext(_fixture.SentryOptions, _fixture.ActiveReplaySession);
+
+ // Assert
+ firstResult.Should().BeSameAs(secondResult);
+ sut.DynamicSamplingContext.Should().BeSameAs(firstResult);
+ }
+
+ [Fact]
+ public void GetOrCreateDynamicSamplingContext_WithActiveReplaySession_IncludesReplayIdInDynamicSamplingContext()
+ {
+ // Arrange
+ using var activity = new Activity("test").Start();
+ var sut = new OtelPropagationContext();
+
+ // Act
+ var result = sut.GetOrCreateDynamicSamplingContext(_fixture.SentryOptions, _fixture.ActiveReplaySession);
+
+ // Assert
+ result.Should().NotBeNull();
+ result.Items.Should().Contain(kvp => kvp.Key == "replay_id" && kvp.Value == _fixture.ActiveReplayId.ToString());
+ }
+}
diff --git a/test/Sentry.OpenTelemetry.Tests/SentryPropagatorTests.cs b/test/Sentry.OpenTelemetry.Tests/SentryPropagatorTests.cs
index 693619aece..cdd24560a3 100644
--- a/test/Sentry.OpenTelemetry.Tests/SentryPropagatorTests.cs
+++ b/test/Sentry.OpenTelemetry.Tests/SentryPropagatorTests.cs
@@ -1,6 +1,7 @@
using Microsoft.Extensions.Primitives;
using OpenTelemetry;
using OpenTelemetry.Context.Propagation;
+using Sentry.Internal.OpenTelemetry;
namespace Sentry.OpenTelemetry.Tests;
diff --git a/test/Sentry.OpenTelemetry.Tests/TracerProviderBuilderExtensionsTests.cs b/test/Sentry.OpenTelemetry.Tests/TracerProviderBuilderExtensionsTests.cs
index d63f64c985..89e747432c 100644
--- a/test/Sentry.OpenTelemetry.Tests/TracerProviderBuilderExtensionsTests.cs
+++ b/test/Sentry.OpenTelemetry.Tests/TracerProviderBuilderExtensionsTests.cs
@@ -1,3 +1,6 @@
+using OpenTelemetry.Exporter;
+using OpenTelemetry.Trace;
+
namespace Sentry.OpenTelemetry.Tests;
public class TracerProviderBuilderExtensionsTests
@@ -91,4 +94,66 @@ public void ImplementationFactory_WithDisabledHub_ReturnsDisabledSpanProcessor()
// Assert
result.Should().BeOfType(); // FluentAssertions
}
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ [InlineData("foo")]
+ public void AddSentryOltp_InvalidDsn_ThrowsArgumentException(string dsn)
+ {
+ // Arrange
+ var tracerProviderBuilder = Substitute.For();
+
+ // Act
+ Action act = () => tracerProviderBuilder.AddSentryOtlp(dsn);
+
+ // Assert
+ act.Should().Throw()
+ .WithMessage($"{TracerProviderBuilderExtensions.MissingDsnWarning}*");
+ }
+
+ [Fact]
+ public void OtlpConfigurationCallback_SetsEndpointFromDsn()
+ {
+ // Arrange
+ var dsn = Dsn.Parse("https://examplePublicKey@o0.ingest.sentry.io/123456");
+ var options = new OtlpExporterOptions();
+
+ // Act
+ TracerProviderBuilderExtensions.OtlpConfigurationCallback(options, dsn);
+
+ // Assert
+ options.Endpoint.Should().Be(dsn.GetOtlpTracesEndpointUri());
+ }
+
+ [Fact]
+ public void OtlpConfigurationCallback_SetsProtocolToHttpProtobuf()
+ {
+ // Arrange
+ var dsn = Dsn.Parse("https://examplePublicKey@o0.ingest.sentry.io/123456");
+ var options = new OtlpExporterOptions();
+
+ // Act
+ TracerProviderBuilderExtensions.OtlpConfigurationCallback(options, dsn);
+
+ // Assert
+ options.Protocol.Should().Be(OtlpExportProtocol.HttpProtobuf);
+ }
+
+ [Fact]
+ public void OtlpConfigurationCallback_HttpClientFactoryCreatesClientWithSentryAuthHeader()
+ {
+ // Arrange
+ var dsn = Dsn.Parse("https://examplePublicKey@o0.ingest.sentry.io/123456");
+ var options = new OtlpExporterOptions();
+
+ // Act
+ TracerProviderBuilderExtensions.OtlpConfigurationCallback(options, dsn);
+ var client = options.HttpClientFactory!.Invoke();
+
+ // Assert
+ client.DefaultRequestHeaders.Should().Contain(h => h.Key == "X-Sentry-Auth");
+ var headerValues = client.DefaultRequestHeaders.GetValues("X-Sentry-Auth");
+ headerValues.Should().ContainSingle(v => v == $"sentry sentry_key={dsn.PublicKey}");
+ }
}
diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs
index c8bb6ea702..11171d8e0c 100644
--- a/test/Sentry.Tests/HubTests.cs
+++ b/test/Sentry.Tests/HubTests.cs
@@ -1510,8 +1510,8 @@ public void ContinueTrace_ReceivesHeaders_SetsPropagationContextAndReturnsTransa
{
scope.PropagationContext.TraceId.Should().Be(SentryId.Parse("5bd5f6d346b442dd9177dce9302fd737"));
scope.PropagationContext.ParentSpanId.Should().Be(SpanId.Parse("2000000000000000"));
- Assert.NotNull(scope.PropagationContext._dynamicSamplingContext);
- scope.PropagationContext._dynamicSamplingContext.Items.Should().Contain(baggageHeader.GetSentryMembers());
+ Assert.NotNull(scope.PropagationContext.DynamicSamplingContext);
+ scope.PropagationContext.DynamicSamplingContext.Items.Should().Contain(baggageHeader.GetSentryMembers());
});
transactionContext.TraceId.Should().Be(SentryId.Parse("5bd5f6d346b442dd9177dce9302fd737"));
@@ -1531,7 +1531,7 @@ public void ContinueTrace_DoesNotReceiveHeaders_CreatesRootTrace()
hub.ScopeManager.ConfigureScope(scope =>
{
Assert.Null(scope.PropagationContext.ParentSpanId);
- Assert.Null(scope.PropagationContext._dynamicSamplingContext);
+ Assert.Null(scope.PropagationContext.DynamicSamplingContext);
});
transactionContext.Name.Should().Be("test-name");
@@ -1564,8 +1564,8 @@ public void ContinueTrace_ReceivesHeadersAsStrings_SetsPropagationContextAndRetu
{
scope.PropagationContext.TraceId.Should().Be(SentryId.Parse("5bd5f6d346b442dd9177dce9302fd737"));
scope.PropagationContext.ParentSpanId.Should().Be(SpanId.Parse("2000000000000000"));
- Assert.NotNull(scope.PropagationContext._dynamicSamplingContext);
- scope.PropagationContext._dynamicSamplingContext.ToBaggageHeader().Members.Should().Contain(BaggageHeader.TryParse(baggageHeader)!.Members);
+ Assert.NotNull(scope.PropagationContext.DynamicSamplingContext);
+ scope.PropagationContext.DynamicSamplingContext.ToBaggageHeader().Members.Should().Contain(BaggageHeader.TryParse(baggageHeader)!.Members);
});
transactionContext.TraceId.Should().Be(SentryId.Parse("5bd5f6d346b442dd9177dce9302fd737"));
@@ -1585,7 +1585,7 @@ public void ContinueTrace_DoesNotReceiveHeadersAsStrings_CreatesRootTrace()
hub.ScopeManager.ConfigureScope(scope =>
{
Assert.Null(scope.PropagationContext.ParentSpanId);
- Assert.Null(scope.PropagationContext._dynamicSamplingContext);
+ Assert.Null(scope.PropagationContext.DynamicSamplingContext);
});
transactionContext.Name.Should().Be("test-name");
diff --git a/test/Sentry.Tests/SentryPropagationContextTests.cs b/test/Sentry.Tests/SentryPropagationContextTests.cs
index bb90359d0a..fa307db89a 100644
--- a/test/Sentry.Tests/SentryPropagationContextTests.cs
+++ b/test/Sentry.Tests/SentryPropagationContextTests.cs
@@ -32,12 +32,12 @@ public void CopyConstructor_CreatesCopyWithReplayId(bool replaySessionIsActive)
Assert.Equal(original.TraceId, copy.TraceId);
Assert.Equal(original.SpanId, copy.SpanId);
- Assert.Equal(original._dynamicSamplingContext!.Items.Count, copy._dynamicSamplingContext!.Items.Count);
- foreach (var dscItem in original._dynamicSamplingContext!.Items)
+ Assert.Equal(original.DynamicSamplingContext!.Items.Count, copy.DynamicSamplingContext!.Items.Count);
+ foreach (var dscItem in original.DynamicSamplingContext!.Items)
{
if (dscItem.Key == "replay_id")
{
- copy._dynamicSamplingContext!.Items["replay_id"].Should().Be(replaySessionIsActive
+ copy.DynamicSamplingContext!.Items["replay_id"].Should().Be(replaySessionIsActive
// We overwrite the replay_id when we have an active replay session
? _fixture.ActiveReplayId.ToString()
// Otherwise we propagate whatever was in the baggage header
@@ -45,7 +45,7 @@ public void CopyConstructor_CreatesCopyWithReplayId(bool replaySessionIsActive)
}
else
{
- copy._dynamicSamplingContext!.Items.Should()
+ copy.DynamicSamplingContext!.Items.Should()
.Contain(kvp => kvp.Key == dscItem.Key && kvp.Value == dscItem.Value);
}
}
@@ -59,18 +59,18 @@ public void GetOrCreateDynamicSamplingContext_DynamicSamplingContextIsNull_Creat
var options = new SentryOptions { Dsn = ValidDsn };
var propagationContext = new SentryPropagationContext();
- Assert.Null(propagationContext._dynamicSamplingContext); // Sanity check
+ Assert.Null(propagationContext.DynamicSamplingContext); // Sanity check
_ = propagationContext.GetOrCreateDynamicSamplingContext(options, replaySessionIsActive ? _fixture.ActiveReplaySession : _fixture.InactiveReplaySession);
- Assert.NotNull(propagationContext._dynamicSamplingContext);
+ Assert.NotNull(propagationContext.DynamicSamplingContext);
if (replaySessionIsActive)
{
// We add the replay_id automatically when we have an active replay session
- Assert.Equal(_fixture.ActiveReplayId.ToString(), Assert.Contains("replay_id", propagationContext._dynamicSamplingContext.Items));
+ Assert.Equal(_fixture.ActiveReplayId.ToString(), Assert.Contains("replay_id", propagationContext.DynamicSamplingContext.Items));
}
else
{
- Assert.DoesNotContain("replay_id", propagationContext._dynamicSamplingContext.Items);
+ Assert.DoesNotContain("replay_id", propagationContext.DynamicSamplingContext.Items);
}
}
@@ -95,7 +95,7 @@ public void CreateFromHeaders_HeadersNull_CreatesPropagationContextWithTraceAndS
Assert.NotEqual(propagationContext.TraceId, SentryId.Empty);
Assert.NotEqual(propagationContext.SpanId, SpanId.Empty);
- Assert.Null(propagationContext._dynamicSamplingContext);
+ Assert.Null(propagationContext.DynamicSamplingContext);
}
[Fact]
@@ -108,7 +108,7 @@ public void CreateFromHeaders_TraceHeaderNotNull_CreatesPropagationContextFromTr
Assert.Equal(traceHeader.TraceId, propagationContext.TraceId);
Assert.NotEqual(traceHeader.SpanId, propagationContext.SpanId); // Sanity check
Assert.Equal(traceHeader.SpanId, propagationContext.ParentSpanId);
- Assert.Null(propagationContext._dynamicSamplingContext);
+ Assert.Null(propagationContext.DynamicSamplingContext);
}
[Fact]
@@ -124,7 +124,7 @@ public void CreateFromHeaders_BaggageExistsButTraceHeaderNull_CreatesPropagation
var propagationContext = SentryPropagationContext.CreateFromHeaders(null, null, baggageHeader, _fixture.InactiveReplaySession);
- Assert.Null(propagationContext._dynamicSamplingContext);
+ Assert.Null(propagationContext.DynamicSamplingContext);
}
[Theory]