Logging:LogLevel
Set minimum log levels per category and per provider in configuration, predict which rule applies, and change levels per environment without a rebuild.
"Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } } | Logging__LogLevel__Default=Warning- Added in .NET ASP.NET Core 1.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$Defaultoptional | LogLevel= Information | Rule for categories no other rule matches. |
$<category prefix>optional | LogLevel | Rule for categories that start with this text, such as Microsoft.AspNetCore or Orders.Payments. The longest matching prefix wins. A * wildcard is allowed once. |
$Logging:<Provider>:LogLevel:<category>optional | LogLevel | Rule for one provider only (Console, Debug, EventSource, EventLog, or a provider alias). Beats the general rules for that provider. |
Return value
LogLevelFilter rules: for each logger category and provider, the minimum LogLevel that is written. Messages below it are dropped before formatting.
Errors
Nothing. An invalid level name fails binding with InvalidOperationException when the logger factory is first built. An unknown category or provider name is accepted silently.
Changelog
- PHP ASP.NET Core 2.0Filter rules (LoggerFilterOptions) bound from the Logging section and applied by the logger factory to every provider.
The Logging section is bound to LoggerFilterOptions. Every rule has three parts: a provider (or none), a category (or none) and a minimum level. A logger's category is the full type name for ILogger<OrdersService>, or the string passed to CreateLogger. Levels are Trace (0), Debug, Information, Warning, Error, Critical and None (6, turns a category off). Without any rule the minimum is Information.
How a rule is chosen
- Rules for the specific provider (
Logging:Console:LogLevel) are preferred over rules with no provider (Logging:LogLevel). - Among those, the rule with the longest category prefix that matches wins.
Defaultis the rule with no category. - Only one rule applies; levels are not combined.
Matching is a plain, case-insensitive string prefix, not a namespace match. A rule for Orders also matches a category called OrdersReporting. Write rules for a namespace that no unrelated category starts with, for example Orders.Payments rather than Orders when an OrdersReporting namespace also exists.
| Category | Typical production level | Why |
|---|---|---|
Default | Information | Your own business events |
Microsoft.AspNetCore | Warning | Per-request Information logs (request starting/finished, endpoint executing) are high volume |
Microsoft.Hosting.Lifetime | Information | Keeps "Now listening on" and "Application started" |
Microsoft.EntityFrameworkCore.Database.Command | Warning (Information to see SQL) | Every SQL command is an Information event |
System.Net.Http.HttpClient | Warning | Each outgoing call logs several Information lines |
Changing levels
CreateBuilder loads appsettings.json with reload on change and the logger factory listens for it, so editing the file on a VM changes levels within seconds. In containers the file is in the image, so teams set Logging__LogLevel__Default=Debug and restart one pod. For changes without a restart, mount a ConfigMap as a directory (say /config, not over /app/appsettings.json: that needs subPath, and subPath mounts never update), add it with builder.Configuration.AddJsonFile("/config/logging.json", optional: true, reloadOnChange: true), and set DOTNET_USE_POLLING_FILE_WATCHER=true, because the ConfigMap update is a symlink swap the default watcher often misses. Otherwise plan on a rollout restart. The production failure modes: Debug left on after an incident multiplies log volume and the bill; "Microsoft": "Warning" hides the lifetime messages people grep for during deploys; and Microsoft.EntityFrameworkCore.Database.Command at Information with EnableSensitiveDataLogging writes parameter values, including personal data, to the log store. A message below the threshold costs almost nothing only if you use message templates or LoggerMessage; an interpolated string is built before the level check.
Example
When to use it
- Silence per-request Microsoft.AspNetCore Information logs in production while keeping Microsoft.Hosting.Lifetime startup messages
- See the SQL EF Core sends for a slow orders query by raising Microsoft.EntityFrameworkCore.Database.Command to Information on one replica
- Write only warnings to the console in a container but keep Information for an OpenTelemetry exporter via provider-specific rules
Frequently asked questions
What is the default log level if nothing is configured?
Information. The templates also add Microsoft.AspNetCore at Warning in appsettings.json; without that rule every request writes several Information lines.
How do I set a category with dots from an environment variable?
Keep the dots and replace each colon with a double underscore: Logging__LogLevel__Microsoft.AspNetCore=Warning. Linux allows dots in variable names; some shells do not let you export them directly, but Kubernetes and Docker do.
Can I change the level without restarting?
Yes if the setting comes from a reloadable source: CreateBuilder loads appsettings.json with reload on change and the logger factory applies new rules. Environment variables are read once at startup.
More examples
Production appsettings.json
This is the usual production baseline: your code at Information, framework chatter at Warning, lifetime events kept. <code>Orders.Payments</code> at Debug is a temporary rule for an incident; remove it after, because nothing will remind you. The console formatter writes JSON lines that log collectors parse without regexes. There is deliberately no <code>Console:LogLevel</code> block: a provider-level <code>Default</code> beats every general category rule for that provider, so it would put the framework back at Information on the console.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore.Database.Command": "Warning",
"System.Net.Http.HttpClient": "Warning",
"Orders.Payments": "Debug"
},
"Console": {
"FormatterName": "json"
}
}
}Turning SQL logging on for one pod
Environment variable names may contain dots, and the double underscore becomes the colon, so this sets <code>Logging:LogLevel:Microsoft.EntityFrameworkCore.Database.Command</code>. One extra replica with verbose logs limits the volume and cost while you reproduce the issue.
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders-api-debug
spec:
replicas: 1
selector:
matchLabels:
app: orders-api
track: debug
template:
metadata:
labels:
app: orders-api
track: debug
spec:
containers:
- name: api
image: registry.example.com/orders-api:1.8.2
env:
- name: Logging__LogLevel__Microsoft.EntityFrameworkCore.Database.Command
value: Information
- name: Logging__LogLevel__Default
value: Debug
Templates keep filtered logs cheap
The category is the full name of <code>ShippingEndpoint</code>. Filtering happens inside <code>Log</code>, after the arguments are evaluated. The <code>LoggerMessage</code> source generator saves formatting and boxing when the level is off, but the caller still evaluates its arguments, so an expensive argument such as <code>BuildCarrierPayload(id)</code> needs an <code>IsEnabled</code> guard either way.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/orders/{id:int}/ship", (int id, ILogger<ShippingEndpoint> logger) =>
{
// Bad: the string is built even when Debug is filtered out.
// logger.LogDebug($"Shipping order {id} at {DateTime.UtcNow:O}");
// Good: formatting happens only if Debug is enabled for this category.
logger.LogDebug("Shipping order {OrderId}", id);
if (logger.IsEnabled(LogLevel.Trace))
{
logger.LogTrace("Carrier payload {Payload}", BuildCarrierPayload(id));
}
return TypedResults.Accepted($"/orders/{id}/shipment");
});
app.Run();
static string BuildCarrierPayload(int orderId) => $"{{\"order\":{orderId},\"carrier\":\"DHL\"}}";
public sealed class ShippingEndpoint;
Discussion