This guide shows how to set log levels and filtering rules directly in C# code without using appsettings.json. You can define fine-grained control as compile-time rules, such as “show warnings and above by default, but only show errors and above for specific namespaces.”
Table of Contents
- Sample Implementation: Inventory Management System
- Explanation
- Difference between SetMinimumLevel and AddFilter
- Priority of Code Settings
Sample Implementation: Inventory Management System
In the following example, the log level for the entire application (including System and Microsoft namespaces) is set to Warning. However, the custom WarehouseApp namespace is filtered to show only Error and above.
1. Program.cs (Host Configuration)
We use SetMinimumLevel and AddFilter within the ConfigureLogging method.
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
namespace WarehouseApp
{
class Program
{
static async Task Main(string[] args) =>
await CreateHostBuilder(args).Build().RunAsync();
private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging((context, logging) =>
{
// 1. Clear default log providers
logging.ClearProviders();
// 2. Add Console output
logging.AddConsole();
// 3. Set the global minimum log level to "Warning"
// This prevents Information and Debug logs from being displayed by default.
logging.SetMinimumLevel(LogLevel.Warning);
// 4. Add a filter for a specific category (namespace)
// Classes in the "WarehouseApp" namespace will only show "Error" and above.
logging.AddFilter("WarehouseApp", LogLevel.Error);
})
.ConfigureServices((context, services) =>
{
// Register the worker service
services.AddHostedService<InventoryWorker>();
});
}
}
2. InventoryWorker.cs (Logging Class)
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
namespace WarehouseApp
{
public class InventoryWorker : BackgroundService
{
private readonly ILogger<InventoryWorker> _logger;
public InventoryWorker(ILogger<InventoryWorker> logger)
{
_logger = logger;
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
// Based on the settings in Program.cs, logs below Error
// will be ignored in this class (WarehouseApp).
_logger.LogTrace("Trace: Starting inventory scan"); // Not displayed
_logger.LogDebug("Debug: Checking DB connection"); // Not displayed
_logger.LogInformation("Information: Running routine process"); // Not displayed
// The global setting is Warning, but the filter raises it to Error for this namespace.
_logger.LogWarning("Warning: Scan is taking too long"); // Not displayed
// Only the following levels are displayed
_logger.LogError("Error: Detected inventory data inconsistency");
_logger.LogCritical("Critical: Inventory DB is not responding");
return Task.CompletedTask;
}
}
}
3. Execution Result
Only Error and Critical logs are output to the console.
fail: WarehouseApp.InventoryWorker[0]
Error: Detected inventory data inconsistency
crit: WarehouseApp.InventoryWorker[0]
Critical: Inventory DB is not responding
Explanation
Difference between SetMinimumLevel and AddFilter
- logging.SetMinimumLevel(LogLevel.Warning): This sets the “floor” for the entire application. Logs lower than the specified level (Trace, Debug, Information) will not be output from any class.
- logging.AddFilter(“Category”, LogLevel.Error): This overrides the rules for a specific category (usually a namespace or class name). In this example, while the global level is
Warning, theWarehouseAppcode is restricted toErrorto suppress unnecessary warning logs during development.
Priority of Code Settings
If there is a conflict between appsettings.json and code settings, the configuration loaded last (or explicitly specified via AddConfiguration) usually takes effect. However, AddFilter defined in code acts as a very strong rule. It is recommended to use appsettings.json for behavior that changes per environment and code for rules that must always be followed.
