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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETStandard'">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.0" />
</ItemGroup>

<!-- The lowest version with HostOptions: a per-runtime version makes apps that reference an older Microsoft.Extensions.Hosting fail with NU1605 -->
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' And '$(TargetFrameworkVersion)' == 'v8.0'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ private static void AddSentryGenericHostServices(
: new SentryStructuredLoggerProvider(c.GetRequiredService<IHub>()));
services.TryAddSingleton<HostLoggerProvidersMarker>();
services.AddSentry<SentryGenericHostOptions>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<HostOptions>, SentrySdkInitializer>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, SentryHostedService>(c =>
new SentryHostedService(c, app == App.Web && !HasHostIntegration(services))));

Expand Down
14 changes: 14 additions & 0 deletions src/Sentry.Extensions.Hosting/SentrySdkInitializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;

namespace Sentry.Extensions.Hosting;

/// <summary>
/// Starts the SDK while the host is built. The host's constructor reads <c>IOptions&lt;HostOptions&gt;</c>, which runs
/// this, so the SDK starts even when the app removes or replaces Sentry's logger providers.
/// </summary>
internal sealed class SentrySdkInitializer(IServiceProvider services) : IConfigureOptions<HostOptions>
{
public void Configure(HostOptions options) => _ = services.GetRequiredService<Func<IHub>>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<ILoggerFactory>(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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<IHostedService>(appService))
.UseSentry(Capture)
.ClearLoggerProviders();
.ClearLoggerProviders()
.ConfigureServices(RemoveSdkInitializer);
using var host = testHost.Build(kind);
SentrySdk.IsEnabled.Should().BeFalse();

Expand All @@ -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()
Expand All @@ -103,7 +107,8 @@ public async Task StartAsync_ServicesStartConcurrently_SdkRunsBeforeAppHostedSer
services.AddSingleton<IHostedService>(appService);
})
.UseSentry(Capture)
.ClearLoggerProviders();
.ClearLoggerProviders()
.ConfigureServices(RemoveSdkInitializer);
using var host = testHost.Build(kind);
SentrySdk.IsEnabled.Should().BeFalse();

Expand Down
Loading