A Brief Introduction to .NET Default Logger and Optimization Techniques
TLDR
- .NET integrates Console, Debug, and EventSource providers by default, which can be customized via
builder.Logging. appsettings.jsonallows flexible control over log levels for different namespaces and providers, supporting the*wildcard.- For high-frequency or resource-intensive logs, prioritize using
logger.IsEnabledfor pre-checks to avoid unnecessary computations. - Structured Logging avoids performance overhead from string concatenation and facilitates parsing by systems like ELK.
- The
[LoggerMessage]Source Generator is recommended; it automatically generates strongly-typed delegates, eliminatesobject[]allocation and boxing costs, and includes built-inIsEnabledchecks.
Sample Project
Executable sample for this article: CloudyWing/DotNetLoggingSample.
Log Levels
During development and maintenance, choose the appropriate Log Level based on the context to facilitate filtering and debugging in production environments.
| Level | Value | Method | Description |
|---|---|---|---|
| Trace | 0 | LogTrace | Most detailed messages; disabled by default; should not be enabled in production. |
| Debug | 1 | LogDebug | Used for development debugging; use with caution in production. |
| Information | 2 | LogInformation | Tracks general application flow. |
| Warning | 3 | LogWarning | Handles abnormal or unexpected events that do not affect program operation. |
| Error | 4 | LogError | Unhandled exceptions. |
| Critical | 5 | LogCritical | Severe failures requiring immediate attention. |
| None | 6 | N/A | Disables all logs entirely. |
Basic Injection and Configuration
WebApplication.CreateBuilder() loads Console, Debug, and EventSource providers by default. If adjustments are needed, they can be configured via builder.Logging.
When to encounter this issue: When you need to customize log output targets or clear default providers.
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// Clear default providers and reconfigure
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();Controlling Log Levels via Appsettings.json
You can achieve fine-grained control by setting log levels for different namespaces (Categories) through configuration files.
When to encounter this issue: When you want to hide detailed logs for specific services in production, keeping only Warning level and above.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"LoggerTest.Services.TestService1": "Warning"
}
}
}- To apply the same settings to multiple namespaces, use wildcards, e.g.,
"*.Services": "Warning".
Improving Performance with Logger.IsEnabled
Before performing expensive operations (such as database queries), check if the log level is enabled.
When to encounter this issue: When log content requires complex calculations or large data processing to be generated.
if (logger.IsEnabled(LogLevel.Information)) {
int processedRecords = await database.GetProcessedRecordsCount();
logger.LogInformation("System has finished data update, processed {Count} records.", processedRecords);
}Advantages of Structured Logging
Structured logging separates templates from parameters, avoiding runtime string concatenation and outputting data in JSON format, which facilitates subsequent analysis.
When to encounter this issue: When you need to import logs into ELK or other centralized log analysis systems.
// Correct structured logging approach
logger.LogInformation("User {UserId} has logged into the system", user.Id);
// Output in JSON format
builder.Logging.AddJsonConsole();Using LoggerMessage.Define and Source Generator
Traditional LogInformation calls incur object[] allocation and boxing costs. Using the [LoggerMessage] Source Generator generates efficient, strongly-typed delegates at compile time.
When to encounter this issue: When the application has high performance requirements and a large volume of log output.
Example of using [LoggerMessage]
public partial class UserService {
private readonly ILogger<UserService> logger;
public UserService(ILogger<UserService> logger) => this.logger = logger;
public void Login(User user) => LogUserLoggedIn(user.Id, user.Department);
[LoggerMessage(
EventId = 1001,
Level = LogLevel.Information,
Message = "User {UserId} has logged into the system, department: {Department}"
)]
private partial void LogUserLoggedIn(string userId, string department);
}- Performance Advantage: Automatically generates
IsEnabledchecks at compile time, the call path has noobject[]allocation, and there is no boxing cost. - Naming Suggestion: Use verb phrases starting with
Log. The level is controlled by the Attribute, and parameter names must match the{Placeholder}in the Message template.
Change Log
- 2025-03-23 Initial document creation.
- 2026-05-27 Added "Using LoggerMessage.Define" and "Using LoggerMessage Source Generator" sections, and included a link to the sample project.