diff --git a/sentry-dotnet/5265/Dockerfile b/sentry-dotnet/5265/Dockerfile new file mode 100644 index 0000000..047b99b --- /dev/null +++ b/sentry-dotnet/5265/Dockerfile @@ -0,0 +1,13 @@ +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +WORKDIR /app +COPY *.csproj ./ +RUN dotnet restore +COPY . ./ +RUN dotnet publish -c Release -o /out + +FROM mcr.microsoft.com/dotnet/aspnet:10.0 +WORKDIR /app +COPY --from=build /out . +ENV ASPNETCORE_URLS=http://+:8080 +EXPOSE 8080 +ENTRYPOINT ["dotnet", "Sentry.Repro.dll"] diff --git a/sentry-dotnet/5265/Program.cs b/sentry-dotnet/5265/Program.cs new file mode 100644 index 0000000..e6a2ae9 --- /dev/null +++ b/sentry-dotnet/5265/Program.cs @@ -0,0 +1,107 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Sentry; +using Sentry.AspNetCore; +using Sentry.Extensibility; +using Serilog; + +var builder = WebApplication.CreateBuilder(args); + +builder.Host.UseSerilog((_, c) => + c.Enrich.FromLogContext() + .MinimumLevel.Debug() + .WriteTo.Console() + .WriteTo.Sentry()); + +builder.Services.AddTransient(); + +((IWebHostBuilder)builder.WebHost).UseSentry(o => +{ + o.Dsn = Environment.GetEnvironmentVariable("SENTRY_DSN") + ?? "https://examplePublicKey@o0.ingest.sentry.io/0"; + o.Debug = true; +}); + +var app = builder.Build(); + +app.Use(async (context, next) => +{ + var log = context.RequestServices.GetRequiredService() + .CreateLogger("ReproTest"); + + if (context.Request.Path == "/test") + { + ExampleEventProcessor.CallCount = 0; + + Console.WriteLine(); + Console.WriteLine("========================================"); + Console.WriteLine("=== Test 1: Static Serilog Logger ==="); + Console.WriteLine("========================================"); + // Goes through: Serilog -> WriteTo.Sentry() -> SentrySink -> SentrySdk.CaptureEvent + Log.Logger.Error("Error via static Serilog logger"); + + await Task.Delay(500); + var countAfterTest1 = ExampleEventProcessor.CallCount; + + Console.WriteLine(); + Console.WriteLine("========================================"); + Console.WriteLine("=== Test 2: ILogger (log.LogError) ==="); + Console.WriteLine("========================================"); + // Goes through: ILogger -> Serilog (via UseSerilog) -> WriteTo.Sentry() -> SentrySink + // BUG: ISentryEventProcessor should be called but reportedly is not + log.LogError(new InvalidOperationException("test exception"), "Error via ILogger"); + + await Task.Delay(500); + var countAfterTest2 = ExampleEventProcessor.CallCount; + + Console.WriteLine(); + Console.WriteLine("========================================"); + Console.WriteLine("=== Summary ==="); + Console.WriteLine("========================================"); + Console.WriteLine($"ExampleEventProcessor called after Test 1: {countAfterTest1} time(s)"); + Console.WriteLine($"ExampleEventProcessor called after Test 2: {countAfterTest2 - countAfterTest1} time(s)"); + + if (countAfterTest2 - countAfterTest1 == 0) + { + Console.WriteLine("BUG CONFIRMED: ISentryEventProcessor was NOT called for ILogger path!"); + } + else + { + Console.WriteLine("Bug NOT reproduced: ISentryEventProcessor was called for both paths."); + } + + context.Response.ContentType = "text/plain"; + await context.Response.WriteAsync( + $"Test 1 (static Serilog): ExampleEventProcessor called {countAfterTest1} time(s)\n" + + $"Test 2 (ILogger): ExampleEventProcessor called {countAfterTest2 - countAfterTest1} time(s)\n" + + "Check the server console for full details.\n"); + } + else + { + await next(); + } +}); + +app.MapGet("/", () => "Navigate to /test to trigger the reproduction"); + +app.Run(); + +sealed class ExampleEventProcessor : ISentryEventProcessor +{ + public static int CallCount; + + public SentryEvent Process(SentryEvent @event) + { + Interlocked.Increment(ref CallCount); + Console.WriteLine($">>> ExampleEventProcessor.Process CALLED! (call #{CallCount})"); + Console.WriteLine($" Message: {@event.Message?.Formatted ?? "(no message)"}"); + Console.WriteLine($" Exception: {@event.Exception?.Message ?? "(no exception)"}"); + return @event; + } +} diff --git a/sentry-dotnet/5265/README.md b/sentry-dotnet/5265/README.md new file mode 100644 index 0000000..22bcc56 --- /dev/null +++ b/sentry-dotnet/5265/README.md @@ -0,0 +1,55 @@ +# Reproduction for sentry-dotnet#5265 + +**Issue:** https://github.com/getsentry/sentry-dotnet/issues/5265 + +## Description + +The issue reports that `ISentryEventProcessor` implementations registered via DI +(`builder.Services.AddTransient()`) are **not** called +when logging through `Microsoft.Extensions.Logging.ILogger` (e.g., `log.LogError(...)`), but **are** +called when logging through the static Serilog logger (`Log.Logger.Error(...)`). + +## Steps to Reproduce + +1. Build and run with Docker: + ```bash + docker build -t sentry-repro-5265 . + docker run --rm -p 8085:8080 sentry-repro-5265 + ``` + + Optionally set a real Sentry DSN: + ```bash + export SENTRY_DSN=https://your-dsn@sentry.io/0 + docker run --rm -p 8085:8080 -e SENTRY_DSN sentry-repro-5265 + ``` + +2. Trigger the test: + ```bash + curl http://localhost:8085/test + ``` + +3. Check the server console output for `ExampleEventProcessor.Process CALLED!` messages. + +## Expected Behavior + +`ExampleEventProcessor.Process` should be called for **both**: +- Test 1: `Log.Logger.Error(...)` (static Serilog logger) +- Test 2: `log.LogError(...)` (ILogger from Microsoft.Extensions.Logging) + +## Actual Behavior + +According to the issue, `ExampleEventProcessor.Process` is only called for Test 1 (static Serilog), +**not** for Test 2 (ILogger). + +**Note:** In this reproduction (Sentry 6.5.0, .NET 10), the event processor IS called for both paths. +The issue author reports the bug when running from Visual Studio with a debugger attached. The bug may +be related to how the debugger interacts with the event processing pipeline, or to a specific +environment configuration. + +## Environment + +- .NET: 10.0 +- Sentry.AspNetCore: 6.5.0 +- Sentry.Serilog: 6.5.0 +- Serilog: 4.3.1 +- Serilog.AspNetCore: 10.0.0 diff --git a/sentry-dotnet/5265/Sentry.Repro.csproj b/sentry-dotnet/5265/Sentry.Repro.csproj new file mode 100644 index 0000000..7050c14 --- /dev/null +++ b/sentry-dotnet/5265/Sentry.Repro.csproj @@ -0,0 +1,15 @@ + + + + net10.0 + + + + + + + + + + +