diff --git a/src/Sentry.Extensions.Hosting/Sentry.Extensions.Hosting.csproj b/src/Sentry.Extensions.Hosting/Sentry.Extensions.Hosting.csproj
index e03cb914fa..7ab457ec9e 100644
--- a/src/Sentry.Extensions.Hosting/Sentry.Extensions.Hosting.csproj
+++ b/src/Sentry.Extensions.Hosting/Sentry.Extensions.Hosting.csproj
@@ -20,6 +20,12 @@
+
+
+
+
+
+
diff --git a/src/Sentry.Extensions.Hosting/SentryHostBuilderExtensions.cs b/src/Sentry.Extensions.Hosting/SentryHostBuilderExtensions.cs
index bef06229fc..cc91cd755e 100644
--- a/src/Sentry.Extensions.Hosting/SentryHostBuilderExtensions.cs
+++ b/src/Sentry.Extensions.Hosting/SentryHostBuilderExtensions.cs
@@ -183,6 +183,7 @@ private static void AddSentryGenericHostServices(
: new SentryStructuredLoggerProvider(c.GetRequiredService()));
services.TryAddSingleton();
services.AddSentry();
+ services.TryAddEnumerable(ServiceDescriptor.Singleton, SentrySdkInitializer>());
services.TryAddEnumerable(ServiceDescriptor.Singleton(c =>
new SentryHostedService(c, app == App.Web && !HasHostIntegration(services))));
diff --git a/src/Sentry.Extensions.Hosting/SentrySdkInitializer.cs b/src/Sentry.Extensions.Hosting/SentrySdkInitializer.cs
new file mode 100644
index 0000000000..6c3d330af9
--- /dev/null
+++ b/src/Sentry.Extensions.Hosting/SentrySdkInitializer.cs
@@ -0,0 +1,14 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Options;
+
+namespace Sentry.Extensions.Hosting;
+
+///
+/// Starts the SDK while the host is built. The host's constructor reads IOptions<HostOptions>, which runs
+/// this, so the SDK starts even when the app removes or replaces Sentry's logger providers.
+///
+internal sealed class SentrySdkInitializer(IServiceProvider services) : IConfigureOptions
+{
+ public void Configure(HostOptions options) => _ = services.GetRequiredService>();
+}
diff --git a/test/Sentry.Extensions.Hosting.Tests/SentryHostBuilderExtensionsTests.cs b/test/Sentry.Extensions.Hosting.Tests/SentryHostBuilderExtensionsTests.cs
index 087b9bc587..715c7d0e10 100644
--- a/test/Sentry.Extensions.Hosting.Tests/SentryHostBuilderExtensionsTests.cs
+++ b/test/Sentry.Extensions.Hosting.Tests/SentryHostBuilderExtensionsTests.cs
@@ -2,6 +2,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Sentry.Extensions.Logging;
@@ -297,6 +298,30 @@ public void UseSentry_CapturedEvent_ReportsHostingSdk(BuilderKind kind)
sdk.Packages.Should().Contain(package => package.Name == "nuget:Sentry.Extensions.Hosting");
}
+ [Theory, MemberData(nameof(TestHost.Builders), MemberType = typeof(TestHost))]
+ public void UseSentry_LoggerProvidersClearedAfterwards_CapturesBeforeHostStarts(BuilderKind kind)
+ {
+ var testHost = new TestHost().UseSentry(Capture).ClearLoggerProviders();
+ using var host = testHost.Build(kind);
+
+ SentrySdk.CaptureMessage("captured before the host starts");
+
+ _events.Should().ContainSingle(e => e.Message!.Message == "captured before the host starts");
+ }
+
+ [Theory, MemberData(nameof(TestHost.Builders), MemberType = typeof(TestHost))]
+ public void UseSentry_LoggerFactoryReplaced_CapturesBeforeHostStarts(BuilderKind kind)
+ {
+ var testHost = new TestHost()
+ .UseSentry(Capture)
+ .ConfigureServices(services => services.AddSingleton(NullLoggerFactory.Instance));
+ using var host = testHost.Build(kind);
+
+ SentrySdk.CaptureMessage("captured before the host starts");
+
+ _events.Should().ContainSingle(e => e.Message!.Message == "captured before the host starts");
+ }
+
[Theory, MemberData(nameof(TestHost.Builders), MemberType = typeof(TestHost))]
public async Task UseSentry_EmptyDsnInJson_StartsHostWithSentryDisabled(BuilderKind kind)
{
diff --git a/test/Sentry.Extensions.Hosting.Tests/SentryHostedServiceTests.cs b/test/Sentry.Extensions.Hosting.Tests/SentryHostedServiceTests.cs
index 52b5df0c33..c410ffbced 100644
--- a/test/Sentry.Extensions.Hosting.Tests/SentryHostedServiceTests.cs
+++ b/test/Sentry.Extensions.Hosting.Tests/SentryHostedServiceTests.cs
@@ -74,14 +74,18 @@ public async Task StartAsync_WorkerHost_LogsNoWarning(BuilderKind kind)
standardError.ToString().Should().NotContain("Sentry:");
}
+ private static void RemoveSdkInitializer(IServiceCollection services)
+ => services.Remove(services.Single(d => d.ImplementationType == typeof(SentrySdkInitializer)));
+
[Theory, MemberData(nameof(TestHost.Builders), MemberType = typeof(TestHost))]
- public async Task StartAsync_LoggerProvidersCleared_SdkRunsBeforeAppHostedServiceStarts(BuilderKind kind)
+ public async Task StartAsync_SdkNotStartedDuringBuild_SdkRunsBeforeAppHostedServiceStarts(BuilderKind kind)
{
var appService = new SdkStateAtStart();
var testHost = new TestHost()
.ConfigureServices(services => services.AddSingleton(appService))
.UseSentry(Capture)
- .ClearLoggerProviders();
+ .ClearLoggerProviders()
+ .ConfigureServices(RemoveSdkInitializer);
using var host = testHost.Build(kind);
SentrySdk.IsEnabled.Should().BeFalse();
@@ -93,7 +97,7 @@ public async Task StartAsync_LoggerProvidersCleared_SdkRunsBeforeAppHostedServic
#if NET8_0_OR_GREATER
[Theory, MemberData(nameof(TestHost.Builders), MemberType = typeof(TestHost))]
- public async Task StartAsync_ServicesStartConcurrently_SdkRunsBeforeAppHostedServiceStarts(BuilderKind kind)
+ public async Task StartAsync_SdkNotStartedDuringBuildAndServicesStartConcurrently_SdkRunsBeforeAppHostedServiceStarts(BuilderKind kind)
{
var appService = new SdkStateAtStart();
var testHost = new TestHost()
@@ -103,7 +107,8 @@ public async Task StartAsync_ServicesStartConcurrently_SdkRunsBeforeAppHostedSer
services.AddSingleton(appService);
})
.UseSentry(Capture)
- .ClearLoggerProviders();
+ .ClearLoggerProviders()
+ .ConfigureServices(RemoveSdkInitializer);
using var host = testHost.Build(kind);
SentrySdk.IsEnabled.Should().BeFalse();