Logging in .NET Core and ASP.NET Core
By Kirk Larkin, Juergen Gutsch, and Rick Anderson
This article describes logging in .NET as it applies to ASP.NET Core apps. For detailed information on logging in .NET, see Logging in .NET. For more information on logging in Blazor apps, see ASP.NET Core Blazor logging.
Logging providers
Logging providers store logs, except for the Console
provider which displays logs. For example, the Azure Application Insights provider stores logs in Azure Application Insights. Multiple providers can be enabled.
The default ASP.NET Core web app templates call WebApplication.CreateBuilder, which adds the following logging providers:
- Console
- Debug
- EventSource
- EventLog: Windows only
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
The preceding code shows the Program.cs
file created with the ASP.NET Core web app templates. The next several sections provide samples based on the ASP.NET Core web app templates.
The following code overrides the default set of logging providers added by WebApplication.CreateBuilder
:
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Services.AddRazorPages();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Alternatively, the preceding logging code can be written as follows:
var builder = WebApplication.CreateBuilder();
builder.Host.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
});
For additional providers, see:
Create logs
To create logs, use an ILogger<TCategoryName> object from dependency injection (DI).
The following example:
- Creates a logger,
ILogger<AboutModel>
, which uses a log category of the fully qualified name of the typeAboutModel
. The log category is a string that is associated with each log. - Calls LogInformation to log at the Information level. The Log level indicates the severity of the logged event.
public class AboutModel : PageModel
{
private readonly ILogger _logger;
public AboutModel(ILogger<AboutModel> logger)
{
_logger = logger;
}
public void OnGet()
{
_logger.LogInformation("About page visited at {DT}",
DateTime.UtcNow.ToLongTimeString());
}
}
Levels and categories are explained in more detail later in this document.
For information on Blazor, see ASP.NET Core Blazor logging.
Configure logging
Logging configuration is commonly provided by the Logging
section of appsettings.{ENVIRONMENT}.json
files, where the {ENVIRONMENT}
placeholder is the environment. The following appsettings.Development.json
file is generated by the ASP.NET Core web app templates:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
In the preceding JSON:
- The
"Default"
and"Microsoft.AspNetCore"
categories are specified. - The
"Microsoft.AspNetCore"
category applies to all categories that start with"Microsoft.AspNetCore"
. For example, this setting applies to the"Microsoft.AspNetCore.Routing.EndpointMiddleware"
category. - The
"Microsoft.AspNetCore"
category logs at log levelWarning
and higher. - A specific log provider is not specified, so
LogLevel
applies to all the enabled logging providers except for the Windows EventLog.
The Logging
property can have LogLevel and log provider properties. The LogLevel
specifies the minimum level to log for selected categories. In the preceding JSON, Information
and Warning
log levels are specified. LogLevel
indicates the severity of the log and ranges from 0 to 6:
Trace
= 0, Debug
= 1, Information
= 2, Warning
= 3, Error
= 4, Critical
= 5, and None
= 6.
When a LogLevel
is specified, logging is enabled for messages at the specified level and higher. In the preceding JSON, the Default
category is logged for Information
and higher. For example, Information
, Warning
, Error
, and Critical
messages are logged. If no LogLevel
is specified, logging defaults to the Information
level. For more information, see Log levels.
A provider property can specify a LogLevel
property. LogLevel
under a provider specifies levels to log for that provider, and overrides the non-provider log settings. Consider the following appsettings.json
file:
{
"Logging": {
"LogLevel": { // All providers, LogLevel applies to all the enabled providers.
"Default": "Error", // Default logging, Error and higher.
"Microsoft": "Warning" // All Microsoft* categories, Warning and higher.
},
"Debug": { // Debug provider.
"LogLevel": {
"Default": "Information", // Overrides preceding LogLevel:Default setting.
"Microsoft.Hosting": "Trace" // Debug:Microsoft.Hosting category.
}
},
"EventSource": { // EventSource provider
"LogLevel": {
"Default": "Warning" // All categories of EventSource provider.
}
}
}
}
Settings in Logging.{PROVIDER NAME}.LogLevel
override settings in Logging.LogLevel
, where the {PROVIDER NAME}
placeholder is the provider name. In the preceding JSON, the Debug
provider's default log level is set to Information
:
Logging:Debug:LogLevel:Default:Information
The preceding setting specifies the Information
log level for every Logging:Debug:
category except Microsoft.Hosting
. When a specific category is listed, the specific category overrides the default category. In the preceding JSON, the Logging:Debug:LogLevel
categories "Microsoft.Hosting"
and "Default"
override the settings in Logging:LogLevel
.
The minimum log level can be specified for any of:
- Specific providers: For example,
Logging:EventSource:LogLevel:Default:Information
- Specific categories: For example,
Logging:LogLevel:Microsoft:Warning
- All providers and all categories:
Logging:LogLevel:Default:Warning
Any logs below the minimum level are not:
- Passed to the provider.
- Logged or displayed.
To suppress all logs, specify LogLevel.None. LogLevel.None
has a value of 6, which is higher than LogLevel.Critical
(5).
If a provider supports log scopes, IncludeScopes
indicates whether they're enabled. For more information, see log scopes.
The following appsettings.json
file contains all the providers enabled by default:
{
"Logging": {
"LogLevel": { // No provider, LogLevel applies to all the enabled providers.
"Default": "Error",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Warning"
},
"Debug": { // Debug provider.
"LogLevel": {
"Default": "Information" // Overrides preceding LogLevel:Default setting.
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft.AspNetCore.Mvc.Razor.Internal": "Warning",
"Microsoft.AspNetCore.Mvc.Razor.Razor": "Debug",
"Microsoft.AspNetCore.Mvc.Razor": "Error",
"Default": "Information"
}
},
"EventSource": {
"LogLevel": {
"Microsoft": "Information"
}
},
"EventLog": {
"LogLevel": {
"Microsoft": "Information"
}
},
"AzureAppServicesFile": {
"IncludeScopes": true,
"LogLevel": {
"Default": "Warning"
}
},
"AzureAppServicesBlob": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft": "Information"
}
},
"ApplicationInsights": {
"LogLevel": {
"Default": "Information"
}
}
}
}
In the preceding sample:
- The categories and levels aren't suggested values. The sample is provided to show all of the default providers.
- Settings in
Logging.{PROVIDER NAME}.LogLevel
override settings inLogging.LogLevel
, where the{PROVIDER NAME}
placeholder is the provider name. For example, the level inDebug.LogLevel.Default
overrides the level inLogLevel.Default
. - Each default provider alias is used. Each provider defines an alias that can be used in configuration in place of the fully qualified type name. The built-in providers aliases are:
Console
Debug
EventSource
EventLog
AzureAppServicesFile
AzureAppServicesBlob
ApplicationInsights
Log in Program.cs
The following example calls Builder.WebApplication.Logger in Program.cs
and logs informational messages:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Logger.LogInformation("Adding Routes");
app.MapGet("/", () => "Hello World!");
app.Logger.LogInformation("Starting the app");
app.Run();
The following example calls AddConsole in Program.cs
and logs the /Test
endpoint:
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddConsole();
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.MapGet("/Test", async (ILogger<Program> logger, HttpResponse response) =>
{
logger.LogInformation("Testing logging in Program.cs");
await response.WriteAsync("Testing");
});
app.Run();
The following example calls AddSimpleConsole in Program.cs
, disables color output, and logs the /Test
endpoint:
using Microsoft.Extensions.Logging.Console;
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddSimpleConsole(i => i.ColorBehavior = LoggerColorBehavior.Disabled);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.MapGet("/Test", async (ILogger<Program> logger, HttpResponse response) =>
{
logger.LogInformation("Testing logging in Program.cs");
await response.WriteAsync("Testing");
});
app.Run();
Set log level by command line, environment variables, and other configuration
Log level can be set by any of the configuration providers.
The :
separator doesn't work with environment variable hierarchical keys on all platforms. __
, the double underscore, is:
- Supported by all platforms. For example, the
:
separator is not supported by Bash, but__
is. - Automatically replaced by a
:
The following commands:
- Set the environment key
Logging:LogLevel:Microsoft
to a value ofInformation
on Windows. - Test the settings when using an app created with the ASP.NET Core web application templates. The
dotnet run
command must be run in the project directory after usingset
.
set Logging__LogLevel__Microsoft=Information
dotnet run
The preceding environment setting:
- Is only set in processes launched from the command window they were set in.
- Isn't read by browsers launched with Visual Studio.
The following setx command also sets the environment key and value on Windows. Unlike set
, setx
settings are persisted. The /M
switch sets the variable in the system environment. If /M
isn't used, a user environment variable is set.
setx Logging__LogLevel__Microsoft Information /M
Consider the following appsettings.json
file:
"Logging": {
"Console": {
"LogLevel": {
"Microsoft.Hosting.Lifetime": "Trace"
}
}
}
The following command sets the preceding configuration in the environment:
setx Logging__Console__LogLevel__Microsoft.Hosting.Lifetime Trace /M
Note
When configuring environment variables with names that contain .
(periods) in macOS and Linux, consider the "Exporting a variable with a dot (.) in it" question on Stack Exchange and its corresponding accepted answer.
On Azure App Service, select New application setting on the Settings > Configuration page. Azure App Service application settings are:
- Encrypted at rest and transmitted over an encrypted channel.
- Exposed as environment variables.
For more information, see Azure Apps: Override app configuration using the Azure portal.
For more information on setting ASP.NET Core configuration values using environment variables, see environment variables. For information on using other configuration sources, including the command line, Azure Key Vault, Azure App Configuration, other file formats, and more, see Configuration in ASP.NET Core.
How filtering rules are applied
When an ILogger<TCategoryName> object is created, the ILoggerFactory object selects a single rule per provider to apply to that logger. All messages written by an ILogger
instance are filtered based on the selected rules. The most specific rule for each provider and category pair is selected from the available rules.
The following algorithm is used for each provider when an ILogger
is created for a given category:
- Select all rules that match the provider or its alias. If no match is found, select all rules with an empty provider.
- From the result of the preceding step, select rules with longest matching category prefix. If no match is found, select all rules that don't specify a category.
- If multiple rules are selected, take the last one.
- If no rules are selected, use
MinimumLevel
.
Logging output from dotnet run and Visual Studio
Logs created with the default logging providers are displayed:
- In Visual Studio
- In the Debug output window when debugging.
- In the ASP.NET Core Web Server window.
- In the console window when the app is run with
dotnet run
.
Logs that begin with "Microsoft" categories are from ASP.NET Core framework code. ASP.NET Core and application code use the same logging API and providers.
Log category
When an ILogger
object is created, a category is specified. That category is included with each log message created by that instance of ILogger
. The category string is arbitrary, but the convention is to use the fully qualified class name. For example, in a controller the name might be "TodoApi.Controllers.TodoController"
. The ASP.NET Core web apps use ILogger<T>
to automatically get an ILogger
instance that uses the fully qualified type name of T
as the category:
public class PrivacyModel : PageModel
{
private readonly ILogger<PrivacyModel> _logger;
public PrivacyModel(ILogger<PrivacyModel> logger)
{
_logger = logger;
}
public void OnGet()
{
_logger.LogInformation("GET Pages.PrivacyModel called.");
}
}
If further categorization is desired, the convention is to use a hierarchical name by appending a subcategory to the fully qualified class name, and explicitly specify the category using ILoggerFactory.CreateLogger
:
public class ContactModel : PageModel
{
private readonly ILogger _logger;
public ContactModel(ILoggerFactory logger)
{
_logger = logger.CreateLogger("TodoApi.Pages.ContactModel.MyCategory");
}
public void OnGet()
{
_logger.LogInformation("GET Pages.ContactModel called.");
}
Calling CreateLogger
with a fixed name can be useful when used in multiple methods so the events can be organized by category.
ILogger<T>
is equivalent to calling CreateLogger
with the fully qualified type name of T
.
Log level
The following table lists the LogLevel values, the convenience Log{LogLevel}
extension method, and the suggested usage:
LogLevel | Value | Method | Description |
---|---|---|---|
Trace | 0 | LogTrace | Contain the most detailed messages. These messages may contain sensitive app data. These messages are disabled by default and should not be enabled in production. |
Debug | 1 | LogDebug | For debugging and development. Use with caution in production due to the high volume. |
Information | 2 | LogInformation | Tracks the general flow of the app. May have long-term value. |
Warning | 3 | LogWarning | For abnormal or unexpected events. Typically includes errors or conditions that don't cause the app to fail. |
Error | 4 | LogError | For errors and exceptions that cannot be handled. These messages indicate a failure in the current operation or request, not an app-wide failure. |
Critical | 5 | LogCritical | For failures that require immediate attention. Examples: data loss scenarios, out of disk space. |
None | 6 | Specifies that a logging category shouldn't write messages. |
In the previous table, the LogLevel
is listed from lowest to highest severity.
The Log method's first parameter, LogLevel, indicates the severity of the log. Rather than calling Log(LogLevel, ...)
, most developers call the Log{LOG LEVEL}
extension methods, where the {LOG LEVEL}
placeholder is the log level. For example, the following two logging calls are functionally equivalent and produce the same log:
[HttpGet]
public IActionResult Test1(int id)
{
var routeInfo = ControllerContext.ToCtxString(id);
_logger.Log(LogLevel.Information, MyLogEvents.TestItem, routeInfo);
_logger.LogInformation(MyLogEvents.TestItem, routeInfo);
return ControllerContext.MyDisplayRouteInfo();
}
MyLogEvents.TestItem
is the event ID. MyLogEvents
is part of the sample app and is displayed in the Log event ID section.
MyDisplayRouteInfo and ToCtxString are provided by the Rick.Docs.Samples.RouteInfo NuGet package. The methods display Controller
and Razor Page
route information.
The following code creates Information
and Warning
logs:
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
_logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
_logger.LogWarning(MyLogEvents.GetItemNotFound, "Get({Id}) NOT FOUND", id);
return NotFound();
}
return ItemToDTO(todoItem);
}
In the preceding code, the first Log{LOG LEVEL}
parameter,MyLogEvents.GetItem
, is the Log event ID. The second parameter is a message template with placeholders for argument values provided by the remaining method parameters. The method parameters are explained in the message template section later in this document.
Call the appropriate Log{LOG LEVEL}
method to control how much log output is written to a particular storage medium. For example:
- In production:
- Logging at the
Trace
,Debug
, orInformation
levels produces a high-volume of detailed log messages. To control costs and not exceed data storage limits, logTrace
,Debug
, orInformation
level messages to a high-volume, low-cost data store. Consider limitingTrace
,Debug
, orInformation
to specific categories. - Logging at
Warning
throughCritical
levels should produce few log messages.- Costs and storage limits usually aren't a concern.
- Few logs allow more flexibility in data store choices.
- Logging at the
- In development:
- Set to
Warning
. - Add
Trace
,Debug
, orInformation
messages when troubleshooting. To limit output, setTrace
,Debug
, orInformation
only for the categories under investigation.
- Set to
ASP.NET Core writes logs for framework events. For example, consider the log output for:
- A Razor Pages app created with the ASP.NET Core templates.
- Logging set to
Logging:Console:LogLevel:Microsoft:Information
. - Navigation to the Privacy page:
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/2 GET https://localhost:5001/Privacy
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint '/Privacy'
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[3]
Route matched with {page = "/Privacy"}. Executing page /Privacy
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[101]
Executing handler method DefaultRP.Pages.PrivacyModel.OnGet - ModelState is Valid
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[102]
Executed handler method OnGet, returned result .
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[103]
Executing an implicit handler method - ModelState is Valid
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[104]
Executed an implicit handler method, returned result Microsoft.AspNetCore.Mvc.RazorPages.PageResult.
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[4]
Executed page /Privacy in 74.5188ms
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint '/Privacy'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 149.3023ms 200 text/html; charset=utf-8
The following JSON sets Logging:Console:LogLevel:Microsoft:Information
:
{
"Logging": { // Default, all providers.
"LogLevel": {
"Microsoft": "Warning"
},
"Console": { // Console provider.
"LogLevel": {
"Microsoft": "Information"
}
}
}
}
Log event ID
Each log can specify an event ID. The sample app uses the MyLogEvents
class to define event IDs:
public class MyLogEvents
{
public const int GenerateItems = 1000;
public const int ListItems = 1001;
public const int GetItem = 1002;
public const int InsertItem = 1003;
public const int UpdateItem = 1004;
public const int DeleteItem = 1005;
public const int TestItem = 3000;
public const int GetItemNotFound = 4000;
public const int UpdateItemNotFound = 4001;
}
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
_logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
_logger.LogWarning(MyLogEvents.GetItemNotFound, "Get({Id}) NOT FOUND", id);
return NotFound();
}
return ItemToDTO(todoItem);
}
An event ID associates a set of events. For example, all logs related to displaying a list of items on a page might be 1001.
The logging provider may store the event ID in an ID field, in the logging message, or not at all. The Debug provider doesn't show event IDs. The console provider shows event IDs in brackets after the category:
info: TodoApi.Controllers.TodoItemsController[1002]
Getting item 1
warn: TodoApi.Controllers.TodoItemsController[4000]
Get(1) NOT FOUND
Some logging providers store the event ID in a field, which allows for filtering on the ID.
Log message template
Each log API uses a message template. The message template can contain placeholders for which arguments are provided. Use names for the placeholders, not numbers.
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
_logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
_logger.LogWarning(MyLogEvents.GetItemNotFound, "Get({Id}) NOT FOUND", id);
return NotFound();
}
return ItemToDTO(todoItem);
}
The order of the parameters, not their placeholder names, determines which parameters are used to provide placeholder values in log messages. In the following code, the parameter names are out of sequence in the placeholders of the message template:
var apples = 1;
var pears = 2;
var bananas = 3;
_logger.LogInformation("Parameters: {Pears}, {Bananas}, {Apples}", apples, pears, bananas);
However, the parameters are assigned to the placeholders in the order: apples
, pears
, bananas
. The log message reflects the order of the parameters:
Parameters: 1, 2, 3
This approach allows logging providers to implement semantic or structured logging. The arguments themselves are passed to the logging system, not just the formatted message template. This enables logging providers to store the parameter values as fields. For example, consider the following logger method:
_logger.LogInformation("Getting item {Id} at {RequestTime}", id, DateTime.Now);
For example, when logging to Azure Table Storage:
- Each Azure Table entity can have
ID
andRequestTime
properties. - Tables with properties simplify queries on logged data. For example, a query can find all logs within a particular
RequestTime
range without having to parse the time out of the text message.
Log exceptions
The logger methods have overloads that take an exception parameter:
[HttpGet("{id}")]
public IActionResult TestExp(int id)
{
var routeInfo = ControllerContext.ToCtxString(id);
_logger.LogInformation(MyLogEvents.TestItem, routeInfo);
try
{
if (id == 3)
{
throw new Exception("Test exception");
}
}
catch (Exception ex)
{
_logger.LogWarning(MyLogEvents.GetItemNotFound, ex, "TestExp({Id})", id);
return NotFound();
}
return ControllerContext.MyDisplayRouteInfo();
}
MyDisplayRouteInfo and ToCtxString are provided by the Rick.Docs.Samples.RouteInfo NuGet package. The methods display Controller
and Razor Page
route information.
Exception logging is provider-specific.
Default log level
If the default log level is not set, the default log level value is Information
.
For example, consider the following web app:
- Created with the ASP.NET web app templates.
appsettings.json
andappsettings.Development.json
deleted or renamed.
With the preceding setup, navigating to the privacy or home page produces many Trace
, Debug
, and Information
messages with Microsoft
in the category name.
The following code sets the default log level when the default log level is not set in configuration:
var builder = WebApplication.CreateBuilder();
builder.Logging.SetMinimumLevel(LogLevel.Warning);
Generally, log levels should be specified in configuration and not code.
Filter function
A filter function is invoked for all providers and categories that don't have rules assigned to them by configuration or code:
var builder = WebApplication.CreateBuilder();
builder.Logging.AddFilter((provider, category, logLevel) =>
{
if (provider.Contains("ConsoleLoggerProvider")
&& category.Contains("Controller")
&& logLevel >= LogLevel.Information)
{
return true;
}
else if (provider.Contains("ConsoleLoggerProvider")
&& category.Contains("Microsoft")
&& logLevel >= LogLevel.Information)
{
return true;
}
else
{
return false;
}
});
The preceding code displays console logs when the category contains Controller
or Microsoft
and the log level is Information
or higher.
Generally, log levels should be specified in configuration and not code.
ASP.NET Core and EF Core categories
The following table contains some categories used by ASP.NET Core and Entity Framework Core, with notes about the logs:
Category | Notes |
---|---|
Microsoft.AspNetCore |
General ASP.NET Core diagnostics. |
Microsoft.AspNetCore.DataProtection |
Which keys were considered, found, and used. |
Microsoft.AspNetCore.HostFiltering |
Hosts allowed. |
Microsoft.AspNetCore.Hosting |
How long HTTP requests took to complete and what time they started. Which hosting startup assemblies were loaded. |
Microsoft.AspNetCore.Mvc |
MVC and Razor diagnostics. Model binding, filter execution, view compilation, action selection. |
Microsoft.AspNetCore.Routing |
Route matching information. |
Microsoft.AspNetCore.Server |
Connection start, stop, and keep alive responses. HTTPS certificate information. |
Microsoft.AspNetCore.StaticFiles |
Files served. |
Microsoft.EntityFrameworkCore |
General Entity Framework Core diagnostics. Database activity and configuration, change detection, migrations. |
To view more categories in the console window, set appsettings.Development.json
to the following:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Trace",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
Log scopes
A scope can group a set of logical operations. This grouping can be used to attach the same data to each log that's created as part of a set. For example, every log created as part of processing a transaction can include the transaction ID.
A scope:
- Is an IDisposable type that's returned by the BeginScope method.
- Lasts until it's disposed.
The following providers support scopes:
Use a scope by wrapping logger calls in a using
block:
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
TodoItem todoItem;
var transactionId = Guid.NewGuid().ToString();
using (_logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("TransactionId", transactionId),
}))
{
_logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);
todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
_logger.LogWarning(MyLogEvents.GetItemNotFound,
"Get({Id}) NOT FOUND", id);
return NotFound();
}
}
return ItemToDTO(todoItem);
}
Built-in logging providers
ASP.NET Core includes the following logging providers as part of the shared framework:
The following logging providers are shipped by Microsoft, but not as part of the shared framework. They must be installed as additional nuget.
ASP.NET Core doesn't include a logging provider for writing logs to files. To write logs to files from an ASP.NET Core app, consider using a third-party logging provider.
For information on stdout
and debug logging with the ASP.NET Core Module, see Troubleshoot ASP.NET Core on Azure App Service and IIS and ASP.NET Core Module (ANCM) for IIS.
Console
The Console
provider logs output to the console. For more information on viewing Console
logs in development, see Logging output from dotnet run and Visual Studio.
Debug
The Debug
provider writes log output by using the System.Diagnostics.Debug class. Calls to System.Diagnostics.Debug.WriteLine
write to the Debug
provider.
On Linux, the Debug
provider log location is distribution-dependent and may be one of the following:
/var/log/message
/var/log/syslog
Event Source
The EventSource
provider writes to a cross-platform event source with the name Microsoft-Extensions-Logging
. On Windows, the provider uses ETW.
dotnet-trace tooling
The dotnet-trace
tool is a cross-platform CLI global tool that enables the collection of .NET Core traces of a running process. The tool collects Microsoft.Extensions.Logging.EventSource provider data using a LoggingEventSource.
For installation instructions, see dotnet-trace
.
Use the dotnet-trace
tooling to collect a trace from an app:
Run the app with the
dotnet run
command.Determine the process identifier (PID) of the .NET Core app:
dotnet-trace ps
Find the PID for the process that has the same name as the app's assembly.
Execute the
dotnet-trace
command.General command syntax:
dotnet-trace collect -p {PID} --providers Microsoft-Extensions-Logging:{Keyword}:{Provider Level} :FilterSpecs=\" {Logger Category 1}:{Category Level 1}; {Logger Category 2}:{Category Level 2}; ... {Logger Category N}:{Category Level N}\"
When using a PowerShell command shell, enclose the
--providers
value in single quotes ('
):dotnet-trace collect -p {PID} --providers 'Microsoft-Extensions-Logging:{Keyword}:{Provider Level} :FilterSpecs=\" {Logger Category 1}:{Category Level 1}; {Logger Category 2}:{Category Level 2}; ... {Logger Category N}:{Category Level N}\"'
On non-Windows platforms, add the
-f speedscope
option to change the format of the output trace file tospeedscope
.The following table defines the Keyword:
Keyword Description 1 Log meta events about the LoggingEventSource
. Doesn't log events fromILogger
.2 Turns on the Message
event whenILogger.Log()
is called. Provides information in a programmatic (not formatted) way.4 Turns on the FormatMessage
event whenILogger.Log()
is called. Provides the formatted string version of the information.8 Turns on the MessageJson
event whenILogger.Log()
is called. Provides a JSON representation of the arguments.The following table lists the provider levels:
Provider Level Description 0 LogAlways
1 Critical
2 Error
3 Warning
4 Informational
5 Verbose
The parsing for a category level can be either a string or a number:
Category named value Numeric value Trace
0 Debug
1 Information
2 Warning
3 Error
4 Critical
5 The provider level and category level:
- Are in reverse order.
- The string constants aren't all identical.
If no
FilterSpecs
are specified then theEventSourceLogger
implementation attempts to convert the provider level to a category level and applies it to all categories.Provider Level Category Level Verbose
(5)Debug
(1)Informational
(4)Information
(2)Warning
(3)Warning
(3)Error
(2)Error
(4)Critical
(1)Critical
(5)If
FilterSpecs
are provided, any category that is included in the list uses the category level encoded there, all other categories are filtered out.The following examples assume:
- An app is running and calling
logger.LogDebug("12345")
. - The process ID (PID) has been set via
set PID=12345
, where12345
is the actual PID.
Consider the following command:
dotnet-trace collect -p %PID% --providers Microsoft-Extensions-Logging:4:5
The preceding command:
- Captures debug messages.
- Doesn't apply a
FilterSpecs
. - Specifies level 5 which maps category Debug.
Consider the following command:
dotnet-trace collect -p %PID% --providers Microsoft-Extensions-Logging:4:5:\"FilterSpecs=*:5\"
The preceding command:
- Doesn't capture debug messages because the category level 5 is
Critical
. - Provides a
FilterSpecs
.
The following command captures debug messages because category level 1 specifies
Debug
.dotnet-trace collect -p %PID% --providers Microsoft-Extensions-Logging:4:5:\"FilterSpecs=*:1\"
The following command captures debug messages because category specifies
Debug
.dotnet-trace collect -p %PID% --providers Microsoft-Extensions-Logging:4:5:\"FilterSpecs=*:Debug\"
FilterSpecs
entries for{Logger Category}
and{Category Level}
represent additional log filtering conditions. SeparateFilterSpecs
entries with the;
semicolon character.Example using a Windows command shell:
dotnet-trace collect -p %PID% --providers Microsoft-Extensions-Logging:4:2:FilterSpecs=\"Microsoft.AspNetCore.Hosting*:4\"
The preceding command activates:
- The Event Source logger to produce formatted strings (
4
) for errors (2
). Microsoft.AspNetCore.Hosting
logging at theInformational
logging level (4
).
Stop the
dotnet-trace
tooling by pressing the Enter key or Ctrl+C.The trace is saved with the name
trace.nettrace
in the folder where thedotnet-trace
command is executed.Open the trace with Perfview. Open the
trace.nettrace
file and explore the trace events.
If the app doesn't build the host with WebApplication.CreateBuilder, add the Event Source provider to the app's logging configuration.
For more information, see:
- Trace for performance analysis utility (
dotnet-trace
) (.NET Core documentation) - Trace for performance analysis utility (
dotnet-trace
) (dotnet/diagnostics GitHub repository documentation) - LoggingEventSource
- EventLevel
- Perfview: Useful for viewing Event Source traces.
Perfview
Use the PerfView utility to collect and view logs. There are other tools for viewing ETW logs, but PerfView provides the best experience for working with the ETW events emitted by ASP.NET Core.
To configure PerfView for collecting events logged by this provider, add the string *Microsoft-Extensions-Logging
to the Additional Providers list. Don't miss the *
at the start of the string.
Windows EventLog
The EventLog
provider sends log output to the Windows Event Log. Unlike the other providers, the EventLog
provider does not inherit the default non-provider settings. If EventLog
log settings aren't specified, they default to LogLevel.Warning.
To log events lower than LogLevel.Warning, explicitly set the log level. The following example sets the Event Log default log level to LogLevel.Information:
"Logging": {
"EventLog": {
"LogLevel": {
"Default": "Information"
}
}
}
AddEventLog overloads can pass in EventLogSettings. If null
or not specified, the following default settings are used:
LogName
: "Application"SourceName
: ".NET Runtime"MachineName
: The local machine name is used.
The following code changes the SourceName
from the default value of ".NET Runtime"
to MyLogs
:
var builder = WebApplication.CreateBuilder();
builder.Logging.AddEventLog(eventLogSettings =>
{
eventLogSettings.SourceName = "MyLogs";
});
Azure App Service
The Microsoft.Extensions.Logging.AzureAppServices
provider package writes logs to text files in an Azure App Service app's file system and to blob storage in an Azure Storage account.
The provider package isn't included in the shared framework. To use the provider, add the provider package to the project.
To configure provider settings, use AzureFileLoggerOptions and AzureBlobLoggerOptions, as shown in the following example:
using Microsoft.Extensions.Logging.AzureAppServices;
var builder = WebApplication.CreateBuilder();
builder.Logging.AddAzureWebAppDiagnostics();
builder.Services.Configure<AzureFileLoggerOptions>(options =>
{
options.FileName = "azure-diagnostics-";
options.FileSizeLimit = 50 * 1024;
options.RetainedFileCountLimit = 5;
});
builder.Services.Configure<AzureBlobLoggerOptions>(options =>
{
options.BlobName = "log.txt";
});
When deployed to Azure App Service, the app uses the settings in the App Service logs section of the App Service page of the Azure portal. When the following settings are updated, the changes take effect immediately without requiring a restart or redeployment of the app.
- Application Logging (Filesystem)
- Application Logging (Blob)
The default location for log files is in the D:\\home\\LogFiles\\Application
folder, and the default file name is diagnostics-yyyymmdd.txt
. The default file size limit is 10 MB, and the default maximum number of files retained is 2. The default blob name is {app-name}{timestamp}/yyyy/mm/dd/hh/{guid}-applicationLog.txt
.
This provider only logs when the project runs in the Azure environment.
Azure log streaming
Azure log streaming supports viewing log activity in real time from:
- The app server
- The web server
- Failed request tracing
To configure Azure log streaming:
- Navigate to the App Service logs page from the app's portal page.
- Set Application Logging (Filesystem) to On.
- Choose the log Level. This setting only applies to Azure log streaming.
Navigate to the Log Stream page to view logs. The logged messages are logged with the ILogger
interface.
Azure Application Insights
The Microsoft.Extensions.Logging.ApplicationInsights
provider package writes logs to Azure Application Insights. Application Insights is a service that monitors a web app and provides tools for querying and analyzing the telemetry data. If you use this provider, you can query and analyze your logs by using the Application Insights tools.
The logging provider is included as a dependency of Microsoft.ApplicationInsights.AspNetCore
, which is the package that provides all available telemetry for ASP.NET Core. If you use this package, you don't have to install the provider package.
The Microsoft.ApplicationInsights.Web
package is for ASP.NET 4.x, not ASP.NET Core.
For more information, see the following resources:
- Application Insights overview
- Application Insights for ASP.NET Core applications: Start here if you want to implement the full range of Application Insights telemetry along with logging.
- ApplicationInsightsLoggerProvider for .NET Core ILogger logs: Start here if you want to implement the logging provider without the rest of Application Insights telemetry.
- Application Insights logging adapters
- Install, configure, and initialize the Application Insights SDK interactive tutorial.
Third-party logging providers
Third-party logging frameworks that work with ASP.NET Core:
- elmah.io (GitHub repo)
- Gelf (GitHub repo)
- JSNLog (GitHub repo)
- KissLog.net (GitHub repo)
- Log4Net (GitHub repo)
- NLog (GitHub repo)
- PLogger (GitHub repo)
- Sentry (GitHub repo)
- Serilog (GitHub repo)
- Stackdriver (Github repo)
Some third-party frameworks can perform semantic logging, also known as structured logging.
Using a third-party framework is similar to using one of the built-in providers:
- Add a NuGet package to your project.
- Call an
ILoggerFactory
extension method provided by the logging framework.
For more information, see each provider's documentation. Third-party logging providers aren't supported by Microsoft.
No asynchronous logger methods
Logging should be so fast that it isn't worth the performance cost of asynchronous code. If a logging data store is slow, don't write to it directly. Consider writing the log messages to a fast store initially, then moving them to the slow store later. For example, when logging to SQL Server, don't do so directly in a Log
method, since the Log
methods are synchronous. Instead, synchronously add log messages to an in-memory queue and have a background worker pull the messages out of the queue to do the asynchronous work of pushing data to SQL Server. For more information, see Guidance on how to log to a message queue for slow data stores (dotnet/AspNetCore.Docs #11801).
Change log levels in a running app
The Logging API doesn't include a scenario to change log levels while an app is running. However, some configuration providers are capable of reloading configuration, which takes immediate effect on logging configuration. For example, the File Configuration Provider, reloads logging configuration by default. If configuration is changed in code while an app is running, the app can call IConfigurationRoot.Reload to update the app's logging configuration.
ILogger and ILoggerFactory
The ILogger<TCategoryName> and ILoggerFactory interfaces and implementations are included in the .NET Core SDK. They are also available in the following NuGet packages:
- The interfaces are in
Microsoft.Extensions.Logging.Abstractions
. - The default implementations are in
Microsoft.Extensions.Logging
.
Apply log filter rules in code
The preferred approach for setting log filter rules is by using Configuration.
The following example shows how to register filter rules in code:
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Logging.Debug;
var builder = WebApplication.CreateBuilder();
builder.Logging.AddFilter("System", LogLevel.Debug);
builder.Logging.AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Information);
builder.Logging.AddFilter<ConsoleLoggerProvider>("Microsoft", LogLevel.Trace);
logging.AddFilter("System", LogLevel.Debug)
specifies the System
category and log level Debug
. The filter is applied to all providers because a specific provider was not configured.
AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Information)
specifies:
- The
Debug
logging provider. - Log level
Information
and higher. - All categories starting with
"Microsoft"
.
Automatically log scope with SpanId
, TraceId
, ParentId
, Baggage
, and Tags
.
The logging libraries implicitly create a scope object with SpanId
, TraceId
, ParentId
,Baggage
, and Tags
. This behavior is configured via ActivityTrackingOptions.
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddSimpleConsole(options =>
{
options.IncludeScopes = true;
});
builder.Logging.Configure(options =>
{
options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId
| ActivityTrackingOptions.TraceId
| ActivityTrackingOptions.ParentId
| ActivityTrackingOptions.Baggage
| ActivityTrackingOptions.Tags;
});
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
If the traceparent
http request header is set, the ParentId
in the log scope shows the W3C parent-id
from in-bound traceparent
header and the SpanId
in the log scope shows the updated parent-id
for the next out-bound step/span. For more information, see Mutating the traceparent Field.
Create a custom logger
To create a custom logger, see Implement a custom logging provider in .NET.
Additional resources
- Improving logging performance with source generators
- Behind
[LogProperties]
and the new telemetry logging source generator - Microsoft.Extensions.Logging source on GitHub
- View or download sample code (how to download).
- High performance logging
- Logging bugs should be created in the
dotnet/runtime
GitHub repository. - ASP.NET Core Blazor logging
By Kirk Larkin, Juergen Gutsch, and Rick Anderson
This topic describes logging in .NET as it applies to ASP.NET Core apps. For detailed information on logging in .NET, see Logging in .NET. For more information on logging in Blazor apps, see ASP.NET Core Blazor logging.
View or download sample code (how to download).
Logging providers
Logging providers store logs, except for the Console
provider which displays logs. For example, the Azure Application Insights provider stores logs in Azure Application Insights. Multiple providers can be enabled.
The default ASP.NET Core web app templates:
- Use the Generic Host.
- Call CreateDefaultBuilder, which adds the following logging providers:
- Console
- Debug
- EventSource
- EventLog: Windows only
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
The preceding code shows the Program
class created with the ASP.NET Core web app templates. The next several sections provide samples based on the ASP.NET Core web app templates, which use the Generic Host. Non-host console apps are discussed later in this document.
To override the default set of logging providers added by Host.CreateDefaultBuilder
, call ClearProviders
and add the required logging providers. For example, the following code:
- Calls ClearProviders to remove all the ILoggerProvider instances from the builder.
- Adds the Console logging provider.
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
For additional providers, see:
Create logs
To create logs, use an ILogger<TCategoryName> object from dependency injection (DI).
The following example:
- Creates a logger,
ILogger<AboutModel>
, which uses a log category of the fully qualified name of the typeAboutModel
. The log category is a string that is associated with each log. - Calls LogInformation to log at the
Information
level. The Log level indicates the severity of the logged event.
public class AboutModel : PageModel
{
private readonly ILogger _logger;
public AboutModel(ILogger<AboutModel> logger)
{
_logger = logger;
}
public string Message { get; set; }
public void OnGet()
{
Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
_logger.LogInformation(Message);
}
}
Levels and categories are explained in more detail later in this document.
For information on Blazor, see ASP.NET Core Blazor logging.
Create logs in Main and Startup shows how to create logs in Main
and Startup
.
Configure logging
Logging configuration is commonly provided by the Logging
section of appsettings.{Environment}.json
files. The following appsettings.Development.json
file is generated by the ASP.NET Core web app templates:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
In the preceding JSON:
- The
"Default"
,"Microsoft"
, and"Microsoft.Hosting.Lifetime"
categories are specified. - The
"Microsoft"
category applies to all categories that start with"Microsoft"
. For example, this setting applies to the"Microsoft.AspNetCore.Routing.EndpointMiddleware"
category. - The
"Microsoft"
category logs at log levelWarning
and higher. - The
"Microsoft.Hosting.Lifetime"
category is more specific than the"Microsoft"
category, so the"Microsoft.Hosting.Lifetime"
category logs at log level "Information" and higher. - A specific log provider is not specified, so
LogLevel
applies to all the enabled logging providers except for the Windows EventLog.
The Logging
property can have LogLevel and log provider properties. The LogLevel
specifies the minimum level to log for selected categories. In the preceding JSON, Information
and Warning
log levels are specified. LogLevel
indicates the severity of the log and ranges from 0 to 6:
Trace
= 0, Debug
= 1, Information
= 2, Warning
= 3, Error
= 4, Critical
= 5, and None
= 6.
When a LogLevel
is specified, logging is enabled for messages at the specified level and higher. In the preceding JSON, the Default
category is logged for Information
and higher. For example, Information
, Warning
, Error
, and Critical
messages are logged. If no LogLevel
is specified, logging defaults to the Information
level. For more information, see Log levels.
A provider property can specify a LogLevel
property. LogLevel
under a provider specifies levels to log for that provider, and overrides the non-provider log settings. Consider the following appsettings.json
file:
{
"Logging": {
"LogLevel": { // All providers, LogLevel applies to all the enabled providers.
"Default": "Error", // Default logging, Error and higher.
"Microsoft": "Warning" // All Microsoft* categories, Warning and higher.
},
"Debug": { // Debug provider.
"LogLevel": {
"Default": "Information", // Overrides preceding LogLevel:Default setting.
"Microsoft.Hosting": "Trace" // Debug:Microsoft.Hosting category.
}
},
"EventSource": { // EventSource provider
"LogLevel": {
"Default": "Warning" // All categories of EventSource provider.
}
}
}
}
Settings in Logging.{providername}.LogLevel
override settings in Logging.LogLevel
. In the preceding JSON, the Debug
provider's default log level is set to Information
:
Logging:Debug:LogLevel:Default:Information
The preceding setting specifies the Information
log level for every Logging:Debug:
category except Microsoft.Hosting
. When a specific category is listed, the specific category overrides the default category. In the preceding JSON, the Logging:Debug:LogLevel
categories "Microsoft.Hosting"
and "Default"
override the settings in Logging:LogLevel
The minimum log level can be specified for any of:
- Specific providers: For example,
Logging:EventSource:LogLevel:Default:Information
- Specific categories: For example,
Logging:LogLevel:Microsoft:Warning
- All providers and all categories:
Logging:LogLevel:Default:Warning
Any logs below the minimum level are not:
- Passed to the provider.
- Logged or displayed.
To suppress all logs, specify LogLevel.None. LogLevel.None
has a value of 6, which is higher than LogLevel.Critical
(5).
If a provider supports log scopes, IncludeScopes
indicates whether they're enabled. For more information, see log scopes
The following appsettings.json
file contains all the providers enabled by default:
{
"Logging": {
"LogLevel": { // No provider, LogLevel applies to all the enabled providers.
"Default": "Error",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Warning"
},
"Debug": { // Debug provider.
"LogLevel": {
"Default": "Information" // Overrides preceding LogLevel:Default setting.
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft.AspNetCore.Mvc.Razor.Internal": "Warning",
"Microsoft.AspNetCore.Mvc.Razor.Razor": "Debug",
"Microsoft.AspNetCore.Mvc.Razor": "Error",
"Default": "Information"
}
},
"EventSource": {
"LogLevel": {
"Microsoft": "Information"
}
},
"EventLog": {
"LogLevel": {
"Microsoft": "Information"
}
},
"AzureAppServicesFile": {
"IncludeScopes": true,
"LogLevel": {
"Default": "Warning"
}
},
"AzureAppServicesBlob": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft": "Information"
}
},
"ApplicationInsights": {
"LogLevel": {
"Default": "Information"
}
}
}
}
In the preceding sample:
- The categories and levels are not suggested values. The sample is provided to show all the default providers.
- Settings in
Logging.{providername}.LogLevel
override settings inLogging.LogLevel
. For example, the level inDebug.LogLevel.Default
overrides the level inLogLevel.Default
. - Each default provider alias is used. Each provider defines an alias that can be used in configuration in place of the fully qualified type name. The built-in providers aliases are:
- Console
- Debug
- EventSource
- EventLog
- AzureAppServicesFile
- AzureAppServicesBlob
- ApplicationInsights
Set log level by command line, environment variables, and other configuration
Log level can be set by any of the configuration providers.
The :
separator doesn't work with environment variable hierarchical keys on all platforms. __
, the double underscore, is:
- Supported by all platforms. For example, the
:
separator is not supported by Bash, but__
is. - Automatically replaced by a
:
The following commands:
- Set the environment key
Logging:LogLevel:Microsoft
to a value ofInformation
on Windows. - Test the settings when using an app created with the ASP.NET Core web application templates. The
dotnet run
command must be run in the project directory after usingset
.
set Logging__LogLevel__Microsoft=Information
dotnet run
The preceding environment setting:
- Is only set in processes launched from the command window they were set in.
- Isn't read by browsers launched with Visual Studio.
The following setx command also sets the environment key and value on Windows. Unlike set
, setx
settings are persisted. The /M
switch sets the variable in the system environment. If /M
isn't used, a user environment variable is set.
setx Logging__LogLevel__Microsoft Information /M
Consider the following appsettings.json
file:
"Logging": {
"Console": {
"LogLevel": {
"Microsoft.Hosting.Lifetime": "Trace"
}
}
}
The following command sets the preceding configuration in the environment:
setx Logging__Console__LogLevel__Microsoft.Hosting.Lifetime Trace /M
On Azure App Service, select New application setting on the Settings > Configuration page. Azure App Service application settings are:
- Encrypted at rest and transmitted over an encrypted channel.
- Exposed as environment variables.
For more information, see Azure Apps: Override app configuration using the Azure Portal.
For more information on setting ASP.NET Core configuration values using environment variables, see environment variables. For information on using other configuration sources, including the command line, Azure Key Vault, Azure App Configuration, other file formats, and more, see Configuration in ASP.NET Core.
How filtering rules are applied
When an ILogger<TCategoryName> object is created, the ILoggerFactory object selects a single rule per provider to apply to that logger. All messages written by an ILogger
instance are filtered based on the selected rules. The most specific rule for each provider and category pair is selected from the available rules.
The following algorithm is used for each provider when an ILogger
is created for a given category:
- Select all rules that match the provider or its alias. If no match is found, select all rules with an empty provider.
- From the result of the preceding step, select rules with longest matching category prefix. If no match is found, select all rules that don't specify a category.
- If multiple rules are selected, take the last one.
- If no rules are selected, use
MinimumLevel
.
Logging output from dotnet run and Visual Studio
Logs created with the default logging providers are displayed:
- In Visual Studio
- In the Debug output window when debugging.
- In the ASP.NET Core Web Server window.
- In the console window when the app is run with
dotnet run
.
Logs that begin with "Microsoft" categories are from ASP.NET Core framework code. ASP.NET Core and application code use the same logging API and providers.
Log category
When an ILogger
object is created, a category is specified. That category is included with each log message created by that instance of ILogger
. The category string is arbitrary, but the convention is to use the class name. For example, in a controller the name might be "TodoApi.Controllers.TodoController"
. The ASP.NET Core web apps use ILogger<T>
to automatically get an ILogger
instance that uses the fully qualified type name of T
as the category:
public class PrivacyModel : PageModel
{
private readonly ILogger<PrivacyModel> _logger;
public PrivacyModel(ILogger<PrivacyModel> logger)
{
_logger = logger;
}
public void OnGet()
{
_logger.LogInformation("GET Pages.PrivacyModel called.");
}
}
To explicitly specify the category, call ILoggerFactory.CreateLogger
:
public class ContactModel : PageModel
{
private readonly ILogger _logger;
public ContactModel(ILoggerFactory logger)
{
_logger = logger.CreateLogger("TodoApi.Pages.ContactModel.MyCategory");
}
public void OnGet()
{
_logger.LogInformation("GET Pages.ContactModel called.");
}
Calling CreateLogger
with a fixed name can be useful when used in multiple methods so the events can be organized by category.
ILogger<T>
is equivalent to calling CreateLogger
with the fully qualified type name of T
.
Log level
The following table lists the LogLevel values, the convenience Log{LogLevel}
extension method, and the suggested usage:
LogLevel | Value | Method | Description |
---|---|---|---|
Trace | 0 | LogTrace | Contain the most detailed messages. These messages may contain sensitive app data. These messages are disabled by default and should not be enabled in production. |
Debug | 1 | LogDebug | For debugging and development. Use with caution in production due to the high volume. |
Information | 2 | LogInformation | Tracks the general flow of the app. May have long-term value. |
Warning | 3 | LogWarning | For abnormal or unexpected events. Typically includes errors or conditions that don't cause the app to fail. |
Error | 4 | LogError | For errors and exceptions that cannot be handled. These messages indicate a failure in the current operation or request, not an app-wide failure. |
Critical | 5 | LogCritical | For failures that require immediate attention. Examples: data loss scenarios, out of disk space. |
None | 6 | Specifies that a logging category should not write any messages. |
In the previous table, the LogLevel
is listed from lowest to highest severity.
The Log method's first parameter, LogLevel, indicates the severity of the log. Rather than calling Log(LogLevel, ...)
, most developers call the Log{LogLevel} extension methods. The Log{LogLevel}
extension methods call the Log method and specify the LogLevel. For example, the following two logging calls are functionally equivalent and produce the same log:
[HttpGet]
public IActionResult Test1(int id)
{
var routeInfo = ControllerContext.ToCtxString(id);
_logger.Log(LogLevel.Information, MyLogEvents.TestItem, routeInfo);
_logger.LogInformation(MyLogEvents.TestItem, routeInfo);
return ControllerContext.MyDisplayRouteInfo();
}
MyLogEvents.TestItem
is the event ID. MyLogEvents
is part of the sample app and is displayed in the Log event ID section.
MyDisplayRouteInfo and ToCtxString are provided by the Rick.Docs.Samples.RouteInfo NuGet package. The methods display Controller
and Razor Page
route information.
The following code creates Information
and Warning
logs:
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
_logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
_logger.LogWarning(MyLogEvents.GetItemNotFound, "Get({Id}) NOT FOUND", id);
return NotFound();
}
return ItemToDTO(todoItem);
}
In the preceding code, the first Log{LogLevel}
parameter,MyLogEvents.GetItem
, is the Log event ID. The second parameter is a message template with placeholders for argument values provided by the remaining method parameters. The method parameters are explained in the message template section later in this document.
Call the appropriate Log{LogLevel}
method to control how much log output is written to a particular storage medium. For example:
- In production:
- Logging at the
Trace
orInformation
levels produces a high-volume of detailed log messages. To control costs and not exceed data storage limits, logTrace
andInformation
level messages to a high-volume, low-cost data store. Consider limitingTrace
andInformation
to specific categories. - Logging at
Warning
throughCritical
levels should produce few log messages.- Costs and storage limits usually aren't a concern.
- Few logs allow more flexibility in data store choices.
- Logging at the
- In development:
- Set to
Warning
. - Add
Trace
orInformation
messages when troubleshooting. To limit output, setTrace
orInformation
only for the categories under investigation.
- Set to
ASP.NET Core writes logs for framework events. For example, consider the log output for:
- A Razor Pages app created with the ASP.NET Core templates.
- Logging set to
Logging:Console:LogLevel:Microsoft:Information
- Navigation to the Privacy page:
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/2 GET https://localhost:5001/Privacy
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint '/Privacy'
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[3]
Route matched with {page = "/Privacy"}. Executing page /Privacy
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[101]
Executing handler method DefaultRP.Pages.PrivacyModel.OnGet - ModelState is Valid
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[102]
Executed handler method OnGet, returned result .
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[103]
Executing an implicit handler method - ModelState is Valid
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[104]
Executed an implicit handler method, returned result Microsoft.AspNetCore.Mvc.RazorPages.PageResult.
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[4]
Executed page /Privacy in 74.5188ms
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint '/Privacy'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 149.3023ms 200 text/html; charset=utf-8
The following JSON sets Logging:Console:LogLevel:Microsoft:Information
:
{
"Logging": { // Default, all providers.
"LogLevel": {
"Microsoft": "Warning"
},
"Console": { // Console provider.
"LogLevel": {
"Microsoft": "Information"
}
}
}
}
Log event ID
Each log can specify an event ID. The sample app uses the MyLogEvents
class to define event IDs:
public class MyLogEvents
{
public const int GenerateItems = 1000;
public const int ListItems = 1001;
public const int GetItem = 1002;
public const int InsertItem = 1003;
public const int UpdateItem = 1004;
public const int DeleteItem = 1005;
public const int TestItem = 3000;
public const int GetItemNotFound = 4000;
public const int UpdateItemNotFound = 4001;
}
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
_logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
_logger.LogWarning(MyLogEvents.GetItemNotFound, "Get({Id}) NOT FOUND", id);
return NotFound();
}
return ItemToDTO(todoItem);
}
An event ID associates a set of events. For example, all logs related to displaying a list of items on a page might be 1001.
The logging provider may store the event ID in an ID field, in the logging message, or not at all. The Debug provider doesn't show event IDs. The console provider shows event IDs in brackets after the category:
info: TodoApi.Controllers.TodoItemsController[1002]
Getting item 1
warn: TodoApi.Controllers.TodoItemsController[4000]
Get(1) NOT FOUND
Some logging providers store the event ID in a field, which allows for filtering on the ID.
Log message template
Each log API uses a message template. The message template can contain placeholders for which arguments are provided. Use names for the placeholders, not numbers.
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
_logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
_logger.LogWarning(MyLogEvents.GetItemNotFound, "Get({Id}) NOT FOUND", id);
return NotFound();
}
return ItemToDTO(todoItem);
}
The order of the parameters, not their placeholder names, determines which parameters are used to provide placeholder values in log messages. In the following code, the parameter names are out of sequence in the placeholders of the message template:
var apples = 1;
var pears = 2;
var bananas = 3;
_logger.LogInformation("Parameters: {pears}, {bananas}, {apples}", apples, pears, bananas);
However, the parameters are assigned to the placeholders in the order: apples
, pears
, bananas
. The log message reflects the order of the parameters:
Parameters: 1, 2, 3
This approach allows logging providers to implement semantic or structured logging. The arguments themselves are passed to the logging system, not just the formatted message template. This enables logging providers to store the parameter values as fields. For example, consider the following logger method:
_logger.LogInformation("Getting item {Id} at {RequestTime}", id, DateTime.Now);
For example, when logging to Azure Table Storage:
- Each Azure Table entity can have
ID
andRequestTime
properties. - Tables with properties simplify queries on logged data. For example, a query can find all logs within a particular
RequestTime
range without having to parse the time out of the text message.
Log exceptions
The logger methods have overloads that take an exception parameter:
[HttpGet("{id}")]
public IActionResult TestExp(int id)
{
var routeInfo = ControllerContext.ToCtxString(id);
_logger.LogInformation(MyLogEvents.TestItem, routeInfo);
try
{
if (id == 3)
{
throw new Exception("Test exception");
}
}
catch (Exception ex)
{
_logger.LogWarning(MyLogEvents.GetItemNotFound, ex, "TestExp({Id})", id);
return NotFound();
}
return ControllerContext.MyDisplayRouteInfo();
}
MyDisplayRouteInfo and ToCtxString are provided by the Rick.Docs.Samples.RouteInfo NuGet package. The methods display Controller
and Razor Page
route information.
Exception logging is provider-specific.
Default log level
If the default log level is not set, the default log level value is Information
.
For example, consider the following web app:
- Created with the ASP.NET web app templates.
appsettings.json
andappsettings.Development.json
deleted or renamed.
With the preceding setup, navigating to the privacy or home page produces many Trace
, Debug
, and Information
messages with Microsoft
in the category name.
The following code sets the default log level when the default log level is not set in configuration:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Warning))
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Generally, log levels should be specified in configuration and not code.
Filter function
A filter function is invoked for all providers and categories that don't have rules assigned to them by configuration or code:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging =>
{
logging.AddFilter((provider, category, logLevel) =>
{
if (provider.Contains("ConsoleLoggerProvider")
&& category.Contains("Controller")
&& logLevel >= LogLevel.Information)
{
return true;
}
else if (provider.Contains("ConsoleLoggerProvider")
&& category.Contains("Microsoft")
&& logLevel >= LogLevel.Information)
{
return true;
}
else
{
return false;
}
});
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
The preceding code displays console logs when the category contains Controller
or Microsoft
and the log level is Information
or higher.
Generally, log levels should be specified in configuration and not code.
ASP.NET Core and EF Core categories
The following table contains some categories used by ASP.NET Core and Entity Framework Core, with notes about the logs:
Category | Notes |
---|---|
Microsoft.AspNetCore | General ASP.NET Core diagnostics. |
Microsoft.AspNetCore.DataProtection | Which keys were considered, found, and used. |
Microsoft.AspNetCore.HostFiltering | Hosts allowed. |
Microsoft.AspNetCore.Hosting | How long HTTP requests took to complete and what time they started. Which hosting startup assemblies were loaded. |
Microsoft.AspNetCore.Mvc | MVC and Razor diagnostics. Model binding, filter execution, view compilation, action selection. |
Microsoft.AspNetCore.Routing | Route matching information. |
Microsoft.AspNetCore.Server | Connection start, stop, and keep alive responses. HTTPS certificate information. |
Microsoft.AspNetCore.StaticFiles | Files served. |
Microsoft.EntityFrameworkCore | General Entity Framework Core diagnostics. Database activity and configuration, change detection, migrations. |
To view more categories in the console window, set appsettings.Development.json
to the following:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Trace",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
Log scopes
A scope can group a set of logical operations. This grouping can be used to attach the same data to each log that's created as part of a set. For example, every log created as part of processing a transaction can include the transaction ID.
A scope:
- Is an IDisposable type that's returned by the BeginScope method.
- Lasts until it's disposed.
The following providers support scopes:
Use a scope by wrapping logger calls in a using
block:
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
TodoItem todoItem;
var transactionId = Guid.NewGuid().ToString();
using (_logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("TransactionId", transactionId),
}))
{
_logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);
todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
_logger.LogWarning(MyLogEvents.GetItemNotFound,
"Get({Id}) NOT FOUND", id);
return NotFound();
}
}
return ItemToDTO(todoItem);
}
Built-in logging providers
ASP.NET Core includes the following logging providers as part of the shared framework:
The following logging providers are shipped by Microsoft, but not as part of the shared framework. They must be installed as additional nuget.
ASP.NET Core doesn't include a logging provider for writing logs to files. To write logs to files from an ASP.NET Core app, consider using a third-party logging provider.
For information on stdout
and debug logging with the ASP.NET Core Module, see Troubleshoot ASP.NET Core on Azure App Service and IIS and ASP.NET Core Module (ANCM) for IIS.
Console
The Console
provider logs output to the console. For more information on viewing Console
logs in development, see Logging output from dotnet run and Visual Studio.
Debug
The Debug
provider writes log output by using the System.Diagnostics.Debug class. Calls to System.Diagnostics.Debug.WriteLine
write to the Debug
provider.
On Linux, the Debug
provider log location is distribution-dependent and may be one of the following:
- /var/log/message
- /var/log/syslog
Event Source
The EventSource
provider writes to a cross-platform event source with the name Microsoft-Extensions-Logging
. On Windows, the provider uses ETW.
dotnet trace tooling
The dotnet-trace tool is a cross-platform CLI global tool that enables the collection of .NET Core traces of a running process. The tool collects Microsoft.Extensions.Logging.EventSource provider data using a LoggingEventSource.
See dotnet-trace for installation instructions.
Use the dotnet trace tooling to collect a trace from an app:
Run the app with the
dotnet run
command.Determine the process identifier (PID) of the .NET Core app:
dotnet trace ps
Find the PID for the process that has the same name as the app's assembly.
Execute the
dotnet trace
command.General command syntax:
dotnet trace collect -p {PID} --providers Microsoft-Extensions-Logging:{Keyword}:{Provider Level} :FilterSpecs=\" {Logger Category 1}:{Category Level 1}; {Logger Category 2}:{Category Level 2}; ... {Logger Category N}:{Category Level N}\"
When using a PowerShell command shell, enclose the
--providers
value in single quotes ('
):dotnet trace collect -p {PID} --providers 'Microsoft-Extensions-Logging:{Keyword}:{Provider Level} :FilterSpecs=\" {Logger Category 1}:{Category Level 1}; {Logger Category 2}:{Category Level 2}; ... {Logger Category N}:{Category Level N}\"'
On non-Windows platforms, add the
-f speedscope
option to change the format of the output trace file tospeedscope
.The following table defines the Keyword:
Keyword Description 1 Log meta events about the LoggingEventSource
. Doesn't log events fromILogger
.2 Turns on the Message
event whenILogger.Log()
is called. Provides information in a programmatic (not formatted) way.4 Turns on the FormatMessage
event whenILogger.Log()
is called. Provides the formatted string version of the information.8 Turns on the MessageJson
event whenILogger.Log()
is called. Provides a JSON representation of the arguments.The following table lists the provider levels:
Provider Level Description 0 LogAlways
1 Critical
2 Error
3 Warning
4 Informational
5 Verbose
The parsing for a category level can be either a string or a number:
Category named value Numeric value Trace
0 Debug
1 Information
2 Warning
3 Error
4 Critical
5 The provider level and category level:
- Are in reverse order.
- The string constants aren't all identical.
If no
FilterSpecs
are specified then theEventSourceLogger
implementation attempts to convert the provider level to a category level and applies it to all categories.Provider Level Category Level Verbose
(5)Debug
(1)Informational
(4)Information
(2)Warning
(3)Warning
(3)Error
(2)Error
(4)Critical
(1)Critical
(5)If
FilterSpecs
are provided, any category that is included in the list uses the category level encoded there, all other categories are filtered out.The following examples assume:
- An app is running and calling
logger.LogDebug("12345")
. - The process ID (PID) has been set via
set PID=12345
, where12345
is the actual PID.
Consider the following command:
dotnet trace collect -p %PID% --providers Microsoft-Extensions-Logging:4:5
The preceding command:
- Captures debug messages.
- Doesn't apply a
FilterSpecs
. - Specifies level 5 which maps category Debug.
Consider the following command:
dotnet trace collect -p %PID% --providers Microsoft-Extensions-Logging:4:5:\"FilterSpecs=*:5\"
The preceding command:
- Doesn't capture debug messages because the category level 5 is
Critical
. - Provides a
FilterSpecs
.
The following command captures debug messages because category level 1 specifies
Debug
.dotnet trace collect -p %PID% --providers Microsoft-Extensions-Logging:4:5:\"FilterSpecs=*:1\"
The following command captures debug messages because category specifies
Debug
.dotnet trace collect -p %PID% --providers Microsoft-Extensions-Logging:4:5:\"FilterSpecs=*:Debug\"
FilterSpecs
entries for{Logger Category}
and{Category Level}
represent additional log filtering conditions. SeparateFilterSpecs
entries with the;
semicolon character.Example using a Windows command shell:
dotnet trace collect -p %PID% --providers Microsoft-Extensions-Logging:4:2:FilterSpecs=\"Microsoft.AspNetCore.Hosting*:4\"
The preceding command activates:
- The Event Source logger to produce formatted strings (
4
) for errors (2
). Microsoft.AspNetCore.Hosting
logging at theInformational
logging level (4
).
Stop the dotnet trace tooling by pressing the Enter key or Ctrl+C.
The trace is saved with the name trace.nettrace in the folder where the
dotnet trace
command is executed.Open the trace with Perfview. Open the trace.nettrace file and explore the trace events.
If the app doesn't build the host with CreateDefaultBuilder
, add the Event Source provider to the app's logging configuration.
For more information, see:
- Trace for performance analysis utility (dotnet-trace) (.NET Core documentation)
- Trace for performance analysis utility (dotnet-trace) (dotnet/diagnostics GitHub repository documentation)
- LoggingEventSource Class (.NET API Browser)
- EventLevel
- LoggingEventSource reference source (3.0): To obtain reference source for a different version, change the branch to
release/{Version}
, where{Version}
is the version of ASP.NET Core desired. - Perfview: Useful for viewing Event Source traces.
Perfview
Use the PerfView utility to collect and view logs. There are other tools for viewing ETW logs, but PerfView provides the best experience for working with the ETW events emitted by ASP.NET Core.
To configure PerfView for collecting events logged by this provider, add the string *Microsoft-Extensions-Logging
to the Additional Providers list. Don't miss the *
at the start of the string.
Windows EventLog
The EventLog
provider sends log output to the Windows Event Log. Unlike the other providers, the EventLog
provider does not inherit the default non-provider settings. If EventLog
log settings aren't specified, they default to LogLevel.Warning.
To log events lower than LogLevel.Warning, explicitly set the log level. The following example sets the Event Log default log level to LogLevel.Information:
"Logging": {
"EventLog": {
"LogLevel": {
"Default": "Information"
}
}
}
AddEventLog overloads can pass in EventLogSettings. If null
or not specified, the following default settings are used:
LogName
: "Application"SourceName
: ".NET Runtime"MachineName
: The local machine name is used.
The following code changes the SourceName
from the default value of ".NET Runtime"
to MyLogs
:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging =>
{
logging.AddEventLog(eventLogSettings =>
{
eventLogSettings.SourceName = "MyLogs";
});
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Azure App Service
The Microsoft.Extensions.Logging.AzureAppServices provider package writes logs to text files in an Azure App Service app's file system and to blob storage in an Azure Storage account.
The provider package isn't included in the shared framework. To use the provider, add the provider package to the project.
To configure provider settings, use AzureFileLoggerOptions and AzureBlobLoggerOptions, as shown in the following example:
public class Scopes
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging => logging.AddAzureWebAppDiagnostics())
.ConfigureServices(serviceCollection => serviceCollection
.Configure<AzureFileLoggerOptions>(options =>
{
options.FileName = "azure-diagnostics-";
options.FileSizeLimit = 50 * 1024;
options.RetainedFileCountLimit = 5;
})
.Configure<AzureBlobLoggerOptions>(options =>
{
options.BlobName = "log.txt";
}))
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
When deployed to Azure App Service, the app uses the settings in the App Service logs section of the App Service page of the Azure portal. When the following settings are updated, the changes take effect immediately without requiring a restart or redeployment of the app.
- Application Logging (Filesystem)
- Application Logging (Blob)
The default location for log files is in the D:\home\LogFiles\Application folder, and the default file name is diagnostics-yyyymmdd.txt. The default file size limit is 10 MB, and the default maximum number of files retained is 2. The default blob name is {app-name}{timestamp}/yyyy/mm/dd/hh/{guid}-applicationLog.txt.
This provider only logs when the project runs in the Azure environment.
Azure log streaming
Azure log streaming supports viewing log activity in real time from:
- The app server
- The web server
- Failed request tracing
To configure Azure log streaming:
- Navigate to the App Service logs page from the app's portal page.
- Set Application Logging (Filesystem) to On.
- Choose the log Level. This setting only applies to Azure log streaming.
Navigate to the Log Stream page to view logs. The logged messages are logged with the ILogger
interface.
Azure Application Insights
The Microsoft.Extensions.Logging.ApplicationInsights provider package writes logs to Azure Application Insights. Application Insights is a service that monitors a web app and provides tools for querying and analyzing the telemetry data. If you use this provider, you can query and analyze your logs by using the Application Insights tools.
The logging provider is included as a dependency of Microsoft.ApplicationInsights.AspNetCore, which is the package that provides all available telemetry for ASP.NET Core. If you use this package, you don't have to install the provider package.
The Microsoft.ApplicationInsights.Web package is for ASP.NET 4.x, not ASP.NET Core.
For more information, see the following resources:
- Application Insights overview
- Application Insights for ASP.NET Core applications - Start here if you want to implement the full range of Application Insights telemetry along with logging.
- ApplicationInsightsLoggerProvider for .NET Core ILogger logs - Start here if you want to implement the logging provider without the rest of Application Insights telemetry.
- Application Insights logging adapters.
- Install, configure, and initialize the Application Insights SDK interactive tutorial.
Third-party logging providers
Third-party logging frameworks that work with ASP.NET Core:
- elmah.io (GitHub repo)
- Gelf (GitHub repo)
- JSNLog (GitHub repo)
- KissLog.net (GitHub repo)
- Log4Net (GitHub repo)
- NLog (GitHub repo)
- PLogger (GitHub repo)
- Sentry (GitHub repo)
- Serilog (GitHub repo)
- Stackdriver (Github repo)
Some third-party frameworks can perform semantic logging, also known as structured logging.
Using a third-party framework is similar to using one of the built-in providers:
- Add a NuGet package to your project.
- Call an
ILoggerFactory
extension method provided by the logging framework.
For more information, see each provider's documentation. Third-party logging providers aren't supported by Microsoft.
Non-host console app
For an example of how to use the Generic Host in a non-web console app, see the Program.cs
file of the Background Tasks sample app (Background tasks with hosted services in ASP.NET Core).
Logging code for apps without Generic Host differs in the way providers are added and loggers are created.
Logging providers
In a non-host console app, call the provider's Add{provider name}
extension method while creating a LoggerFactory
:
class Program
{
static void Main(string[] args)
{
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
.AddConsole()
.AddEventLog();
});
ILogger logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("Example log message");
}
}
Create logs
To create logs, use an ILogger<TCategoryName> object. Use the LoggerFactory
to create an ILogger
.
The following example creates a logger with LoggingConsoleApp.Program
as the category.
class Program
{
static void Main(string[] args)
{
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
.AddConsole()
.AddEventLog();
});
ILogger logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("Example log message");
}
}
In the following example, the logger is used to create logs with Information
as the level. The Log level indicates the severity of the logged event.
class Program
{
static void Main(string[] args)
{
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
.AddConsole()
.AddEventLog();
});
ILogger logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("Example log message");
}
}
Levels and categories are explained in more detail in this document.
Log during host construction
Logging during host construction isn't directly supported. However, a separate logger can be used. In the following example, a Serilog logger is used to log in CreateHostBuilder
. AddSerilog
uses the static configuration specified in Log.Logger
:
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
var builtConfig = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddCommandLine(args)
.Build();
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File(builtConfig["Logging:FilePath"])
.CreateLogger();
try
{
return Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
services.AddRazorPages();
})
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddConfiguration(builtConfig);
})
.ConfigureLogging(logging =>
{
logging.AddSerilog();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
catch (Exception ex)
{
Log.Fatal(ex, "Host builder error");
throw;
}
finally
{
Log.CloseAndFlush();
}
}
}
Configure a service that depends on ILogger
Constructor injection of a logger into Startup
works in earlier versions of ASP.NET Core because a separate DI container is created for the Web Host. For information about why only one container is created for the Generic Host, see the breaking change announcement.
To configure a service that depends on ILogger<T>
, use constructor injection or provide a factory method. The factory method approach is recommended only if there is no other option. For example, consider a service that needs an ILogger<T>
instance provided by DI:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddRazorPages();
services.AddSingleton<IMyService>((container) =>
{
var logger = container.GetRequiredService<ILogger<MyService>>();
return new MyService() { Logger = logger };
});
}
The preceding highlighted code is a Func<T,TResult> that runs the first time the DI container needs to construct an instance of MyService
. You can access any of the registered services in this way.
Create logs in Main
The following code logs in Main
by getting an ILogger
instance from DI after building the host:
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Host created.");
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
Create logs in Startup
The following code writes logs in Startup.Configure
:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
ILogger<Startup> logger)
{
if (env.IsDevelopment())
{
logger.LogInformation("In Development.");
app.UseDeveloperExceptionPage();
}
else
{
logger.LogInformation("Not Development.");
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
Writing logs before completion of the DI container setup in the Startup.ConfigureServices
method is not supported:
- Logger injection into the
Startup
constructor is not supported. - Logger injection into the
Startup.ConfigureServices
method signature is not supported
The reason for this restriction is that logging depends on DI and on configuration, which in turns depends on DI. The DI container isn't set up until ConfigureServices
finishes.
For information on configuring a service that depends on ILogger<T>
or why constructor injection of a logger into Startup
worked in earlier versions, see Configure a service that depends on ILogger
No asynchronous logger methods
Logging should be so fast that it isn't worth the performance cost of asynchronous code. If a logging data store is slow, don't write to it directly. Consider writing the log messages to a fast store initially, then moving them to the slow store later. For example, when logging to SQL Server, don't do so directly in a Log
method, since the Log
methods are synchronous. Instead, synchronously add log messages to an in-memory queue and have a background worker pull the messages out of the queue to do the asynchronous work of pushing data to SQL Server. For more information, see this GitHub issue.
Change log levels in a running app
The Logging API doesn't include a scenario to change log levels while an app is running. However, some configuration providers are capable of reloading configuration, which takes immediate effect on logging configuration. For example, the File Configuration Provider, reloads logging configuration by default. If configuration is changed in code while an app is running, the app can call IConfigurationRoot.Reload to update the app's logging configuration.
ILogger and ILoggerFactory
The ILogger<TCategoryName> and ILoggerFactory interfaces and implementations are included in the .NET Core SDK. They are also available in the following NuGet packages:
- The interfaces are in Microsoft.Extensions.Logging.Abstractions.
- The default implementations are in Microsoft.Extensions.Logging.
Apply log filter rules in code
The preferred approach for setting log filter rules is by using Configuration.
The following example shows how to register filter rules in code:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging =>
logging.AddFilter("System", LogLevel.Debug)
.AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Information)
.AddFilter<ConsoleLoggerProvider>("Microsoft", LogLevel.Trace))
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
logging.AddFilter("System", LogLevel.Debug)
specifies the System
category and log level Debug
. The filter is applied to all providers because a specific provider was not configured.
AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Information)
specifies:
- The
Debug
logging provider. - Log level
Information
and higher. - All categories starting with
"Microsoft"
.
Automatically log scope with SpanId, TraceId, and ParentId
The logging libraries implicitly create a scope object with SpanId
, TraceId
, and ParentId
. This behavior is configured via ActivityTrackingOptions.
var loggerFactory = LoggerFactory.Create(logging =>
{
logging.Configure(options =>
{
options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId
| ActivityTrackingOptions.TraceId
| ActivityTrackingOptions.ParentId;
}).AddSimpleConsole(options =>
{
options.IncludeScopes = true;
});
});
If the traceparent
http request header is set, the ParentId
in the log scope shows the W3C parent-id
from in-bound traceparent
header and the SpanId
in the log scope shows the updated parent-id
for the next out-bound step/span. For more information, see Mutating the traceparent Field.
Create a custom logger
To create a custom logger, see Implement a custom logging provider in .NET.
Additional resources
- High performance logging
- Logging bugs should be created in the github.com/dotnet/runtime/ repo.
- ASP.NET Core Blazor logging
ASP.NET Core
Feedback
https://aka.ms/ContentUserFeedback.
Coming soon: Throughout 2024 we will be phasing out GitHub Issues as the feedback mechanism for content and replacing it with a new feedback system. For more information see:Submit and view feedback for