diff --git a/.craft.yml b/.craft.yml
index 45e4f66610..5875bcbd7c 100644
--- a/.craft.yml
+++ b/.craft.yml
@@ -20,5 +20,6 @@ targets:
nuget:Sentry.Maui:
nuget:Sentry.NLog:
nuget:Sentry.OpenTelemetry:
+ nuget:Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol:
nuget:Sentry.Serilog:
nuget:Sentry.Profiling:
diff --git a/.generated.NoMobile.slnx b/.generated.NoMobile.slnx
index 3ed20edf11..45de3e4077 100644
--- a/.generated.NoMobile.slnx
+++ b/.generated.NoMobile.slnx
@@ -142,6 +142,7 @@
+
@@ -171,6 +172,7 @@
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f699acc9d5..1819735499 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
## Unreleased
+### Features
+
+- Add support to send OTEL traces via OTLP ([#4899](https://github.com/getsentry/sentry-dotnet/pull/4899))
+
### Fixes
- The SDK now logs a `Warning` instead of an `Error` when being ratelimited ([#4927](https://github.com/getsentry/sentry-dotnet/pull/4927))
diff --git a/Sentry-CI-Build-Linux-NoMobile.slnf b/Sentry-CI-Build-Linux-NoMobile.slnf
index 69921c2bcb..c5a37bdd6e 100644
--- a/Sentry-CI-Build-Linux-NoMobile.slnf
+++ b/Sentry-CI-Build-Linux-NoMobile.slnf
@@ -44,6 +44,7 @@
"src\\Sentry.Hangfire\\Sentry.Hangfire.csproj",
"src\\Sentry.Log4Net\\Sentry.Log4Net.csproj",
"src\\Sentry.NLog\\Sentry.NLog.csproj",
+ "src\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj",
"src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj",
"src\\Sentry.Profiling\\Sentry.Profiling.csproj",
"src\\Sentry.Serilog\\Sentry.Serilog.csproj",
@@ -63,6 +64,7 @@
"test\\Sentry.Hangfire.Tests\\Sentry.Hangfire.Tests.csproj",
"test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.Tests.csproj",
"test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj",
+ "test\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj",
"test\\Sentry.OpenTelemetry.Tests\\Sentry.OpenTelemetry.Tests.csproj",
"test\\Sentry.Profiling.Tests\\Sentry.Profiling.Tests.csproj",
"test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj",
diff --git a/Sentry-CI-Build-Linux.slnf b/Sentry-CI-Build-Linux.slnf
index 14ffc09833..58caee81de 100644
--- a/Sentry-CI-Build-Linux.slnf
+++ b/Sentry-CI-Build-Linux.slnf
@@ -50,6 +50,7 @@
"src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj",
"src\\Sentry.Maui\\Sentry.Maui.csproj",
"src\\Sentry.NLog\\Sentry.NLog.csproj",
+ "src\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj",
"src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj",
"src\\Sentry.Profiling\\Sentry.Profiling.csproj",
"src\\Sentry.Serilog\\Sentry.Serilog.csproj",
@@ -72,6 +73,7 @@
"test\\Sentry.Maui.CommunityToolkit.Mvvm.Tests\\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj",
"test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj",
"test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj",
+ "test\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj",
"test\\Sentry.OpenTelemetry.Tests\\Sentry.OpenTelemetry.Tests.csproj",
"test\\Sentry.Profiling.Tests\\Sentry.Profiling.Tests.csproj",
"test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj",
diff --git a/Sentry-CI-Build-Windows-arm64.slnf b/Sentry-CI-Build-Windows-arm64.slnf
index 41d924ac5f..316108212f 100644
--- a/Sentry-CI-Build-Windows-arm64.slnf
+++ b/Sentry-CI-Build-Windows-arm64.slnf
@@ -52,6 +52,7 @@
"src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj",
"src\\Sentry.Maui\\Sentry.Maui.csproj",
"src\\Sentry.NLog\\Sentry.NLog.csproj",
+ "src\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj",
"src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj",
"src\\Sentry.Profiling\\Sentry.Profiling.csproj",
"src\\Sentry.Serilog\\Sentry.Serilog.csproj",
@@ -71,6 +72,7 @@
"test\\Sentry.Maui.CommunityToolkit.Mvvm.Tests\\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj",
"test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj",
"test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj",
+ "test\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj",
"test\\Sentry.OpenTelemetry.Tests\\Sentry.OpenTelemetry.Tests.csproj",
"test\\Sentry.Profiling.Tests\\Sentry.Profiling.Tests.csproj",
"test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj",
diff --git a/Sentry-CI-Build-Windows.slnf b/Sentry-CI-Build-Windows.slnf
index 1b158939c9..3d66c34922 100644
--- a/Sentry-CI-Build-Windows.slnf
+++ b/Sentry-CI-Build-Windows.slnf
@@ -52,6 +52,7 @@
"src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj",
"src\\Sentry.Maui\\Sentry.Maui.csproj",
"src\\Sentry.NLog\\Sentry.NLog.csproj",
+ "src\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj",
"src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj",
"src\\Sentry.Profiling\\Sentry.Profiling.csproj",
"src\\Sentry.Serilog\\Sentry.Serilog.csproj",
@@ -74,6 +75,7 @@
"test\\Sentry.Maui.CommunityToolkit.Mvvm.Tests\\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj",
"test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj",
"test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj",
+ "test\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj",
"test\\Sentry.OpenTelemetry.Tests\\Sentry.OpenTelemetry.Tests.csproj",
"test\\Sentry.Profiling.Tests\\Sentry.Profiling.Tests.csproj",
"test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj",
diff --git a/Sentry-CI-Build-macOS.slnf b/Sentry-CI-Build-macOS.slnf
index 08b1295241..a8d7cea54b 100644
--- a/Sentry-CI-Build-macOS.slnf
+++ b/Sentry-CI-Build-macOS.slnf
@@ -57,6 +57,7 @@
"src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj",
"src\\Sentry.Maui\\Sentry.Maui.csproj",
"src\\Sentry.NLog\\Sentry.NLog.csproj",
+ "src\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj",
"src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj",
"src\\Sentry.Profiling\\Sentry.Profiling.csproj",
"src\\Sentry.Serilog\\Sentry.Serilog.csproj",
@@ -80,6 +81,7 @@
"test\\Sentry.Maui.Device.TestApp\\Sentry.Maui.Device.TestApp.csproj",
"test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj",
"test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj",
+ "test\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj",
"test\\Sentry.OpenTelemetry.Tests\\Sentry.OpenTelemetry.Tests.csproj",
"test\\Sentry.Profiling.Tests\\Sentry.Profiling.Tests.csproj",
"test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj",
diff --git a/Sentry-CI-CodeQL.slnf b/Sentry-CI-CodeQL.slnf
index f155eb8373..f61f3cebf1 100644
--- a/Sentry-CI-CodeQL.slnf
+++ b/Sentry-CI-CodeQL.slnf
@@ -19,6 +19,7 @@
"src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj",
"src\\Sentry.Maui\\Sentry.Maui.csproj",
"src\\Sentry.NLog\\Sentry.NLog.csproj",
+ "src\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj",
"src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj",
"src\\Sentry.Profiling\\Sentry.Profiling.csproj",
"src\\Sentry.Serilog\\Sentry.Serilog.csproj",
diff --git a/Sentry.slnx b/Sentry.slnx
index 3ed20edf11..45de3e4077 100644
--- a/Sentry.slnx
+++ b/Sentry.slnx
@@ -142,6 +142,7 @@
+
@@ -171,6 +172,7 @@
+
diff --git a/SentryNoMobile.slnf b/SentryNoMobile.slnf
index a5db92897e..60fb62453e 100644
--- a/SentryNoMobile.slnf
+++ b/SentryNoMobile.slnf
@@ -46,6 +46,7 @@
"src\\Sentry.Hangfire\\Sentry.Hangfire.csproj",
"src\\Sentry.Log4Net\\Sentry.Log4Net.csproj",
"src\\Sentry.NLog\\Sentry.NLog.csproj",
+ "src\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj",
"src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj",
"src\\Sentry.Profiling\\Sentry.Profiling.csproj",
"src\\Sentry.Serilog\\Sentry.Serilog.csproj",
@@ -68,6 +69,7 @@
"test\\Sentry.Hangfire.Tests\\Sentry.Hangfire.Tests.csproj",
"test\\Sentry.Log4Net.Tests\\Sentry.Log4Net.Tests.csproj",
"test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj",
+ "test\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj",
"test\\Sentry.OpenTelemetry.Tests\\Sentry.OpenTelemetry.Tests.csproj",
"test\\Sentry.Profiling.Tests\\Sentry.Profiling.Tests.csproj",
"test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj",
diff --git a/SentryNoSamples.slnf b/SentryNoSamples.slnf
index 81a7c4243b..9605b36f6a 100644
--- a/SentryNoSamples.slnf
+++ b/SentryNoSamples.slnf
@@ -20,6 +20,7 @@
"src\\Sentry.Maui.CommunityToolkit.Mvvm\\Sentry.Maui.CommunityToolkit.Mvvm.csproj",
"src\\Sentry.Maui\\Sentry.Maui.csproj",
"src\\Sentry.NLog\\Sentry.NLog.csproj",
+ "src\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj",
"src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj",
"src\\Sentry.Profiling\\Sentry.Profiling.csproj",
"src\\Sentry.Serilog\\Sentry.Serilog.csproj",
@@ -44,6 +45,7 @@
"test\\Sentry.Maui.CommunityToolkit.Mvvm.Tests\\Sentry.Maui.CommunityToolkit.Mvvm.Tests.csproj",
"test\\Sentry.Maui.Tests\\Sentry.Maui.Tests.csproj",
"test\\Sentry.NLog.Tests\\Sentry.NLog.Tests.csproj",
+ "test\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests\\Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj",
"test\\Sentry.OpenTelemetry.Tests\\Sentry.OpenTelemetry.Tests.csproj",
"test\\Sentry.Profiling.Tests\\Sentry.Profiling.Tests.csproj",
"test\\Sentry.Serilog.Tests\\Sentry.Serilog.Tests.csproj",
diff --git a/samples/Sentry.Samples.AspNetCore.Blazor.Server/Sentry.Samples.AspNetCore.Blazor.Server.csproj b/samples/Sentry.Samples.AspNetCore.Blazor.Server/Sentry.Samples.AspNetCore.Blazor.Server.csproj
index 07200d7743..3a4426aa55 100644
--- a/samples/Sentry.Samples.AspNetCore.Blazor.Server/Sentry.Samples.AspNetCore.Blazor.Server.csproj
+++ b/samples/Sentry.Samples.AspNetCore.Blazor.Server/Sentry.Samples.AspNetCore.Blazor.Server.csproj
@@ -22,9 +22,9 @@
-
-
-
+
+
+
diff --git a/samples/Sentry.Samples.GraphQL.Server/Sentry.Samples.GraphQL.Server.csproj b/samples/Sentry.Samples.GraphQL.Server/Sentry.Samples.GraphQL.Server.csproj
index c7ed799d41..0ff52ef056 100644
--- a/samples/Sentry.Samples.GraphQL.Server/Sentry.Samples.GraphQL.Server.csproj
+++ b/samples/Sentry.Samples.GraphQL.Server/Sentry.Samples.GraphQL.Server.csproj
@@ -14,9 +14,9 @@
-
-
-
+
+
+
diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs
index e53d2b20b2..b834e5245d 100644
--- a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs
+++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs
@@ -3,10 +3,20 @@
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using Sentry.OpenTelemetry;
+using Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol;
using Sentry.Samples.OpenTelemetry.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
+#if SENTRY_DSN_DEFINED_IN_ENV
+var dsn = Environment.GetEnvironmentVariable("SENTRY_DSN")
+ ?? throw new InvalidOperationException("SENTRY_DSN environment variable is not set");
+#else
+// A DSN is required. You can set here in code, or you can set it in the SENTRY_DSN environment variable.
+// See https://docs.sentry.io/product/sentry-basics/dsn-explainer/
+var dsn = SamplesShared.Dsn;
+#endif
+
// OpenTelemetry Configuration
// See https://opentelemetry.io/docs/instrumentation/net/getting-started/
builder.Services.AddOpenTelemetry()
@@ -20,22 +30,17 @@
// 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 =>
{
-#if !SENTRY_DSN_DEFINED_IN_ENV
- // A DSN is required. You can set here in code, or you can set it in the SENTRY_DSN environment variable.
- // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/
- options.Dsn = SamplesShared.Dsn;
-#endif
-
+ options.Dsn = dsn;
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..87ed8ed44b 100644
--- a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Sentry.Samples.OpenTelemetry.AspNetCore.csproj
+++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Sentry.Samples.OpenTelemetry.AspNetCore.csproj
@@ -7,10 +7,11 @@
-
-
-
-
+
+
+
+
+
@@ -20,9 +21,10 @@
true
+
-
+
diff --git a/samples/Sentry.Samples.OpenTelemetry.AzureFunctions/Program.cs b/samples/Sentry.Samples.OpenTelemetry.AzureFunctions/Program.cs
index 7be6020a50..9a35d5a2a7 100644
--- a/samples/Sentry.Samples.OpenTelemetry.AzureFunctions/Program.cs
+++ b/samples/Sentry.Samples.OpenTelemetry.AzureFunctions/Program.cs
@@ -3,6 +3,16 @@
using Microsoft.Extensions.Logging;
using OpenTelemetry.Trace;
using Sentry.OpenTelemetry;
+using Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol;
+
+#if SENTRY_DSN_DEFINED_IN_ENV
+var dsn = Environment.GetEnvironmentVariable("SENTRY_DSN")
+ ?? throw new InvalidOperationException("SENTRY_DSN environment variable is not set");
+#else
+// A DSN is required. You can set here in code, or you can set it in the SENTRY_DSN environment variable.
+// See https://docs.sentry.io/product/sentry-basics/dsn-explainer/
+var dsn = SamplesShared.Dsn;
+#endif
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
@@ -11,7 +21,7 @@
services.AddOpenTelemetry().WithTracing(builder =>
{
builder
- .AddSentry() // <-- Configure OpenTelemetry to send traces to Sentry
+ .AddSentryOtlp(dsn) // <-- Configure OpenTelemetry to send traces to Sentry
.AddHttpClientInstrumentation(); // From OpenTelemetry.Instrumentation.Http... adds automatic tracing for outgoing HTTP requests
});
})
@@ -19,12 +29,9 @@
{
logging.AddSentry(options =>
{
-#if !SENTRY_DSN_DEFINED_IN_ENV
- // A DSN is required. You can set here in code, or you can set it in the SENTRY_DSN environment variable.
- options.Dsn = SamplesShared.Dsn;
-#endif
+ options.Dsn = dsn;
options.TracesSampleRate = 1.0;
- options.UseOpenTelemetry(); // <-- Configure Sentry to use open telemetry
+ options.UseOtlp(); // <-- Configure Sentry to use open telemetry
options.DisableSentryHttpMessageHandler = true; // So Sentry doesn't also create spans for outbound HTTP requests
options.Debug = true;
});
diff --git a/samples/Sentry.Samples.OpenTelemetry.AzureFunctions/Sentry.Samples.OpenTelemetry.AzureFunctions.csproj b/samples/Sentry.Samples.OpenTelemetry.AzureFunctions/Sentry.Samples.OpenTelemetry.AzureFunctions.csproj
index 564884ea00..775f9b74c2 100644
--- a/samples/Sentry.Samples.OpenTelemetry.AzureFunctions/Sentry.Samples.OpenTelemetry.AzureFunctions.csproj
+++ b/samples/Sentry.Samples.OpenTelemetry.AzureFunctions/Sentry.Samples.OpenTelemetry.AzureFunctions.csproj
@@ -10,9 +10,9 @@
-
-
-
+
+
+
@@ -31,9 +31,9 @@
true
+
-
-
+
diff --git a/samples/Sentry.Samples.OpenTelemetry.Console/Program.cs b/samples/Sentry.Samples.OpenTelemetry.Console/Program.cs
index 0ce193ad13..cc33278495 100644
--- a/samples/Sentry.Samples.OpenTelemetry.Console/Program.cs
+++ b/samples/Sentry.Samples.OpenTelemetry.Console/Program.cs
@@ -9,26 +9,31 @@
using OpenTelemetry;
using OpenTelemetry.Trace;
using Sentry.OpenTelemetry;
+using Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol;
+
+#if SENTRY_DSN_DEFINED_IN_ENV
+var dsn = Environment.GetEnvironmentVariable("SENTRY_DSN")
+ ?? throw new InvalidOperationException("SENTRY_DSN environment variable is not set");
+#else
+// A DSN is required. You can set here in code, or you can set it in the SENTRY_DSN environment variable.
+// See https://docs.sentry.io/product/sentry-basics/dsn-explainer/
+var dsn = SamplesShared.Dsn;
+#endif
var activitySource = new ActivitySource("Sentry.Samples.OpenTelemetry.Console");
SentrySdk.Init(options =>
{
-#if !SENTRY_DSN_DEFINED_IN_ENV
- // A DSN is required. You can set here in code, or you can set it in the SENTRY_DSN environment variable.
- // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/
- options.Dsn = SamplesShared.Dsn;
-#endif
-
+ options.Dsn = dsn;
options.Debug = true;
options.TracesSampleRate = 1.0;
- options.UseOpenTelemetry(); // <-- Configure Sentry to use OpenTelemetry trace information
+ options.UseOtlp(); // <-- Configure Sentry to use OpenTelemetry trace information
});
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddSource(activitySource.Name)
.AddHttpClientInstrumentation()
- .AddSentry() // <-- Configure OpenTelemetry to send traces to Sentry
+ .AddSentryOtlp(dsn) // <-- Configure OpenTelemetry to send traces to Sentry over OTLP
.Build();
Console.WriteLine("Hello World!");
@@ -42,7 +47,7 @@
task?.SetTag("Answer", 42);
Thread.Sleep(100); // simulate some work
Console.WriteLine("Task 1 completed");
- task?.SetStatus(Status.Ok);
+ task?.SetStatus(ActivityStatusCode.Ok);
}
// Since we use `AddHttpClientInstrumentation` when initializing OpenTelemetry, the following Http request will also
diff --git a/samples/Sentry.Samples.OpenTelemetry.Console/Sentry.Samples.OpenTelemetry.Console.csproj b/samples/Sentry.Samples.OpenTelemetry.Console/Sentry.Samples.OpenTelemetry.Console.csproj
index 0aed012a4f..b8aeeedef1 100644
--- a/samples/Sentry.Samples.OpenTelemetry.Console/Sentry.Samples.OpenTelemetry.Console.csproj
+++ b/samples/Sentry.Samples.OpenTelemetry.Console/Sentry.Samples.OpenTelemetry.Console.csproj
@@ -8,8 +8,8 @@
-
-
+
+
@@ -19,10 +19,10 @@
true
-
+
-
+
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.Exporter.OpenTelemetryProtocol/ActivityExtensions.cs b/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/ActivityExtensions.cs
new file mode 100644
index 0000000000..df986a1574
--- /dev/null
+++ b/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/ActivityExtensions.cs
@@ -0,0 +1,14 @@
+namespace Sentry.Internal;
+
+internal static class ActivityExtensions
+{
+ public static SpanId AsSentrySpanId(this ActivitySpanId id) => SpanId.Parse(id.ToHexString());
+
+ public static ActivitySpanId AsActivitySpanId(this SpanId id) =>
+ ActivitySpanId.CreateFromString(id.ToString().AsSpan());
+
+ public static SentryId AsSentryId(this ActivityTraceId id) => SentryId.Parse(id.ToHexString());
+
+ public static ActivityTraceId AsActivityTraceId(this SentryId id) =>
+ ActivityTraceId.CreateFromString(id.ToString().AsSpan());
+}
diff --git a/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/OtelPropagationContext.cs b/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/OtelPropagationContext.cs
new file mode 100644
index 0000000000..f73ba6cd56
--- /dev/null
+++ b/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/OtelPropagationContext.cs
@@ -0,0 +1,28 @@
+using Sentry.Extensibility;
+using Sentry.Internal;
+
+namespace Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol;
+
+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.Exporter.OpenTelemetryProtocol/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj b/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj
new file mode 100644
index 0000000000..16808d248a
--- /dev/null
+++ b/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj
@@ -0,0 +1,31 @@
+
+
+
+ Official OpenTelemetry integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time.
+ $(PackageTags);OpenTelemetry;OTLP
+ $(CurrentTfms);netstandard2.1;netstandard2.0;net462
+ enable
+
+ $(NoWarn);AD0001
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/SentryOptionsExtensions.cs b/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/SentryOptionsExtensions.cs
new file mode 100644
index 0000000000..3ecf286513
--- /dev/null
+++ b/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/SentryOptionsExtensions.cs
@@ -0,0 +1,62 @@
+using OpenTelemetry.Context.Propagation;
+using OpenTelemetry.Trace;
+
+namespace Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol;
+
+///
+/// OpenTelemetry Extensions for .
+///
+public static class SentryOptionsExtensions
+{
+ ///
+ /// 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. 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.
+ ///
+ ///
+ ///
+ /// 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.DisableSentryTracing = true;
+ options.PropagationContextFactory = _ => new OtelPropagationContext();
+ }
+}
diff --git a/src/Sentry.OpenTelemetry/SentryPropagator.cs b/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/SentryPropagator.cs
similarity index 93%
rename from src/Sentry.OpenTelemetry/SentryPropagator.cs
rename to src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/SentryPropagator.cs
index 78fd960d6b..fbadd80fff 100644
--- a/src/Sentry.OpenTelemetry/SentryPropagator.cs
+++ b/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/SentryPropagator.cs
@@ -2,6 +2,7 @@
using OpenTelemetry;
using OpenTelemetry.Context.Propagation;
using Sentry.Extensibility;
+using Sentry.Internal;
namespace Sentry.OpenTelemetry;
@@ -49,12 +50,12 @@ private static class OTelKeys
}
///
- public override PropagationContext Extract(PropagationContext context, T carrier, Func> getter)
+ public override PropagationContext Extract(PropagationContext context, T carrier, Func?> getter)
{
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)
@@ -121,13 +122,17 @@ public override void Inject(PropagationContext context, T carrier, Action(T carrier, Func> getter)
+ private static SentryTraceHeader? TryGetSentryTraceHeader(T carrier, Func?> getter)
{
var headerValue = getter(carrier, SentryTraceHeader.HttpHeaderName);
+ if (headerValue is null)
+ {
+ return null;
+ }
try
{
var value = new StringValues(headerValue.ToArray());
- return SentryTraceHeader.Parse(value);
+ return SentryTraceHeader.Parse(value!);
}
catch (Exception)
{
diff --git a/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/TracerProviderBuilderExtensions.cs b/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/TracerProviderBuilderExtensions.cs
new file mode 100644
index 0000000000..28d0d03b47
--- /dev/null
+++ b/src/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol/TracerProviderBuilderExtensions.cs
@@ -0,0 +1,69 @@
+using Microsoft.Extensions.DependencyInjection;
+using OpenTelemetry;
+using OpenTelemetry.Context.Propagation;
+using OpenTelemetry.Exporter;
+using OpenTelemetry.Trace;
+using Sentry.Extensibility;
+using Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol;
+
+namespace Sentry.OpenTelemetry;
+
+///
+/// Contains extension methods for the class.
+///
+public static class TracerProviderBuilderExtensions
+{
+ internal const string MissingDsnWarning = "Invalid DSN passed to AddSentryOTLP";
+
+ ///
+ ///
+ /// 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(MissingDsnWarning, 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.OpenTelemetry/OpenTelemetryExtensions.cs b/src/Sentry.OpenTelemetry/OpenTelemetryExtensions.cs
index 26970194ac..007a788e72 100644
--- a/src/Sentry.OpenTelemetry/OpenTelemetryExtensions.cs
+++ b/src/Sentry.OpenTelemetry/OpenTelemetryExtensions.cs
@@ -5,16 +5,6 @@ namespace Sentry.OpenTelemetry;
internal static class OpenTelemetryExtensions
{
- public static SpanId AsSentrySpanId(this ActivitySpanId id) => SpanId.Parse(id.ToHexString());
-
- public static ActivitySpanId AsActivitySpanId(this SpanId id) =>
- ActivitySpanId.CreateFromString(id.ToString().AsSpan());
-
- public static SentryId AsSentryId(this ActivityTraceId id) => SentryId.Parse(id.ToHexString());
-
- public static ActivityTraceId AsActivityTraceId(this SentryId id) =>
- ActivityTraceId.CreateFromString(id.ToString().AsSpan());
-
public static BaggageHeader AsBaggageHeader(this IEnumerable> baggage,
bool useSentryPrefix = false) =>
BaggageHeader.Create(
diff --git a/src/Sentry.OpenTelemetry/OpenTelemetryTransactionProcessor.cs b/src/Sentry.OpenTelemetry/OpenTelemetryTransactionProcessor.cs
index 4ab485dde1..d7d285eebb 100644
--- a/src/Sentry.OpenTelemetry/OpenTelemetryTransactionProcessor.cs
+++ b/src/Sentry.OpenTelemetry/OpenTelemetryTransactionProcessor.cs
@@ -1,4 +1,5 @@
using Sentry.Extensibility;
+using Sentry.Internal;
namespace Sentry.OpenTelemetry;
diff --git a/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj b/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj
index 6067738ff9..2b284b767d 100644
--- a/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj
+++ b/src/Sentry.OpenTelemetry/Sentry.OpenTelemetry.csproj
@@ -13,6 +13,13 @@
+
+
+
diff --git a/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs b/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs
index 2035787a19..8138facda1 100644
--- a/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs
+++ b/src/Sentry.OpenTelemetry/SentryOptionsExtensions.cs
@@ -24,17 +24,18 @@ 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 the Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol integration instead.
+ ///
+ public static void UseOpenTelemetry(this SentryOptions options, TracerProviderBuilder traceProviderBuilder,
+ TextMapPropagator? defaultTextMapPropagator = null, bool disableSentryTracing = false)
{
- options.Instrumenter = Instrumenter.OpenTelemetry;
- options.AddTransactionProcessor(
- new OpenTelemetryTransactionProcessor()
- );
-
+ options.UseOpenTelemetry(disableSentryTracing);
traceProviderBuilder.AddSentry(defaultTextMapPropagator);
}
@@ -46,12 +47,21 @@ public static void UseOpenTelemetry(
/// to ensure OpenTelemetry sends trace information to Sentry.
///
///
- /// instance
- public static void UseOpenTelemetry(this SentryOptions options)
+ /// 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 the Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol integration instead.
+ ///
+ public static void UseOpenTelemetry(this SentryOptions options, bool disableSentryTracing = false)
{
options.Instrumenter = Instrumenter.OpenTelemetry;
+ options.DisableSentryTracing = disableSentryTracing;
options.AddTransactionProcessor(
new OpenTelemetryTransactionProcessor()
- );
+ );
}
}
diff --git a/src/Sentry.OpenTelemetry/TracerProviderBuilderExtensions.cs b/src/Sentry.OpenTelemetry/TracerProviderBuilderExtensions.cs
index 96472eda98..cc5fb8e12b 100644
--- a/src/Sentry.OpenTelemetry/TracerProviderBuilderExtensions.cs
+++ b/src/Sentry.OpenTelemetry/TracerProviderBuilderExtensions.cs
@@ -12,9 +12,17 @@ namespace Sentry.OpenTelemetry;
public static class TracerProviderBuilderExtensions
{
///
- /// 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.
+ ///
+ ///
+ /// 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 +35,12 @@ public static class TracerProviderBuilderExtensions
///
///
/// The supplied for chaining.
- public static TracerProviderBuilder AddSentry(this TracerProviderBuilder tracerProviderBuilder, TextMapPropagator? defaultTextMapPropagator = null)
+ ///
+ /// This method of initialising the Sentry OpenTelemetry integration will be depricated in a future major release.
+ /// We recommend you use the Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol integration instead.
+ ///
+ public static TracerProviderBuilder AddSentry(this TracerProviderBuilder tracerProviderBuilder,
+ TextMapPropagator? defaultTextMapPropagator = null)
{
defaultTextMapPropagator ??= new SentryPropagator();
Sdk.SetDefaultTextMapPropagator(defaultTextMapPropagator);
@@ -36,7 +49,7 @@ public static TracerProviderBuilder AddSentry(this TracerProviderBuilder tracerP
internal static BaseProcessor ImplementationFactory(IServiceProvider services)
{
- List enrichers = new();
+ List enrichers = [];
// AspNetCoreEnricher
var userFactory = services.GetService();
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..b3b6a3fb62 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,8 +227,16 @@ public static DynamicSamplingContext CreateFromUnsampledTransaction(UnsampledTra
replaySession);
}
- public static DynamicSamplingContext CreateFromPropagationContext(SentryPropagationContext propagationContext, SentryOptions options, IReplaySession? replaySession)
+ ///
+ /// Creates a from the given .
+ ///
+ ///
+ /// This method should not be used when instrumenting with OTEL.
+ /// It will throw an exception if Activity.Current is null.
+ ///
+ public static DynamicSamplingContext CreateFromPropagationContext(IPropagationContext propagationContext, SentryOptions options, IReplaySession? replaySession)
{
+ Debug.Assert(options.Instrumenter is not Instrumenter.OpenTelemetry, "This method should not be used when instrumenting with OTEL.");
var traceId = propagationContext.TraceId;
var publicKey = options.ParsedDsn.PublicKey;
var release = options.SettingLocator.GetRelease();
@@ -257,6 +264,13 @@ 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 .
+ ///
+ ///
+ /// This method should not be used when instrumenting with OTEL.
+ /// It will throw an exception if Activity.Current is null.
+ ///
+ 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..2150fcc825 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,12 @@ public SentryTraceHeader GetTraceHeader()
public BaggageHeader GetBaggage()
{
+ if (_options.Instrumenter is Instrumenter.OpenTelemetry)
+ {
+ _options.LogWarning("GetBaggage should not be called when using OpenTelemetry.");
+ return BaggageHeader.Create([]);
+ }
+
var span = GetSpan();
if (span?.GetTransaction().GetDynamicSamplingContext() is { IsEmpty: false } dsc)
{
@@ -478,12 +490,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/Sentry.csproj b/src/Sentry/Sentry.csproj
index 2fce203ef5..3419f545b2 100644
--- a/src/Sentry/Sentry.csproj
+++ b/src/Sentry/Sentry.csproj
@@ -169,6 +169,8 @@
+
+
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..bf5f53176c 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.OpenTelemetry)
+ {
+ 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.Exporter.OpenTelemetryProtocol.Tests/ActivitySourceTests.cs b/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/ActivitySourceTests.cs
new file mode 100644
index 0000000000..97f76597aa
--- /dev/null
+++ b/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/ActivitySourceTests.cs
@@ -0,0 +1,29 @@
+using OpenTelemetry;
+using OpenTelemetry.Trace;
+using Sentry.OpenTelemetry.Tests.OpenTelemetry;
+
+namespace Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests;
+
+public abstract class ActivitySourceTests : IDisposable
+{
+ protected readonly ActivitySource Tracer;
+ private readonly TracerProvider _traceProvider;
+
+ protected ActivitySourceTests()
+ {
+ var activitySourceName = "SentrySpanProcessorTests";
+ var testSampler = new TestSampler();
+ Tracer = new ActivitySource(activitySourceName);
+ _traceProvider = Sdk.CreateTracerProviderBuilder()
+ .AddSource(activitySourceName)
+ .SetSampler(testSampler)
+ .Build();
+ }
+
+ public void Dispose()
+ {
+ _traceProvider?.Dispose();
+ Tracer.Dispose();
+ GC.SuppressFinalize(this);
+ }
+}
diff --git a/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry/ATTRIBUTION.txt b/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry/ATTRIBUTION.txt
new file mode 100644
index 0000000000..327c24d113
--- /dev/null
+++ b/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry/ATTRIBUTION.txt
@@ -0,0 +1,18 @@
+Parts of the code in this subdirectory have been adapted from
+https://github.com/open-telemetry/
+
+The original license is as follows:
+
+Copyright The OpenTelemetry Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry/TestSampler.cs b/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry/TestSampler.cs
new file mode 100644
index 0000000000..3e1c51ffb4
--- /dev/null
+++ b/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry/TestSampler.cs
@@ -0,0 +1,35 @@
+//
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+using OpenTelemetry.Trace;
+
+namespace Sentry.OpenTelemetry.Tests.OpenTelemetry
+{
+ // This is a copy of OpenTelemetry.Tests.TestSampler:
+ // https://github.com/open-telemetry/opentelemetry-dotnet/blob/b23b1460e96efb5ecd78d1b36c2e00e84de7086b/test/OpenTelemetry.Tests/Shared/TestSampler.cs
+ internal class TestSampler : Sampler
+ {
+ public Func SamplingAction { get; set; }
+
+ public SamplingParameters LatestSamplingParameters { get; private set; }
+
+ public override SamplingResult ShouldSample(in SamplingParameters samplingParameters)
+ {
+ LatestSamplingParameters = samplingParameters;
+ return SamplingAction?.Invoke(samplingParameters) ?? new SamplingResult(SamplingDecision.RecordAndSample);
+ }
+ }
+}
diff --git a/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtelPropagationContextTests.cs b/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtelPropagationContextTests.cs
new file mode 100644
index 0000000000..030a1a9fe9
--- /dev/null
+++ b/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtelPropagationContextTests.cs
@@ -0,0 +1,169 @@
+using Sentry.OpenTelemetry.Tests;
+
+namespace Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests;
+
+public class OtelPropagationContextTests : ActivitySourceTests
+{
+ 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 = Tracer.StartActivity();
+ 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 = Tracer.StartActivity();
+ 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 = Tracer.StartActivity();
+ 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 = Tracer.StartActivity();
+ 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 = Tracer.StartActivity();
+ 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.Exporter.OpenTelemetryProtocol.Tests/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj b/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj
new file mode 100644
index 0000000000..4a40648bb8
--- /dev/null
+++ b/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj
@@ -0,0 +1,29 @@
+
+
+
+ $(CurrentTfms)
+
+ $(TargetFrameworks);net48
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/Sentry.OpenTelemetry.Tests/SentryPropagatorTests.cs b/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/SentryPropagatorTests.cs
similarity index 98%
rename from test/Sentry.OpenTelemetry.Tests/SentryPropagatorTests.cs
rename to test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/SentryPropagatorTests.cs
index 693619aece..38cd0078c7 100644
--- a/test/Sentry.OpenTelemetry.Tests/SentryPropagatorTests.cs
+++ b/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/SentryPropagatorTests.cs
@@ -2,7 +2,7 @@
using OpenTelemetry;
using OpenTelemetry.Context.Propagation;
-namespace Sentry.OpenTelemetry.Tests;
+namespace Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests;
public class SentryPropagatorTests
{
diff --git a/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/TracerProviderBuilderExtensionsTests.cs b/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/TracerProviderBuilderExtensionsTests.cs
new file mode 100644
index 0000000000..b79bc7bca4
--- /dev/null
+++ b/test/Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/TracerProviderBuilderExtensionsTests.cs
@@ -0,0 +1,80 @@
+using OpenTelemetry.Exporter;
+using OpenTelemetry.Trace;
+
+namespace Sentry.OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests;
+
+public class TracerProviderBuilderExtensionsTests
+{
+ private class Fixture
+ {
+ public IServiceProvider ServiceProvider { get; } = Substitute.For();
+ public IHub Hub { get; } = Substitute.For();
+
+ public Fixture()
+ {
+ ServiceProvider.GetService(typeof(IHub)).Returns(Hub);
+ }
+ }
+
+ [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.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/Sentry.OpenTelemetry.Tests.csproj b/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj
index 5371e42135..278e9bf103 100644
--- a/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj
+++ b/test/Sentry.OpenTelemetry.Tests/Sentry.OpenTelemetry.Tests.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/test/Sentry.OpenTelemetry.Tests/TracerProviderBuilderExtensionsTests.cs b/test/Sentry.OpenTelemetry.Tests/TracerProviderBuilderExtensionsTests.cs
index d63f64c985..5ef804c08f 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
diff --git a/test/Sentry.Testing/SentryOptionsExtensions.cs b/test/Sentry.Testing/SentryOptionsExtensions.cs
index 7e18d2b8ab..6fe10016c3 100644
--- a/test/Sentry.Testing/SentryOptionsExtensions.cs
+++ b/test/Sentry.Testing/SentryOptionsExtensions.cs
@@ -31,6 +31,14 @@ private static SentryOptions ReceivedLog(this SentryOptions substitute, SentryLe
return substitute;
}
+ public static SentryOptions AddDiagnosticLoggerSubsititute(this SentryOptions substitute)
+ {
+ substitute.Debug = true;
+ substitute.DiagnosticLogger = Substitute.For();
+ substitute.DiagnosticLogger!.IsEnabled(Arg.Any()).Returns(true);
+ return substitute;
+ }
+
public static SentryOptions ReceivedLogDebug(this SentryOptions substitute)
=> ReceivedLog(substitute, SentryLevel.Debug);
diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs
index c8bb6ea702..34a963c0fe 100644
--- a/test/Sentry.Tests/HubTests.cs
+++ b/test/Sentry.Tests/HubTests.cs
@@ -1441,6 +1441,26 @@ public void GetBaggage_NoSpanActive_ReturnsBaggageFromPropagationContext()
Assert.Contains("sentry-trace_id=43365712692146d08ee11a729dfbcaca", baggage!.ToString());
}
+ [Fact]
+ public void GetBaggage_OtelInstrumenter_ReturnsEmptyBaggage()
+ {
+ // Arrange
+ _fixture.Options.Instrumenter = Instrumenter.OpenTelemetry;
+ _fixture.Options.AddDiagnosticLoggerSubsititute();
+ var hub = _fixture.GetSut();
+
+ // Act
+ var baggage = hub.GetBaggage();
+
+ // Assert
+ baggage.Members.Should().BeEmpty();
+ _fixture.Options.DiagnosticLogger.Received(1).Log(
+ SentryLevel.Warning,
+ Arg.Is(s => s.Contains("GetBaggage should not be called when using OpenTelemetry.")),
+ null,
+ Arg.Any