Logging

Minimal template
This example is weak since it is not scaleable, performant, or configurable.
This sample console app relies on the following NuGet packages:
using Microsoft.Extensions.Logging;
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");- Create an
ILoggerFactoryhere we set it so logs are written to the console - we call the place we write to a sink
- Create the logger, and set the category to
program; a category is just a grouping
- Log at information level, with a template where
Descriptionis set tofun
Logging a method call example
[LoggerMessage(LogLevel.Information, "MethodName was called")]
public static partial void MethodName(ILogger logger);Advanced Logging
Typically most serious applications don’t use the Microsoft logging, and instead continue to use the ILogger API and use a third-party implementation like OTel for sending metrics off to Grafana and Serilog for structured logging.
The Generic and WebApp host both supply a DI ILogger , so no need to register just grab it like so.
partial class ExampleHandler(ILogger<ExampleHandler> logger)
{
public void HandleRequest()
{
LogHandleRequest(logger);
}
[LoggerMessage(LogLevel.Information, "ExampleHandler.HandleRequest was called")]
public static partial void LogHandleRequest(ILogger logger)
{
logger.LogInformation("Hello World! Logging is {Description}.", "fun");
}
}- The primary constructor takes ILogger<TCategoryName>, if no logger exists with that category yet, a new one is created
Configuration
For hosted applications, configs are normally set in appsettings.env.json , within “Logging”
So to be clear, you are setting the minimum logging level based on the category
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
"MyCategoryName": "Trace"
}
}
}- Default is minimum logging level that is used, when not specified
- Microsoft is the specifies the minimum logging level that Microsoft can produce
- Microsoft.Hosting.Lifetime is the exception where the minimum is Information
You can set logging levels based on provider.
Note a provider is where you output your logs to, so debug is your debug window.
{
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting": "Trace"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
}
}Log Levels
0 is the lowest, 6 is the highest priority
Trace = 0, Debug = 1, Information = 2, Warning = 3, Error = 4, Critical = 5, and None = 6.
| LogLevel | Value | Method | Description |
|---|---|---|---|
| Trace | 0 | LogTrace | Contain the most detailed messages. These messages might 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. Might 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 can't 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 no messages should be written. |
Event Ids
You can use event Ids to signal certain events, such as a read, or a certain failure.
using Microsoft.Extensions.Logging;
internal static class AppLogEvents
{
internal static readonly EventId Read = new(1001, "Read");
internal static readonly EventId ReadNotFound = new(4000, "ReadNotFound");
}
public sealed class AccountRepository(ILogger<AccountRepository> logger)
{
public string? GetById(string accountId)
{
logger.LogInformation(AppLogEvents.Read, "Reading account {AccountId}", accountId);
logger.LogWarning(AppLogEvents.ReadNotFound, "Account {AccountId} was not found", accountId);
}
}
using Microsoft.Extensions.Logging;
internal static partial class RepoLog
{
[LoggerMessage(
EventId = 1001,
Level = LogLevel.Information,
Message = "Reading account {AccountId}")]
internal static partial void ReadingAccount(ILogger logger, string accountId);
[LoggerMessage(
EventId = 4000,
Level = LogLevel.Warning,
Message = "Account {AccountId} was not found")]
internal static partial void AccountNotFound(ILogger logger, string accountId);
}
public sealed class AccountRepository(ILogger<AccountRepository> logger)
{
public string? GetById(string accountId)
{
RepoLog.ReadingAccount(logger, accountId);
RepoLog.AccountNotFound(logger, accountId);
}
}