Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
154 changes: 154 additions & 0 deletions docs/core/extensions/console-log-formatter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
---
title: Console log formatting
description: Learn how to use available console log formatting, or implement custom log formatting for your .NET applications.
author: IEvangelist
ms.author: dapine
ms.date: 10/22/2020
---

# Console log formatting

In .NET 5, support for custom formatting was added to console logs in the `Microsoft.Extensions.Logging.Console` namespace. There are three predefined formatting options available: [`Simple`](#simple), [`Systemd`](#systemd), and [`Json`](#json).

> [!IMPORTANT]
> Previously, the <xref:Microsoft.Extensions.Logging.Console.ConsoleLoggerFormat> enum allowed for selecting the desired log format, either human readable which was the `Default`, or single line which is also known as `Systemd`. However, these were **not** customizable, and are now deprecated.

In this article, you will learn about console log formatters. The sample source code demonstrates how to:

- Register a new formatter
- Select a registered formatter to use
- Either through code, or [configuration](configuration.md)
- Implement a custom formatter
- Update configuration via <xref:Microsoft.Extensions.Options.IOptionsMonitor%601>
- Enable custom color formatting

## Register formatter

The [`Console` logging provider](logging-providers.md#console) has several predefined formatters, and exposes the ability to author your own custom formatter. To register any of the available formatters, use the corresponding `Add{Type}Console` extension method:

| Available types | Method to register type |
|--|--|
| <xref:Microsoft.Extensions.Logging.Console.ConsoleFormatterNames.Json?displayProperty=nameWithType> | <xref:Microsoft.Extensions.Logging.ConsoleLoggerExtensions.AddJsonConsole%2A?displayProperty=nameWithType> |
| <xref:Microsoft.Extensions.Logging.Console.ConsoleFormatterNames.Simple?displayProperty=nameWithType> | <xref:Microsoft.Extensions.Logging.ConsoleLoggerExtensions.AddSimpleConsole%2A?displayProperty=nameWithType> |
| <xref:Microsoft.Extensions.Logging.Console.ConsoleFormatterNames.Systemd?displayProperty=nameWithType> | <xref:Microsoft.Extensions.Logging.ConsoleLoggerExtensions.AddSystemdConsole%2A?displayProperty=nameWithType> |

### Simple

To use the `Simple` console formatter, register it with `AddSimpleConsole`:

:::code language="csharp" source="snippets/logging/console-formatter-simple/Program.cs" highlight="11-16":::

In the preceding sample source code, the <xref:Microsoft.Extensions.Logging.Console.ConsoleFormatterNames.Simple?displayProperty=nameWithType> formatter was registered. It provides logs with the ability to not only wrap information such as time and log level in each log message, but also allows for ANSI color embedding and indentation of messages.

### Systemd

The <xref:Microsoft.Extensions.Logging.Console.ConsoleFormatterNames.Systemd?displayProperty=nameWithType> console logger:

- Uses the "Syslog" log level format and severities
- Does **not** format messages with colors
- Always logs messages in a single line

This is commonly useful for containers, which often make use of `Systemd` console logging. With .NET 5, the `Simple` console logger also enables a compact version that logs in a single line, and also allows for disabling colors as shown in an earlier sample.

:::code language="csharp" source="snippets/logging/console-formatter-systemd/Program.cs" highlight="11-15":::

### Json

To write logs in a JSON format, the `Json` console formatter is used. The sample source code shows how an ASP.NET Core app might register it. Using the `webapp` template, create a new ASP.NET Core app with the [dotnet new](../tools/dotnet-new.md) command:

```dotnetcli
dotnet new webapp -o Console.ExampleFormatters.Json
```

When running the app, using the template code, you get the default log format below:

```
info: Microsoft.Hosting.Lifetime[0]
Now listening on: https://localhost:5001
```

By default, the `Simple` console log formatter is selected with default configuration. You change this by calling `AddJsonConsole` in the *Program.cs*:

:::code language="csharp" source="snippets/logging/console-formatter-json/Program.cs" highlight="17-26":::

Run the app again, with the above change, the log message is now formatted as JSON:

:::code language="json" source="snippets/logging/console-formatter-json/example-output.json":::

> [!TIP]
> The `Json` console formatter, by default, logs each message in a single line. In order to make it more readable while configuring the formatter, set <xref:System.Text.Json.JsonWriterOptions.Indented?displayProperty=nameWithType> to `true`.

## Set formatter with configuration

The previous samples have shown how to register a formatter programmatically. Alternatively, this can be done with [configuration](configuration.md). Consider the previous web application sample source code, if you update the *appsettings.json* file rather than calling `ConfigureLogging` in the *Program.cs* file, you could get the same outcome. The updated `appsettings.json` file would configure the formatter as follows:

:::code language="json" source="snippets/logging/console-formatter-json/appsettings.json" highlight="14-23":::

The two key values that need to be set are `"FormatterName"` and `"FormatterOptions"`. If a formatter with the value set for `"FormatterName"` is already registered, that formatter is selected, and its properties can be configured as long as they are provided as a key inside the `"FormatterOptions"` node. The predefined formatter names are reserved under <xref:Microsoft.Extensions.Logging.Console.ConsoleFormatterNames>:

- <xref:Microsoft.Extensions.Logging.Console.ConsoleFormatterNames.Json?displayProperty=nameWithType>
- <xref:Microsoft.Extensions.Logging.Console.ConsoleFormatterNames.Simple?displayProperty=nameWithType>
- <xref:Microsoft.Extensions.Logging.Console.ConsoleFormatterNames.Systemd?displayProperty=nameWithType>

## Implement a custom formatter

To implement a custom formatter, you need to:

- Create a subclass of <xref:Microsoft.Extensions.Logging.Console.ConsoleFormatter>, this represents your custom formatter
- Register your custom formatter with
- <xref:Microsoft.Extensions.Logging.ConsoleLoggerExtensions.AddConsole%2A?displayProperty=nameWithType>
- <xref:Microsoft.Extensions.Logging.ConsoleLoggerExtensions.AddConsoleFormatter%60%602(Microsoft.Extensions.Logging.ILoggingBuilder,System.Action{%60%601})?displayProperty=nameWithType>

Create an extension method to handle this for you:

:::code language="csharp" source="snippets/logging/console-formatter-custom/ConsoleLoggerExtensions.cs" highlight="11-12":::

The `CustomOptions` are defined as follows:

:::code language="csharp" source="snippets/logging/console-formatter-custom/CustomOptions.cs":::

In the preceding code, the options are a subclass of <xref:Microsoft.Extensions.Logging.Console.ConsoleFormatterOptions>.

The `AddConsoleFormatter` API:

- Registers a subclass of `ConsoleFormatter`
- Handles configuration:
- Uses a change token to synchronize updates, based on the [options pattern](options.md), and the [IOptionsMonitor](xref:Microsoft.Extensions.Options.IOptionsMonitor%601) interface

:::code language="csharp" source="snippets/logging/console-formatter-custom/Program.cs" highlight="11-12":::

Define a `CustomerFormatter` subclass of `ConsoleFormatter`:

:::code language="csharp" source="snippets/logging/console-formatter-custom/CustomFormatter.cs" highlight="24-45":::

The preceding `CustomFormatter.Write<TState>` API dictates what text gets wrapped around each log message. A standard `ConsoleFormatter` should be able to wrap around scopes, time stamps, and severity level of logs at a minimum. Additionally, you can encode ANSI colors in the log messages, and provide desired indentations as well. The implementation of the `CustomFormatter.Write<TState>` lacks these capabilities.

For inspiration on further customizing formatting, see the existing implementations in the `Microsoft.Extensions.Logging.Console` namespace:

- [SimpleConsoleFormatter](https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.cs).
- [SystemdConsoleFormatter](https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Logging.Console/src/SystemdConsoleFormatter.cs)
- [JsonConsoleFormatter](https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.cs)

## Implement custom color formatting

In order to properly enable color capabilities in your custom logging formatter, you can extend the <xref:Microsoft.Extensions.Logging.Console.SimpleConsoleFormatterOptions> as it has a <xref:Microsoft.Extensions.Logging.Console.SimpleConsoleFormatterOptions.ColorBehavior?displayProperty=nameWithType> property that can be useful for enabling colors in logs.

Create a `CustomColorOptions` that derives from `SimpleConsoleFormatterOptions`:

:::code language="csharp" source="snippets/logging/console-formatter-custom/CustomColorOptions.cs" highlight="5":::

Next, write some extension methods in a `TextWriterExtensions` class that allow for conveniently embedding ANSI coded colors within formatted log messages:

:::code language="csharp" source="snippets/logging/console-formatter-custom/TextWriterExtensions.cs":::

A custom color formatter that handles applying custom colors could be defined as follows:

:::code language="csharp" source="snippets/logging/console-formatter-custom/CustomColorFormatter.cs" highlight="15-18,52-65":::

When you run the application, the logs will show the `CustomPrefix` message in the color green when `FormatterOptions.ColorBehavior` is `Enabled`.

## See also

- [Logging in .NET](logging.md)
- [Implement a custom logging provider in .NET](custom-logging-provider.md)
- [High-performance logging in .NET](high-performance-logging.md)
1 change: 1 addition & 0 deletions docs/core/extensions/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -525,5 +525,6 @@ class Program

- [Logging providers in .NET](logging-providers.md)
- [Implement a custom logging provider in .NET](custom-logging-provider.md)
- [Console log formatting](console-log-formatter.md)
- [High-performance logging in .NET](high-performance-logging.md)
- Logging bugs should be created in the [github.com/dotnet/extensions](https://github.com/dotnet/extensions/issues) repo
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.Extensions.Logging;
using System;

namespace Console.ExampleFormatters.Custom
{
public static class ConsoleLoggerExtensions
{
public static ILoggingBuilder AddCustomFormatter(
this ILoggingBuilder builder,
Action<CustomOptions> configure) =>
builder.AddConsole(options => options.FormatterName = "customName")
.AddConsoleFormatter<CustomFormatter, CustomOptions>(configure);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Options;
using System;
using System.IO;

namespace Console.ExampleFormatters.Custom
{
public sealed class CustomColorFormatter : ConsoleFormatter, IDisposable
{
private readonly IDisposable _optionsReloadToken;
private CustomColorOptions _formatterOptions;

private bool ConsoleColorFormattingEnabled =>
_formatterOptions.ColorBehavior == LoggerColorBehavior.Enabled ||
_formatterOptions.ColorBehavior == LoggerColorBehavior.Default &&
System.Console.IsOutputRedirected == false;

public CustomColorFormatter(IOptionsMonitor<CustomColorOptions> options)
// Case insensitive
: base("customName") =>
(_optionsReloadToken, _formatterOptions) =
(options.OnChange(ReloadLoggerOptions), options.CurrentValue);

private void ReloadLoggerOptions(CustomColorOptions options) =>
_formatterOptions = options;

public override void Write<TState>(
in LogEntry<TState> logEntry,
IExternalScopeProvider scopeProvider,
TextWriter textWriter)
{
if (logEntry.Exception is null)
{
return;
}

string message =
logEntry.Formatter(
logEntry.State, logEntry.Exception);

if (message == null)
{
return;
}

CustomLogicGoesHere(textWriter);
textWriter.WriteLine(message);
}

private void CustomLogicGoesHere(TextWriter textWriter)
{
if (ConsoleColorFormattingEnabled)
{
textWriter.WriteWithColor(
_formatterOptions.CustomPrefix,
ConsoleColor.Black,
ConsoleColor.Green);
}
else
{
textWriter.Write(_formatterOptions.CustomPrefix);
}
}

public void Dispose() => _optionsReloadToken?.Dispose();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Microsoft.Extensions.Logging.Console;

namespace Console.ExampleFormatters.Custom
{
public class CustomColorOptions : SimpleConsoleFormatterOptions
{
public string CustomPrefix { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Options;
using System;
using System.IO;

namespace Console.ExampleFormatters.Custom
{
public sealed class CustomFormatter : ConsoleFormatter, IDisposable
{
private readonly IDisposable _optionsReloadToken;
private CustomOptions _formatterOptions;

public CustomFormatter(IOptionsMonitor<CustomOptions> options)
// Case insensitive
: base("customName") =>
(_optionsReloadToken, _formatterOptions) =
(options.OnChange(ReloadLoggerOptions), options.CurrentValue);

private void ReloadLoggerOptions(CustomOptions options) =>
_formatterOptions = options;

public override void Write<TState>(
in LogEntry<TState> logEntry,
IExternalScopeProvider scopeProvider,
TextWriter textWriter)
{
if (logEntry.Exception is null)
{
return;
}

string message =
logEntry.Formatter(
logEntry.State, logEntry.Exception);

if (message == null)
{
return;
}

CustomLogicGoesHere(textWriter);
textWriter.WriteLine(message);
}

private void CustomLogicGoesHere(TextWriter textWriter)
{
textWriter.Write(_formatterOptions.CustomPrefix);
}

public void Dispose() => _optionsReloadToken?.Dispose();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Microsoft.Extensions.Logging.Console;

namespace Console.ExampleFormatters.Custom
{
public class CustomOptions : ConsoleFormatterOptions
{
public string CustomPrefix { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Microsoft.Extensions.Logging;

namespace Console.ExampleFormatters.Custom
{
class Program
{
static void Main()
{
using ILoggerFactory loggerFactory =
LoggerFactory.Create(builder =>
builder.AddCustomFormatter(options =>
options.CustomPrefix = " ~~~~~ "));

ILogger<Program> logger = loggerFactory.CreateLogger<Program>();
using (logger.BeginScope("TODO: Add logic to enable scopes"))
{
logger.LogInformation("Hello World!");
logger.LogInformation("TODO: Add logic to enable timestamp and log level info.");
}
}
}
}
Loading