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
32 changes: 32 additions & 0 deletions src/Sentry.AspNetCore/BindableSentryAspNetCoreOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Sentry.Extensibility;
using Sentry.Extensions.Logging;

#if NETSTANDARD2_0
using IWebHostEnvironment = Microsoft.AspNetCore.Hosting.IHostingEnvironment;
#else
using Microsoft.Extensions.Hosting;
#endif

namespace Sentry.AspNetCore;

/// <inheritdoc cref="BindableSentryOptions"/>
internal class BindableSentryAspNetCoreOptions : BindableSentryLoggingOptions
{
public bool? IncludeActivityData { get; set; }
public RequestSize? MaxRequestBodySize { get; set; }
public bool? FlushOnCompletedRequest { get; set; }
public bool? FlushBeforeRequestCompleted { get; set; }
public bool? AdjustStandardEnvironmentNameCasing { get; set; }

public void ApplyTo(SentryAspNetCoreOptions options)
{
base.ApplyTo(options);
options.IncludeActivityData = IncludeActivityData ?? options.IncludeActivityData;
options.MaxRequestBodySize = MaxRequestBodySize ?? options.MaxRequestBodySize;
options.FlushOnCompletedRequest = FlushOnCompletedRequest ?? options.FlushOnCompletedRequest;
options.FlushBeforeRequestCompleted = FlushBeforeRequestCompleted ?? options.FlushBeforeRequestCompleted;
options.AdjustStandardEnvironmentNameCasing = AdjustStandardEnvironmentNameCasing ?? options.AdjustStandardEnvironmentNameCasing;
}
}
6 changes: 6 additions & 0 deletions src/Sentry.AspNetCore/Sentry.AspNetCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
<Description>Official ASP.NET Core integration for Sentry - Open-source error tracking that helps developers monitor and fix crashes in real time.</Description>
</PropertyGroup>

<PropertyGroup Condition="'$(FrameworkSupportsAot)' == 'true'">
<IsAotCompatible>true</IsAotCompatible>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Sentry.Extensions.Logging\Sentry.Extensions.Logging.csproj" />
</ItemGroup>
Expand All @@ -26,6 +31,7 @@

<ItemGroup Condition="'$(TargetFramework)' != 'netstandard2.0'">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
52 changes: 48 additions & 4 deletions src/Sentry.AspNetCore/SentryAspNetCoreOptionsSetup.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Options;
using Sentry.Extensions.Logging;
Expand All @@ -13,6 +14,7 @@ namespace Sentry.AspNetCore;
/// <summary>
/// Sets up ASP.NET Core option for Sentry.
/// </summary>
#if NETSTANDARD2_0
public class SentryAspNetCoreOptionsSetup : ConfigureFromConfigurationOptions<SentryAspNetCoreOptions>
{
/// <summary>
Expand All @@ -30,7 +32,53 @@ public SentryAspNetCoreOptionsSetup(
public override void Configure(SentryAspNetCoreOptions options)
{
base.Configure(options);
options.AddDiagnosticSourceIntegration();
options.DeduplicateUnhandledException();
}
}

#else
public class SentryAspNetCoreOptionsSetup : IConfigureOptions<SentryAspNetCoreOptions>
{
private readonly IConfiguration _config;

/// <summary>
/// Creates a new instance of <see cref="SentryAspNetCoreOptionsSetup"/>.
/// </summary>
public SentryAspNetCoreOptionsSetup(ILoggerProviderConfiguration<SentryAspNetCoreLoggerProvider> providerConfiguration)
: this(providerConfiguration.Configuration)
{
}

/// <summary>
/// Creates a new instance of <see cref="SentryAspNetCoreOptionsSetup"/>.
/// </summary>
internal SentryAspNetCoreOptionsSetup(IConfiguration config)
{
ArgumentNullException.ThrowIfNull(config);
_config = config;
}

/// <summary>
/// Configures the <see cref="SentryAspNetCoreOptions"/>.
/// </summary>
public void Configure(SentryAspNetCoreOptions options)
{
ArgumentNullException.ThrowIfNull(options);

var bindable = new BindableSentryAspNetCoreOptions();
_config.Bind(bindable);
bindable.ApplyTo(options);

options.DeduplicateUnhandledException();
}
}
#endif

internal static class SentryAspNetCoreOptionsExtensions
{
internal static void DeduplicateUnhandledException(this SentryAspNetCoreOptions options)
{
options.AddLogEntryFilter((category, _, eventId, _)
// https://github.com/aspnet/KestrelHttpServer/blob/0aff4a0440c2f393c0b98e9046a8e66e30a56cb0/src/Kestrel.Core/Internal/Infrastructure/KestrelTrace.cs#L33
// 13 = Application unhandled exception, which is captured by the middleware so the LogError of kestrel ends up as a duplicate with less info
Expand All @@ -39,9 +87,5 @@ public override void Configure(SentryAspNetCoreOptions options)
category,
"Microsoft.AspNetCore.Server.Kestrel",
StringComparison.Ordinal));

#if NETSTANDARD2_0
options.AddDiagnosticSourceIntegration();
#endif
}
}
8 changes: 8 additions & 0 deletions src/Sentry.AspNetCore/SentryTunnelMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Sentry.Internal.Extensions;

namespace Sentry.AspNetCore;

Expand Down Expand Up @@ -70,7 +71,14 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next)

try
{
#if NETSTANDARD2_0
var headerJson = JsonSerializer.Deserialize<Dictionary<string, object>>(header);
#else
var headerJson = JsonSerializer.Deserialize(
header,
SentryJsonContext.Default.DictionaryStringObject
);
#endif
if (headerJson == null)
{
response.StatusCode = StatusCodes.Status400BadRequest;
Expand Down
8 changes: 7 additions & 1 deletion src/Sentry.AspNetCore/SentryWebHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Configuration;
Expand Down Expand Up @@ -83,8 +84,13 @@ public static IWebHostBuilder UseSentry(
logging.AddConfiguration();

var section = context.Configuration.GetSection("Sentry");
#if NETSTANDARD2_0
_ = logging.Services.Configure<SentryAspNetCoreOptions>(section);

#else
_ = logging.Services.AddSingleton<IConfigureOptions<SentryAspNetCoreOptions>>(_ =>
new SentryAspNetCoreOptionsSetup(section)
);
#endif
_ = logging.Services
.AddSingleton<IConfigureOptions<SentryAspNetCoreOptions>, SentryAspNetCoreOptionsSetup>();
_ = logging.Services.AddSingleton<ILoggerProvider, SentryAspNetCoreLoggerProvider>();
Expand Down
1 change: 1 addition & 0 deletions src/Sentry/Internal/Extensions/JsonExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,7 @@ public static void WriteString(

[JsonSerializable(typeof(GrowableArray<int>))]
[JsonSerializable(typeof(Dictionary<string, bool>))]
[JsonSerializable(typeof(Dictionary<string, object>))]
internal partial class SentryJsonContext : JsonSerializerContext
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ namespace Sentry.AspNetCore
public Sentry.Extensibility.RequestSize MaxRequestBodySize { get; set; }
public Sentry.AspNetCore.TransactionNameProvider? TransactionNameProvider { get; set; }
}
public class SentryAspNetCoreOptionsSetup : Microsoft.Extensions.Options.ConfigureFromConfigurationOptions<Sentry.AspNetCore.SentryAspNetCoreOptions>
public class SentryAspNetCoreOptionsSetup : Microsoft.Extensions.Options.IConfigureOptions<Sentry.AspNetCore.SentryAspNetCoreOptions>
{
public SentryAspNetCoreOptionsSetup(Microsoft.Extensions.Logging.Configuration.ILoggerProviderConfiguration<Sentry.AspNetCore.SentryAspNetCoreLoggerProvider> providerConfiguration) { }
public override void Configure(Sentry.AspNetCore.SentryAspNetCoreOptions options) { }
public void Configure(Sentry.AspNetCore.SentryAspNetCoreOptions options) { }
}
public static class SentryBuilderExtensions
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ namespace Sentry.AspNetCore
public Sentry.Extensibility.RequestSize MaxRequestBodySize { get; set; }
public Sentry.AspNetCore.TransactionNameProvider? TransactionNameProvider { get; set; }
}
public class SentryAspNetCoreOptionsSetup : Microsoft.Extensions.Options.ConfigureFromConfigurationOptions<Sentry.AspNetCore.SentryAspNetCoreOptions>
public class SentryAspNetCoreOptionsSetup : Microsoft.Extensions.Options.IConfigureOptions<Sentry.AspNetCore.SentryAspNetCoreOptions>
{
public SentryAspNetCoreOptionsSetup(Microsoft.Extensions.Logging.Configuration.ILoggerProviderConfiguration<Sentry.AspNetCore.SentryAspNetCoreLoggerProvider> providerConfiguration) { }
public override void Configure(Sentry.AspNetCore.SentryAspNetCoreOptions options) { }
public void Configure(Sentry.AspNetCore.SentryAspNetCoreOptions options) { }
}
public static class SentryBuilderExtensions
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ namespace Sentry.AspNetCore
public Sentry.Extensibility.RequestSize MaxRequestBodySize { get; set; }
public Sentry.AspNetCore.TransactionNameProvider? TransactionNameProvider { get; set; }
}
public class SentryAspNetCoreOptionsSetup : Microsoft.Extensions.Options.ConfigureFromConfigurationOptions<Sentry.AspNetCore.SentryAspNetCoreOptions>
public class SentryAspNetCoreOptionsSetup : Microsoft.Extensions.Options.IConfigureOptions<Sentry.AspNetCore.SentryAspNetCoreOptions>
{
public SentryAspNetCoreOptionsSetup(Microsoft.Extensions.Logging.Configuration.ILoggerProviderConfiguration<Sentry.AspNetCore.SentryAspNetCoreLoggerProvider> providerConfiguration) { }
public override void Configure(Sentry.AspNetCore.SentryAspNetCoreOptions options) { }
public void Configure(Sentry.AspNetCore.SentryAspNetCoreOptions options) { }
}
public static class SentryBuilderExtensions
{
Expand Down
42 changes: 37 additions & 5 deletions test/Sentry.AspNetCore.Tests/SentryAspNetCoreOptionsSetupTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Configuration;

Expand All @@ -11,29 +12,60 @@ namespace Sentry.AspNetCore.Tests;

public class SentryAspNetCoreOptionsSetupTests
{
private readonly SentryAspNetCoreOptionsSetup _sut = new(
Substitute.For<ILoggerProviderConfiguration<SentryAspNetCoreLoggerProvider>>());
class Fixture
{
public Dictionary<string, string> Configuration { get; set; } = new();

public SentryAspNetCoreOptionsSetup GetSut()
{
var config = new ConfigurationBuilder()
.AddInMemoryCollection(Configuration)
.Build();
var loggingConfig = Substitute.For<ILoggerProviderConfiguration<SentryAspNetCoreLoggerProvider>>();
loggingConfig.Configuration.Returns(config);
return new(loggingConfig);
}
}

private readonly Fixture _fixture = new();
private readonly SentryAspNetCoreOptions _target = new();

[Fact]
public void Filters_KestrelApplicationEvent_NoException_Filtered()
{
_sut.Configure(_target);
// Arrange
var sut = _fixture.GetSut();

// Act
sut.Configure(_target);

//Assert
Assert.Contains(_target.Filters, f => f.Filter("Microsoft.AspNetCore.Server.Kestrel", LogLevel.Critical, 13, null));
}

[Fact]
public void Filters_KestrelApplicationEvent_WithException_Filtered()
{
_sut.Configure(_target);
// Arrange
var sut = _fixture.GetSut();

// Act
sut.Configure(_target);

// Assert
Assert.Contains(_target.Filters, f => f.Filter("Microsoft.AspNetCore.Server.Kestrel", LogLevel.Critical, 13, new Exception()));
}

[Fact]
public void Filters_KestrelEventId1_WithException_NotFiltered()
{
_sut.Configure(_target);
// Arrange
var sut = _fixture.GetSut();

// Act
sut.Configure(_target);

// Assert
Assert.DoesNotContain(_target.Filters, f => f.Filter("Microsoft.AspNetCore.Server.Kestrel", LogLevel.Trace, 1, null));
}

Expand Down