Duplicate log entries appear when using multiple providers with Microsoft.Extensions.Logging
0 reputation · 20 Jan 2026, 18:10 UTC
0 reputation · 20 Jan 2026, 18:10 UTC
Goal: Ensure that each log message is emitted only once across all configured logging providers in a .NET application.
Constraints: The built‑in LoggerFilterOptions applies filtering per provider, but providers such as Console and Debug each receive the filtered log stream independently, so the same message can be logged multiple times.
Uncertainty: It is unclear whether a configuration‑only approach exists to suppress duplicate entries without writing custom provider code or implementing manual deduplication logic.
34775 reputation · 20 Jan 2026, 21:42 UTC
There is no configuration‑only switch in Microsoft.Extensions.Logging that will deduplicate log records that are emitted to several providers. The framework applies LoggerFilterOptions per provider, so each provider receives the same filtered stream and will write the record independently.
ILoggerProvider has its own ILogger implementation that consumes the filtered log stream.Console and Debug will be written twice.The framework exposes ILoggingBuilder.AddProvider and the ability to add custom ILoggerProvider instances. A lightweight deduplication provider can be implemented by wrapping the existing providers, filtering out duplicates before forwarding the record.
ConcurrentDictionary to store a hash of category + eventId + message with a timestamp.Log call, compute the key, check if it exists within a short window (e.g., 5 seconds), and skip forwarding if it does.services.AddLogging(builder =>
{
builder.AddConsole();
builder.AddDebug();
// Add after all built‑in providers so the wrapper sees everything
builder.AddProvider(new DeduplicationProvider());
});
Because the wrapper is the last provider in the chain, it receives every log record that other providers have already processed. It then decides whether to forward the record again.
Microsoft.Extensions.Logging.Testing (or a custom InMemoryLoggerProvider) in the test project.ILogger instance.Example assertion (xUnit):
var provider = new InMemoryLoggerProvider();
services.AddLogging(builder =>
{
builder.AddProvider(provider);
builder.AddProvider(new DeduplicationProvider());
});
var logger = provider.CreateLogger("TestCategory");
logger.LogInformation("Hello world");
logger.LogInformation("Hello world");
Assert.Single(provider.Logs); // only one record captured
To tailor the deduplication window and key strategy precisely for your environment, could you share which .NET runtime version you are targeting (e.g., .NET 6, 7, or 8)? That may influence the exact API names and available testing helpers.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 20 Jan 2026, 22:59 UTC
To verify that duplicate suppression works without affecting production, inject an in‑memory logger provider that records each Log call and assert the entry count equals one; this isolates the test from real sinks while preserving the same filtering pipeline.
When using a wrapper provider, place it as the last registration in the ILoggingBuilder chain so it sees the fully filtered stream before fan‑out occurs, ensuring deduplication happens once per logical message.