Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 25 additions & 39 deletions docs/core/extensions/logging/custom-provider.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,53 @@
---
title: Implement a custom logging provider
description: Discover how to implement a custom logging provider with colorized logs, writing custom C# ILogger and ILoggerProvider implementations.
ms.date: 10/20/2025
ms.date: 02/04/2026
ms.topic: how-to
---
Comment on lines 1 to 6
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the .NET Documentation Guidelines (CodingGuidelineID: 1000000), Markdown files that have been generated or significantly modified with AI assistance should include an ai-usage frontmatter key. Since this PR involves substantial edits and improvements, consider adding ai-usage: ai-assisted to the frontmatter if AI was used to assist with these changes.

Copilot generated this review using guidance from repository custom instructions.

# Implement a custom logging provider in .NET

There are many [logging providers](providers.md) available for common logging needs. You might need to implement a custom <xref:Microsoft.Extensions.Logging.ILoggerProvider> when one of the available providers doesn't suit your application needs. In this article, you learn how to implement a custom logging provider that can be used to colorize logs in the console.
There are many [logging providers](providers.md) available for common logging needs. But you might need to implement a custom <xref:Microsoft.Extensions.Logging.ILoggerProvider> when one of the available providers doesn't suit your application needs. In this article, you learn how to implement a custom logging provider that can be used to colorize logs in the console.

> [!TIP]
> The custom logging provider example source code is available in the **Docs GitHub repo**. For more information, see [GitHub: .NET Docs - Console Custom Logging](https://github.com/dotnet/docs/tree/main/docs/core/extensions/snippets/configuration/console-custom-logging).
> The custom logging provider example source code is available in the [docs GitHub repo](https://github.com/dotnet/docs/tree/main/docs/core/extensions/snippets/configuration/console-custom-logging).

### Sample custom logger configuration
## Sample custom logger configuration

The sample creates different color console entries per log level and event ID using the following configuration type:
The sample logger creates different color console entries per log level and event ID using the following configuration type:

:::code language="csharp" source="../snippets/configuration/console-custom-logging/ColorConsoleLoggerConfiguration.cs":::

The preceding code sets the default level to `Information`, the color to `Green`, and the `EventId` is implicitly `0`.
The preceding code sets the default color for the `Information` level to `Green`. The `EventId` is implicitly 0.

### Create the custom logger
## Create the custom logger

The `ILogger` implementation category name is typically the logging source. For example, the type where the logger is created:
The following code snippet shows the `ILogger` implementation:

:::code language="csharp" source="../snippets/configuration/console-custom-logging/ColorConsoleLogger.cs":::

The preceding code:
Each logger instance is instantiated by passing a category name, which is typically the type where the logger is created. The `IsEnabled` method checks `_getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel)` to see if the requested log level is enabled (that is, in the configuration's dictionary of log levels).

- Creates a logger instance per category name.
- Checks `_getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel)` in `IsEnabled`, so each `logLevel` has a unique logger. In this implementation, each log level requires an explicit configuration entry to log.
It's a good practice to call <xref:Microsoft.Extensions.Logging.ILogger.IsEnabled*?displayProperty=nameWithType> within <xref:Microsoft.Extensions.Logging.ILogger.Log*?displayProperty=nameWithType> implementations since `Log` can be called by any consumer, and there are no guarantees that it was previously checked. The `IsEnabled` method should be very fast in most implementations.

It's a good practice to call <xref:Microsoft.Extensions.Logging.ILogger.IsEnabled%2A?displayProperty=nameWithType> within <xref:Microsoft.Extensions.Logging.ILogger.Log%2A?displayProperty=nameWithType> implementations since `Log` can be called by any consumer, and there are no guarantees that it was previously checked. The `IsEnabled` method should be very fast in most implementations.
:::code language="csharp" source="../snippets/configuration/console-custom-logging/ColorConsoleLogger.cs" range="20-23":::
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code range "19-22" only shows the opening brace of the Log method and the early return check. Based on the context in line 31, which explains that "It's a good practice to call IsEnabled within Log implementations," the range should likely be "20-23" to show the complete IsEnabled check including the closing brace, or the range should be adjusted to show the meaningful part of the implementation.

Suggested change
:::code language="csharp" source="../snippets/configuration/console-custom-logging/ColorConsoleLogger.cs" range="20-23":::
:::code language="csharp" source="../snippets/configuration/console-custom-logging/ColorConsoleLogger.cs":::

Copilot uses AI. Check for mistakes.

:::code language="csharp" source="../snippets/configuration/console-custom-logging/ColorConsoleLogger.cs" range="15-16":::

The logger is instantiated with the `name` and a `Func<ColorConsoleLoggerConfiguration>`, which returns the current config&mdash;this handles updates to the config values as monitored through the <xref:Microsoft.Extensions.Options.IOptionsMonitor%601.OnChange%2A?displayProperty=nameWithType> callback.
The logger is instantiated with the `name` and a `Func<ColorConsoleLoggerConfiguration>` that returns the current configuration.

> [!IMPORTANT]
> The <xref:Microsoft.Extensions.Logging.ILogger.Log%2A?displayProperty=nameWithType> implementation checks if the `config.EventId` value is set. When `config.EventId` is not set or when it matches the exact `logEntry.EventId`, the logger logs in color.
> The <xref:Microsoft.Extensions.Logging.ILogger.Log*?displayProperty=nameWithType> implementation checks if the `config.EventId` value is set. When `config.EventId` is not set or when it matches the exact `logEntry.EventId`, the logger logs in color.

## Custom logger provider

The `ILoggerProvider` object is responsible for creating logger instances. It's not necessary to create a logger instance per category, but it makes sense for some loggers, like NLog or log4net. This strategy allows you to choose different logging output targets per category, as in the following example:

:::code language="csharp" source="../snippets/configuration/console-custom-logging/ColorConsoleLoggerProvider.cs":::

In the preceding code, <xref:Microsoft.Build.Logging.LoggerDescription.CreateLogger%2A> creates a single instance of the `ColorConsoleLogger` per category name and stores it in the [`ConcurrentDictionary<TKey,TValue>`](/dotnet/api/system.collections.concurrent.concurrentdictionary-2). Additionally, the <xref:Microsoft.Extensions.Options.IOptionsMonitor%601> interface is required to update changes to the underlying `ColorConsoleLoggerConfiguration` object.

To control the configuration of the `ColorConsoleLogger`, you define an alias on its provider:
In the preceding code, <xref:Microsoft.Build.Logging.LoggerDescription.CreateLogger*> creates a single instance of the `ColorConsoleLogger` per category name and stores it in the [`ConcurrentDictionary<TKey,TValue>`](/dotnet/api/system.collections.concurrent.concurrentdictionary-2).

:::code language="csharp" source="../snippets/configuration/console-custom-logging/ColorConsoleLoggerProvider.cs" range="6-8" highlight="6-7":::
The `ColorConsoleLoggerProvider` class is decorated with two attributes:

The `ColorConsoleLoggerProvider` class defines two class-scoped attributes:
:::code language="csharp" source="../snippets/configuration/console-custom-logging/ColorConsoleLoggerProvider.cs" range="6-8":::

- <xref:System.Runtime.Versioning.UnsupportedOSPlatformAttribute>: The `ColorConsoleLogger` type is _not supported_ in the `"browser"`.
- <xref:Microsoft.Extensions.Logging.ProviderAliasAttribute>: Configuration sections can define options using the `"ColorConsole"` key.
Expand All @@ -61,34 +56,25 @@ The configuration can be specified with any valid [configuration provider](../co

:::code language="json" source="../snippets/configuration/console-custom-logging/appsettings.json":::

This configures the log levels to the following values:

- <xref:Microsoft.Extensions.Logging.LogLevel.Information?displayProperty=nameWithType>: <xref:System.ConsoleColor.DarkGreen?displayProperty=nameWithType>
- <xref:Microsoft.Extensions.Logging.LogLevel.Warning?displayProperty=nameWithType>: <xref:System.ConsoleColor.Cyan?displayProperty=nameWithType>
- <xref:Microsoft.Extensions.Logging.LogLevel.Error?displayProperty=nameWithType>: <xref:System.ConsoleColor.Red?displayProperty=nameWithType>

The <xref:Microsoft.Extensions.Logging.LogLevel.Information> log level is set to <xref:System.ConsoleColor.DarkGreen>, which overrides the default value set in the `ColorConsoleLoggerConfiguration` object.
The _appsettings.json_ file specifies that the color for the <xref:Microsoft.Extensions.Logging.LogLevel.Information> log level is <xref:System.ConsoleColor.DarkGreen>, which overrides the default value set in the `ColorConsoleLoggerConfiguration` object.

## Usage and registration of the custom logger

By convention, registering services for dependency injection happens as part of the startup routine of an application. The registration occurs in the `Program` class, or could be delegated to a `Startup` class. In this example, you'll register directly from the _Program.cs_.

To add the custom logging provider and corresponding logger, add an <xref:Microsoft.Extensions.Logging.ILoggerProvider> with <xref:Microsoft.Extensions.Logging.ILoggingBuilder> from the <xref:Microsoft.Extensions.Hosting.HostingHostBuilderExtensions.ConfigureLogging(Microsoft.Extensions.Hosting.IHostBuilder,System.Action{Microsoft.Extensions.Logging.ILoggingBuilder})?displayProperty=nameWithType>:
By convention, services are registered for dependency injection as part of the startup routine of an application. In this example, the logging service is registered directly from the _Program.cs_ file.

:::code language="csharp" source="../snippets/configuration/console-custom-logging/Program.cs" highlight="6-14":::
To add the custom logging provider and corresponding logger, add an <xref:Microsoft.Extensions.Logging.ILoggerProvider> by calling a custom extension method, `AddColorConsoleLogger`, on the <xref:Microsoft.Extensions.Logging.ILoggingBuilder> from the <xref:Microsoft.Extensions.Hosting.IHostApplicationBuilder.Logging?displayProperty=nameWithType> property:

The `ILoggingBuilder` creates one or more `ILogger` instances. The `ILogger` instances are used by the framework to log the information.

The configuration from the _appsettings.json_ file overrides the following values:

- <xref:Microsoft.Extensions.Logging.LogLevel.Warning?displayProperty=nameWithType>: <xref:System.ConsoleColor.DarkCyan?displayProperty=nameWithType>
- <xref:Microsoft.Extensions.Logging.LogLevel.Error?displayProperty=nameWithType>: <xref:System.ConsoleColor.DarkRed?displayProperty=nameWithType>
:::code language="csharp" source="../snippets/configuration/console-custom-logging/Program.cs" highlight="8-14":::

By convention, extension methods on `ILoggingBuilder` are used to register the custom provider:

:::code language="csharp" source="../snippets/configuration/console-custom-logging/Extensions/ColorConsoleLoggerExtensions.cs":::

Running this simple application will render color output to the console window similar to the following image:
The `ILoggingBuilder` creates one or more `ILogger` instances. The `ILogger` instances are used by the framework to log the information.

The instantiation code overrides the color values from the _appsettings.json_ file for <xref:Microsoft.Extensions.Logging.LogLevel.Warning?displayProperty=nameWithType> and <xref:Microsoft.Extensions.Logging.LogLevel.Error?displayProperty=nameWithType>.
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The highlight range "8-14" appears to be incorrect. Looking at the Program.cs file, line 8 is the call to AddColorConsoleLogger, which starts the configuration block, but the closing bracket is on line 16, not line 14. The highlight should be "8-16" to include the complete AddColorConsoleLogger call.

Copilot uses AI. Check for mistakes.

When you run this simple app, it renders color output to the console window similar to the following image:

:::image type="content" source="media/color-console-logger.png" alt-text="Color console logger sample output":::

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ public sealed class ColorConsoleLogger(
string name,
Func<ColorConsoleLoggerConfiguration> getCurrentConfig) : ILogger
{
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => default!;
public IDisposable? BeginScope<TState>(TState state)
where TState : notnull => default!;

public bool IsEnabled(LogLevel logLevel) =>
getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
builder.Logging.ClearProviders();
builder.Logging.AddColorConsoleLogger(configuration =>
{
// Replace warning value from appsettings.json of "Cyan"
configuration.LogLevelToColorMap[LogLevel.Warning] = ConsoleColor.DarkCyan;
// Replace warning value from appsettings.json of "Red"
configuration.LogLevelToColorMap[LogLevel.Error] = ConsoleColor.DarkRed;
// Replace value of "Cyan" from appsettings.json.
configuration.LogLevelToColorMap[LogLevel.Warning]
= ConsoleColor.DarkCyan;
// Replace value of "Red" from appsettings.json.
configuration.LogLevelToColorMap[LogLevel.Error]
= ConsoleColor.DarkRed;
});

using IHost host = builder.Build();

var logger = host.Services.GetRequiredService<ILogger<Program>>();
ILogger<Program> logger = host.Services.GetRequiredService<ILogger<Program>>();

logger.LogDebug(1, "Does this line get hit?"); // Not logged
logger.LogInformation(3, "Nothing to see here."); // Logs in ConsoleColor.DarkGreen
Expand Down