From 49f3ef73c39b3912dd397adf6ee0d07e229e3e1e Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Wed, 21 Oct 2020 15:53:45 -0700 Subject: [PATCH 01/11] Add draft for console log formatter --- docs/core/extensions/console-log-formatter.md | 429 ++++++++++++++++++ 1 file changed, 429 insertions(+) create mode 100644 docs/core/extensions/console-log-formatter.md diff --git a/docs/core/extensions/console-log-formatter.md b/docs/core/extensions/console-log-formatter.md new file mode 100644 index 0000000000000..bcc05598e3d3e --- /dev/null +++ b/docs/core/extensions/console-log-formatter.md @@ -0,0 +1,429 @@ + + +# Console Log Formatting + +In .NET 5.0 we added support for custom formatting of console logs in Microsoft.Extensions.Logging.Console and three available in-box: simple, systemd, and json. + +## Motivation (Why needed) + +Traditionally in extensions ConsoleLoggerFormat enum allowed for toggling logging format between Systemd (single line) and Default (human readable) but they were not customizable and now have become deprecated. In environments where folks scrape/redirect console logs for structured logging, they want to know how to enable JSON formatted logs. + +## Abstract: Write an abstract. In one **short** paragraph, describe what this topic will cover. + +This article describes console log formatters. It goes through samples using the in-box console formatters, showing examples on how to register a new formatter, how to select a registered formatter to use (via both code and config) and how to author (implement) a custom formatter. While going through the custom console formatting topic we discuss more advanced topics such as how to ensure custom console logging gets updated configuration via IOptionsMonitor, and how to implement a color enabled custom formatter. + +## Console log formatting APIs + +### What is a console formatter? (covered below) + +### what are the built-in console log formatters? (covered below) + +### Register new formatter (covered below) + +The Console Logger provides built-in formatters and the ability author your own custom formatter. New APIs such as `AddSimpleConsole`, `AddSystemdConsole`, and `AddJsonConsole` were introduced in .NET 5.0 to provide different built-in formatting options for log messages to show in console. + +#### Simple + +Sample below shows how to configure the simple console formatter: + +```c# +using Microsoft.Extensions.Logging.Console; + +namespace ConsoleApp46 +{ + class Program + { + public static void Main(string[] args) + { + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddSimpleConsole(o => { + o.IncludeScopes = true; + o.SingleLine = true; + o.TimestampFormat = "hh:mm:ss "; + }); + }); + + var logger = loggerFactory.CreateLogger(); + using (logger.BeginScope("[scope is enabled]")) + { + logger.LogInformation("Hello World!"); + logger.LogInformation("Logs contain timestamp and log level."); + logger.LogInformation("Each log message is fit in a single line."); + } + } + } +} +``` + +The `ConsoleFormatterName.Simple` formatter provides logs with 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 Systemd console logger uses Syslog log level format and severities, does not format messages with colors, and always logs messages in a single line. This has especially been useful for containers which normally use Systemd console logging. Now with .NET 5.0, 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. + +```c# +using Microsoft.Extensions.Logging.Console; + +namespace ConsoleApp46 +{ + class Program + { + public static void Main(string[] args) + { + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddSystemdConsole(o => { + o.IncludeScopes = true; + o.TimestampFormat = "hh:mm:ss "; + }); + }); + + var logger = loggerFactory.CreateLogger(); + using (logger.BeginScope("[scope is enabled]")) + { + logger.LogInformation("Hello World!"); + logger.LogInformation("Logs contain timestamp and log level."); + logger.LogInformation("Systemd console logs never provide color options."); + logger.LogInformation("Systemd console logs always appear in a single line."); + } + } + } +} +``` + +#### Json + +The .NET community had long requested for a built-in performant console logger that writes logs in a Json format. The code below shows a console logging sample with ASP.NET Core sample. For example using template ASP.NET Core application: + +``` +dotnet new webapp -o SampleWebApp +``` + +When running the app, using the template code, we 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. To change from default, in Program.cs as seen below we could configure logging by adding `AddJsonConsole` which allows to select and configure the Json console formatter: + +```c# +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System.Text.Json; +using Microsoft.Extensions.WebEncoders.Testing; + +namespace SampleWebApp +{ + 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(); + }) + .ConfigureLogging(builder => + { + builder.AddJsonConsole(o => + { + o.IncludeScopes = false; + o.TimestampFormat = "hh:mm:ss "; + o.JsonWriterOptions = new JsonWriterOptions() + { + Encoder = JavaScriptTestEncoder.UnsafeRelaxedJsonEscaping, // TODO: 2 + Indented = true + }; + }); + }); + } +} +``` + +When running the app again, with the above change, the log message would look something like the snippet shown below: + +``` +{ + "Timestamp": "09:08:33 ", + "EventId": 0, + "LogLevel": "Information", + "Category": "Microsoft.Hosting.Lifetime", + "Message": "Now listening on: https://localhost:5001", + "State": { + "Message": "Now listening on: https://localhost:5001", + "address": "https://localhost:5001", + "{OriginalFormat}": "Now listening on: {address}" + } +} +``` + +Json console formatter by default logs each message in a single line, in order to make it more readable while configuring the formatter, we set Indented property on JsonWriterOptions to true. + +### How to select which formatter to use (via both code and config)? + +So far the samples showed how to register a formatter via code, but another way this can be done is via configuration. Looking back at the ASP.NET Core application sample, if we update the `appsettings.json` file rather than adding ConfigureLogging in Program.cs we could get the same outcome. Here's what we would add in the `appsettings.json` file: + +```json +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + }, + "Console": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + }, + "FormatterName": "json", + "FormatterOptions": { + "SingleLine" : true, + "IncludeScopes": true, + "TimestampFormat": "HH:mm:ss ", + "UseUtcTimestamp": true, + "JsonWriterOptions": { + "Indented": true, + } + } + } + }, + "AllowedHosts": "*" +} +``` + +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 would get selected, and its properties can be configured as long as they are provided as key inside the "FormatterOptions" element. The built-in formatter names are reserved under [ConsoleFormatterName](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.console.consoleformatternames?view=dotnet-plat-ext-5.0) (json, simple and systemd). It is also possible to register a custom formatter. In a later section we discuss that topic in more detail. + +### How do I author a new formatter? + +Log formatters need to be both registered (done by AddConsoleFormatter) and selected as the console formatter (done by AddConsole overloads) via the LoggingBuilder: + +```c# +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Console; +using System; + +namespace SampleConsoleApp +{ + public static class ConsoleLoggerExtensions + { + public static ILoggingBuilder AddCustomFormatter(this ILoggingBuilder builder, Action configure) + { + return builder.AddConsole(o => { o.FormatterName = "customName"; }) + .AddConsoleFormatter(configure); + } + } + + public class CustomOptions : ConsoleFormatterOptions + { + public string CustomPrefix { get; set; } + } +} +``` + +The `AddConsoleFormatter` API registers a ConsoleFormatter (in this example the CustomFormatter) along with configuration and change token which keeps the formatter in sync with code and configuration updates to FormatterOptions properties (in this example CustomOptions). + +```c# +namespace SampleConsoleApp +{ + class Program + { + public static void Main(string[] args) + { + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddCustomFormatter(o => + { + o.CustomPrefix = " ~~~~~ "; + }); + }); + + var logger = loggerFactory.CreateLogger(); + 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."); + } + } + } +} +``` + +The next piece is to implement the CustomFormatter. A very lightweight implementation is available below: + +```c# +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Logging.Console; +using Microsoft.Extensions.Options; +using System; +using System.IO; + +namespace SampleConsoleApp +{ + public class CustomFormatter : ConsoleFormatter, IDisposable + { + private IDisposable _optionsReloadToken; + + public CustomFormatter(IOptionsMonitor options) + // case insensitive name for the formatter + : base("customName") + { + ReloadLoggerOptions(options.CurrentValue); + _optionsReloadToken = options.OnChange(ReloadLoggerOptions); + } + + private void ReloadLoggerOptions(CustomOptions options) + { + FormatterOptions = options; + } + + public CustomOptions FormatterOptions { get; set; } + public void Dispose() + { + _optionsReloadToken?.Dispose(); + } + + public override void Write(in LogEntry logEntry, IExternalScopeProvider scopeProvider, TextWriter textWriter) + { + string message = logEntry.Formatter(logEntry.State, logEntry.Exception); + if (logEntry.Exception == null && message == null) + { + return; + } + CustomLogicGoesHere(textWriter); + textWriter.WriteLine(message); + } + + private void CustomLogicGoesHere(TextWriter textWriter) + { + textWriter.Write(FormatterOptions.CustomPrefix); + } + } +} +``` + +The above `CustomFormatter.Write` API dictates what text gets wrapped around each log message. A standard ConsoleFormatter, same as those provided in in the box like JsonConsoleFormatter, should be able to wrap around scopes, time stats and severity level of logs at least. Additionally they may be able to encode ANSI colors in the log messages or provide proper indentations as well. The above implementation for `CustomFormatter.Write` lacks these capabilities but is mainly to try out a hello world sample on custom console log formatting. To learn more about how to enable/customize scope/time/severity and optionally indentation/color capabilities take a deeper look at the implementation of the different console formatters available in the box in Microsoft.Extensions.Logging.Console, namely: [JsonConsoleFormatter](https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.cs), and [SimpleConsoleFormatter](https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.cs). + +#### Custom Formatting with Color capabilities + +In order to properly enable color capabilities in your custom console logger, you may have its FormatterOptions derive from `SimpleConsoleFormatterOptions` as it has a `LoggerColorBehavior` property that can be useful for enabling colors in logs. + +In your working sample from previous section, first update CustomOptions to derive from SimpleConsoleFormatterOptions: + +```c# + public class CustomOptions : SimpleConsoleFormatterOptions + { + public string CustomPrefix { get; set; } + } +``` + +Then update the CustomFormatter methods below: + +```c# +// TODO: Add DisableColors: + private bool DisableColors => + FormatterOptions.ColorBehavior == LoggerColorBehavior.Disabled || + (FormatterOptions.ColorBehavior == LoggerColorBehavior.Default && System.Console.IsOutputRedirected); + + private void CustomLogicGoesHere(TextWriter textWriter) + { + if (DisableColors) + { + textWriter.Write(FormatterOptions.CustomPrefix); + } + else + { + textWriter.WriteWithColor(FormatterOptions.CustomPrefix, ConsoleColor.Black, ConsoleColor.Green); + } + } +``` + +In order to remove the squiggly on `WriteColorMessage` add the below helper methods as well. These extension methods in `TextWriterExtensions` allow for conveniently embedding ANSI coded colors within formatted log messages: + +```c# +using System; +using System.IO; + +namespace SampleConsoleApp +{ + public static class TextWriterExtensions + { + public static void WriteWithColor(this TextWriter textWriter, string message, ConsoleColor? background, ConsoleColor? foreground) + { + // Order: backgroundcolor, foregroundcolor, Message, reset foregroundcolor, reset backgroundcolor + if (background.HasValue) + { + textWriter.Write(GetBackgroundColorEscapeCode(background.Value)); + } + if (foreground.HasValue) + { + textWriter.Write(GetForegroundColorEscapeCode(foreground.Value)); + } + textWriter.WriteLine(message); + if (foreground.HasValue) + { + textWriter.Write(DefaultForegroundColor); // reset to default foreground color + } + if (background.HasValue) + { + textWriter.Write(DefaultBackgroundColor); // reset to the background color + } + } + internal const string DefaultForegroundColor = "\x1B[39m\x1B[22m"; // reset to default foreground color + internal const string DefaultBackgroundColor = "\x1B[49m"; // reset to the background color + internal static string GetForegroundColorEscapeCode(ConsoleColor color) + { + return color switch + { + ConsoleColor.Black => "\x1B[30m", + ConsoleColor.DarkRed => "\x1B[31m", + ConsoleColor.DarkGreen => "\x1B[32m", + ConsoleColor.DarkYellow => "\x1B[33m", + ConsoleColor.DarkBlue => "\x1B[34m", + ConsoleColor.DarkMagenta => "\x1B[35m", + ConsoleColor.DarkCyan => "\x1B[36m", + ConsoleColor.Gray => "\x1B[37m", + ConsoleColor.Red => "\x1B[1m\x1B[31m", + ConsoleColor.Green => "\x1B[1m\x1B[32m", + ConsoleColor.Yellow => "\x1B[1m\x1B[33m", + ConsoleColor.Blue => "\x1B[1m\x1B[34m", + ConsoleColor.Magenta => "\x1B[1m\x1B[35m", + ConsoleColor.Cyan => "\x1B[1m\x1B[36m", + ConsoleColor.White => "\x1B[1m\x1B[37m", + _ => DefaultForegroundColor // default foreground color + }; + } + + internal static string GetBackgroundColorEscapeCode(ConsoleColor color) + { + return color switch + { + ConsoleColor.Black => "\x1B[40m", + ConsoleColor.DarkRed => "\x1B[41m", + ConsoleColor.DarkGreen => "\x1B[42m", + ConsoleColor.DarkYellow => "\x1B[43m", + ConsoleColor.DarkBlue => "\x1B[44m", + ConsoleColor.DarkMagenta => "\x1B[45m", + ConsoleColor.DarkCyan => "\x1B[46m", + ConsoleColor.Gray => "\x1B[47m", + _ => DefaultBackgroundColor // Use default background color + }; + } + } +} +``` + +When you run your application again, the logs would show `CustomPrefix` message in green when FormatterOptions.ColorBehavior is not disabled. So for example, when configuring options in `AddCustomFormatter` below, when color behavior is set to disabled, the the same log prefix will no longer be shown in green. + + + From b108a07f83d963af94d9199675b91b4fde2e2dfd Mon Sep 17 00:00:00 2001 From: David Pine Date: Thu, 22 Oct 2020 08:39:21 -0500 Subject: [PATCH 02/11] Extract code into local snippets/csproj --- docs/core/extensions/console-log-formatter.md | 68 ++++++++------- .../ConsoleLoggerExtensions.cs | 14 +++ .../CustomColorFormatter.cs | 55 ++++++++++++ .../CustomColorOptions.cs | 9 ++ .../CustomFormatter.cs | 55 ++++++++++++ .../console-formatter-custom/CustomOptions.cs | 9 ++ .../console-formatter-custom/Program.cs | 25 ++++++ .../TextWriterExtensions.cs | 87 +++++++++++++++++++ .../console-formatter-custom.csproj | 13 +++ .../logging/console-formatter-json/Program.cs | 26 ++++++ .../console-formatter-json/appsettings.json | 27 ++++++ .../console-formatter-json.csproj | 13 +++ .../example-output.json | 12 +++ .../console-formatter-simple/Program.cs | 28 ++++++ .../console-formatter-simple.csproj | 14 +++ .../console-formatter-systemd/Program.cs | 27 ++++++ .../console-formatter-systemd.csproj | 13 +++ 17 files changed, 465 insertions(+), 30 deletions(-) create mode 100644 docs/core/extensions/snippets/logging/console-formatter-custom/ConsoleLoggerExtensions.cs create mode 100644 docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorFormatter.cs create mode 100644 docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorOptions.cs create mode 100644 docs/core/extensions/snippets/logging/console-formatter-custom/CustomFormatter.cs create mode 100644 docs/core/extensions/snippets/logging/console-formatter-custom/CustomOptions.cs create mode 100644 docs/core/extensions/snippets/logging/console-formatter-custom/Program.cs create mode 100644 docs/core/extensions/snippets/logging/console-formatter-custom/TextWriterExtensions.cs create mode 100644 docs/core/extensions/snippets/logging/console-formatter-custom/console-formatter-custom.csproj create mode 100644 docs/core/extensions/snippets/logging/console-formatter-json/Program.cs create mode 100644 docs/core/extensions/snippets/logging/console-formatter-json/appsettings.json create mode 100644 docs/core/extensions/snippets/logging/console-formatter-json/console-formatter-json.csproj create mode 100644 docs/core/extensions/snippets/logging/console-formatter-json/example-output.json create mode 100644 docs/core/extensions/snippets/logging/console-formatter-simple/Program.cs create mode 100644 docs/core/extensions/snippets/logging/console-formatter-simple/console-formatter-simple.csproj create mode 100644 docs/core/extensions/snippets/logging/console-formatter-systemd/Program.cs create mode 100644 docs/core/extensions/snippets/logging/console-formatter-systemd/console-formatter-systemd.csproj diff --git a/docs/core/extensions/console-log-formatter.md b/docs/core/extensions/console-log-formatter.md index bcc05598e3d3e..c3483ecee47cc 100644 --- a/docs/core/extensions/console-log-formatter.md +++ b/docs/core/extensions/console-log-formatter.md @@ -1,12 +1,17 @@ +--- +title: Implement a custom configuration provider in .NET +description: Learn how to implement a custom configuration provider in .NET applications. +author: IEvangelist +ms.author: dapine +ms.date: 10/21/2020 +--- +# Console log formatting -# Console Log Formatting +In .NET 5, support for custom formatting was added for console logs in the `Microsoft.Extensions.Logging.Console` namespace. There are three custom formatting options available: `Simple`, `Systemd`, and `Json`. In environments where scraping and redirecting console logs for structured logging, it is advantageous to have customization capabilities. -In .NET 5.0 we added support for custom formatting of console logs in Microsoft.Extensions.Logging.Console and three available in-box: simple, systemd, and json. - -## Motivation (Why needed) - -Traditionally in extensions ConsoleLoggerFormat enum allowed for toggling logging format between Systemd (single line) and Default (human readable) but they were not customizable and now have become deprecated. In environments where folks scrape/redirect console logs for structured logging, they want to know how to enable JSON formatted logs. +> [!IMPORTANT] +> The enum allowed for selecting the desired log format, either human readable which was the `Default`, or single line which is known as `Systemd`. However, these were **not** customizable, and are now deprecated. ## Abstract: Write an abstract. In one **short** paragraph, describe what this topic will cover. @@ -20,13 +25,13 @@ This article describes console log formatters. It goes through samples using the ### Register new formatter (covered below) -The Console Logger provides built-in formatters and the ability author your own custom formatter. New APIs such as `AddSimpleConsole`, `AddSystemdConsole`, and `AddJsonConsole` were introduced in .NET 5.0 to provide different built-in formatting options for log messages to show in console. +The Console Logger provides built-in formatters and the ability author your own custom formatter. New APIs such as `AddSimpleConsole`, `AddSystemdConsole`, and `AddJsonConsole` were introduced in .NET 5.0 to provide different built-in formatting options for log messages to show in console. #### Simple Sample below shows how to configure the simple console formatter: -```c# +```csharp using Microsoft.Extensions.Logging.Console; namespace ConsoleApp46 @@ -56,13 +61,13 @@ namespace ConsoleApp46 } ``` -The `ConsoleFormatterName.Simple` formatter provides logs with 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. +The `ConsoleFormatterName.Simple` formatter provides logs with 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 Systemd console logger uses Syslog log level format and severities, does not format messages with colors, and always logs messages in a single line. This has especially been useful for containers which normally use Systemd console logging. Now with .NET 5.0, 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. -```c# +```csharp using Microsoft.Extensions.Logging.Console; namespace ConsoleApp46 @@ -96,7 +101,7 @@ namespace ConsoleApp46 The .NET community had long requested for a built-in performant console logger that writes logs in a Json format. The code below shows a console logging sample with ASP.NET Core sample. For example using template ASP.NET Core application: -``` +```dotnetcli dotnet new webapp -o SampleWebApp ``` @@ -109,7 +114,7 @@ info: Microsoft.Hosting.Lifetime[0] By default, the Simple console log formatter is selected with default configuration. To change from default, in Program.cs as seen below we could configure logging by adding `AddJsonConsole` which allows to select and configure the Json console formatter: -```c# +```csharp using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -150,7 +155,7 @@ namespace SampleWebApp When running the app again, with the above change, the log message would look something like the snippet shown below: -``` +```json { "Timestamp": "09:08:33 ", "EventId": 0, @@ -165,7 +170,7 @@ When running the app again, with the above change, the log message would look so } ``` -Json console formatter by default logs each message in a single line, in order to make it more readable while configuring the formatter, we set Indented property on JsonWriterOptions to true. +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 to `true`. ### How to select which formatter to use (via both code and config)? @@ -201,13 +206,19 @@ So far the samples showed how to register a formatter via code, but another way } ``` -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 would get selected, and its properties can be configured as long as they are provided as key inside the "FormatterOptions" element. The built-in formatter names are reserved under [ConsoleFormatterName](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.console.consoleformatternames?view=dotnet-plat-ext-5.0) (json, simple and systemd). It is also possible to register a custom formatter. In a later section we discuss that topic in more detail. +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 would get selected, and its properties can be configured as long as they are provided as key inside the "FormatterOptions" element. The built-in formatter names are reserved under : + +- +- +- + +It is also possible to register a custom formatter. In a later section we discuss that topic in more detail. ### How do I author a new formatter? Log formatters need to be both registered (done by AddConsoleFormatter) and selected as the console formatter (done by AddConsole overloads) via the LoggingBuilder: -```c# +```csharp using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Console; using System; @@ -216,11 +227,11 @@ namespace SampleConsoleApp { public static class ConsoleLoggerExtensions { - public static ILoggingBuilder AddCustomFormatter(this ILoggingBuilder builder, Action configure) - { - return builder.AddConsole(o => { o.FormatterName = "customName"; }) + public static ILoggingBuilder AddCustomFormatter( + this ILoggingBuilder builder, + Action configure) => + builder.AddConsole(o => o.FormatterName = "customName") .AddConsoleFormatter(configure); - } } public class CustomOptions : ConsoleFormatterOptions @@ -232,7 +243,7 @@ namespace SampleConsoleApp The `AddConsoleFormatter` API registers a ConsoleFormatter (in this example the CustomFormatter) along with configuration and change token which keeps the formatter in sync with code and configuration updates to FormatterOptions properties (in this example CustomOptions). -```c# +```csharp namespace SampleConsoleApp { class Program @@ -260,7 +271,7 @@ namespace SampleConsoleApp The next piece is to implement the CustomFormatter. A very lightweight implementation is available below: -```c# +```csharp using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Console; @@ -275,8 +286,7 @@ namespace SampleConsoleApp private IDisposable _optionsReloadToken; public CustomFormatter(IOptionsMonitor options) - // case insensitive name for the formatter - : base("customName") + : base("customName") // Case insensitive { ReloadLoggerOptions(options.CurrentValue); _optionsReloadToken = options.OnChange(ReloadLoggerOptions); @@ -288,6 +298,7 @@ namespace SampleConsoleApp } public CustomOptions FormatterOptions { get; set; } + public void Dispose() { _optionsReloadToken?.Dispose(); @@ -320,7 +331,7 @@ In order to properly enable color capabilities in your custom console logger, yo In your working sample from previous section, first update CustomOptions to derive from SimpleConsoleFormatterOptions: -```c# +```csharp public class CustomOptions : SimpleConsoleFormatterOptions { public string CustomPrefix { get; set; } @@ -329,7 +340,7 @@ In your working sample from previous section, first update CustomOptions to deri Then update the CustomFormatter methods below: -```c# +```csharp // TODO: Add DisableColors: private bool DisableColors => FormatterOptions.ColorBehavior == LoggerColorBehavior.Disabled || @@ -350,7 +361,7 @@ Then update the CustomFormatter methods below: In order to remove the squiggly on `WriteColorMessage` add the below helper methods as well. These extension methods in `TextWriterExtensions` allow for conveniently embedding ANSI coded colors within formatted log messages: -```c# +```csharp using System; using System.IO; @@ -424,6 +435,3 @@ namespace SampleConsoleApp ``` When you run your application again, the logs would show `CustomPrefix` message in green when FormatterOptions.ColorBehavior is not disabled. So for example, when configuring options in `AddCustomFormatter` below, when color behavior is set to disabled, the the same log prefix will no longer be shown in green. - - - diff --git a/docs/core/extensions/snippets/logging/console-formatter-custom/ConsoleLoggerExtensions.cs b/docs/core/extensions/snippets/logging/console-formatter-custom/ConsoleLoggerExtensions.cs new file mode 100644 index 0000000000000..256c04df889ba --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-custom/ConsoleLoggerExtensions.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.Logging; +using System; + +namespace Console.ExampleFormatters.Json +{ + public static class ConsoleLoggerExtensions + { + public static ILoggingBuilder AddCustomFormatter( + this ILoggingBuilder builder, + Action configure) => + builder.AddConsole(options => options.FormatterName = "customName") + .AddConsoleFormatter(configure); + } +} diff --git a/docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorFormatter.cs b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorFormatter.cs new file mode 100644 index 0000000000000..c4a076d4fbdd4 --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorFormatter.cs @@ -0,0 +1,55 @@ +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 class CustomColorFormatter : ConsoleFormatter, IDisposable + { + private readonly IDisposable _optionsReloadToken; + private CustomColorOptions _formatterOptions; + + public CustomColorFormatter(IOptionsMonitor options) + : base("customName") // case insensitive + { + _optionsReloadToken = options.OnChange(ReloadLoggerOptions); + _formatterOptions = options.CurrentValue; + } + + private void ReloadLoggerOptions(CustomOptions options) => + _formatterOptions = options; + + public override void Write( + in LogEntry 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(); + } +} diff --git a/docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorOptions.cs b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorOptions.cs new file mode 100644 index 0000000000000..1b2a48ef2b5e7 --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorOptions.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Logging.Console; + +namespace Console.ExampleFormatters.Custom +{ + public class CustomOptions : SimpleConsoleFormatterOptions + { + public string CustomPrefix { get; set; } + } +} diff --git a/docs/core/extensions/snippets/logging/console-formatter-custom/CustomFormatter.cs b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomFormatter.cs new file mode 100644 index 0000000000000..d612f29c436c4 --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomFormatter.cs @@ -0,0 +1,55 @@ +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.Json +{ + public sealed class CustomFormatter : ConsoleFormatter, IDisposable + { + private readonly IDisposable _optionsReloadToken; + private CustomOptions _formatterOptions; + + public CustomFormatter(IOptionsMonitor options) + : base("customName") // case insensitive + { + _optionsReloadToken = options.OnChange(ReloadLoggerOptions); + _formatterOptions = options.CurrentValue; + } + + private void ReloadLoggerOptions(CustomOptions options) => + _formatterOptions = options; + + public override void Write( + in LogEntry 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(); + } +} diff --git a/docs/core/extensions/snippets/logging/console-formatter-custom/CustomOptions.cs b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomOptions.cs new file mode 100644 index 0000000000000..1b55364b6a66c --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomOptions.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Logging.Console; + +namespace Console.ExampleFormatters.Json +{ + public class CustomOptions : ConsoleFormatterOptions + { + public string CustomPrefix { get; set; } + } +} diff --git a/docs/core/extensions/snippets/logging/console-formatter-custom/Program.cs b/docs/core/extensions/snippets/logging/console-formatter-custom/Program.cs new file mode 100644 index 0000000000000..1be848cac5fcf --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-custom/Program.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.Hosting; + +namespace Console.ExampleFormatters.Custom +{ + class Program + { + static void Main(string[] args) + { + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddCustomFormatter(options => + { + options.CustomPrefix = " ~~~~~ "; + }); + }); + + var logger = loggerFactory.CreateLogger(); + 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."); + } + } + } +} diff --git a/docs/core/extensions/snippets/logging/console-formatter-custom/TextWriterExtensions.cs b/docs/core/extensions/snippets/logging/console-formatter-custom/TextWriterExtensions.cs new file mode 100644 index 0000000000000..60f0e00a61278 --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-custom/TextWriterExtensions.cs @@ -0,0 +1,87 @@ +using System; +using System.IO; + +namespace Console.ExampleFormatters.Custom +{ + public static class TextWriterExtensions + { + const string DefaultForegroundColor = "\x1B[39m\x1B[22m"; + const string DefaultBackgroundColor = "\x1B[49m"; + + public static void WriteWithColor( + this TextWriter textWriter, + string message, + ConsoleColor? background, + ConsoleColor? foreground) + { + // Order: + // 1. background color + // 2. foreground color + // 3. message + // 4. reset foreground color + // 5. reset background color + + (bool writeBackgroundColor, string backgroundColor) = + (background.HasValue, GetBackgroundColorEscapeCode(background.Value)); + (bool writeForegroundColor, string foregroundColor) = + (foreground.HasValue, GetForegroundColorEscapeCode(foreground.Value)); + + if (writeBackgroundColor) + { + textWriter.Write(backgroundColor); + } + if (writeForegroundColor) + { + textWriter.Write(foregroundColor); + } + + textWriter.WriteLine(message); + + if (writeForegroundColor) + { + textWriter.Write(DefaultForegroundColor); + } + if (writeBackgroundColor) + { + textWriter.Write(DefaultBackgroundColor); + } + } + + static string GetForegroundColorEscapeCode(ConsoleColor color) => + color switch + { + ConsoleColor.Black => "\x1B[30m", + ConsoleColor.DarkRed => "\x1B[31m", + ConsoleColor.DarkGreen => "\x1B[32m", + ConsoleColor.DarkYellow => "\x1B[33m", + ConsoleColor.DarkBlue => "\x1B[34m", + ConsoleColor.DarkMagenta => "\x1B[35m", + ConsoleColor.DarkCyan => "\x1B[36m", + ConsoleColor.Gray => "\x1B[37m", + ConsoleColor.Red => "\x1B[1m\x1B[31m", + ConsoleColor.Green => "\x1B[1m\x1B[32m", + ConsoleColor.Yellow => "\x1B[1m\x1B[33m", + ConsoleColor.Blue => "\x1B[1m\x1B[34m", + ConsoleColor.Magenta => "\x1B[1m\x1B[35m", + ConsoleColor.Cyan => "\x1B[1m\x1B[36m", + ConsoleColor.White => "\x1B[1m\x1B[37m", + + _ => DefaultForegroundColor + }; + + static string GetBackgroundColorEscapeCode(ConsoleColor color) => + color switch + { + ConsoleColor.Black => "\x1B[40m", + ConsoleColor.DarkRed => "\x1B[41m", + ConsoleColor.DarkGreen => "\x1B[42m", + ConsoleColor.DarkYellow => "\x1B[43m", + ConsoleColor.DarkBlue => "\x1B[44m", + ConsoleColor.DarkMagenta => "\x1B[45m", + ConsoleColor.DarkCyan => "\x1B[46m", + ConsoleColor.Gray => "\x1B[47m", + + _ => DefaultBackgroundColor + }; + } +} diff --git a/docs/core/extensions/snippets/logging/console-formatter-custom/console-formatter-custom.csproj b/docs/core/extensions/snippets/logging/console-formatter-custom/console-formatter-custom.csproj new file mode 100644 index 0000000000000..6e38e3c303f8d --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-custom/console-formatter-custom.csproj @@ -0,0 +1,13 @@ + + + + Exe + net5.0 + Console.ExampleFormatters.Custom + + + + + + + diff --git a/docs/core/extensions/snippets/logging/console-formatter-json/Program.cs b/docs/core/extensions/snippets/logging/console-formatter-json/Program.cs new file mode 100644 index 0000000000000..ab4072f8ac226 --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-json/Program.cs @@ -0,0 +1,26 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; + +namespace Console.ExampleFormatters.Json +{ + class Program + { + static Task Main(string[] args) => + CreateHostBuilder(args).Build().RunAsync(); + + static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(builder => builder.UseStartup()) + .ConfigureLogging(builder => + builder.AddJsonConsole(options => + { + options.IncludeScopes = false; + options.TimestampFormat = "hh:mm:ss "; + options.JsonWriterOptions = new JsonWriterOptions + { + Encoder = JavaScriptTestEncoder.UnsafeRelaxedJsonEscaping, + Indented = true + }; + })); + } +} diff --git a/docs/core/extensions/snippets/logging/console-formatter-json/appsettings.json b/docs/core/extensions/snippets/logging/console-formatter-json/appsettings.json new file mode 100644 index 0000000000000..ecbfacc4d3460 --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-json/appsettings.json @@ -0,0 +1,27 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + }, + "Console": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + }, + "FormatterName": "json", + "FormatterOptions": { + "SingleLine": true, + "IncludeScopes": true, + "TimestampFormat": "HH:mm:ss ", + "UseUtcTimestamp": true, + "JsonWriterOptions": { + "Indented": true + } + } + } + }, + "AllowedHosts": "*" +} diff --git a/docs/core/extensions/snippets/logging/console-formatter-json/console-formatter-json.csproj b/docs/core/extensions/snippets/logging/console-formatter-json/console-formatter-json.csproj new file mode 100644 index 0000000000000..958aecf2896ed --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-json/console-formatter-json.csproj @@ -0,0 +1,13 @@ + + + + Exe + net5.0 + Console.ExampleFormatters.Json + + + + + + + diff --git a/docs/core/extensions/snippets/logging/console-formatter-json/example-output.json b/docs/core/extensions/snippets/logging/console-formatter-json/example-output.json new file mode 100644 index 0000000000000..6414fca7f0e99 --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-json/example-output.json @@ -0,0 +1,12 @@ +{ + "Timestamp": "09:08:33 ", + "EventId": 0, + "LogLevel": "Information", + "Category": "Microsoft.Hosting.Lifetime", + "Message": "Now listening on: https://localhost:5001", + "State": { + "Message": "Now listening on: https://localhost:5001", + "address": "https://localhost:5001", + "{OriginalFormat}": "Now listening on: {address}" + } +} diff --git a/docs/core/extensions/snippets/logging/console-formatter-simple/Program.cs b/docs/core/extensions/snippets/logging/console-formatter-simple/Program.cs new file mode 100644 index 0000000000000..8351aa675d09a --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-simple/Program.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.Logging.Console; + +namespace Console.ExampleFormatters.Simple +{ + class Program + { + static void Main(string[] args) + { + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddSimpleConsole(o => + { + o.IncludeScopes = true; + o.SingleLine = true; + o.TimestampFormat = "hh:mm:ss "; + }); + }); + + var logger = loggerFactory.CreateLogger(); + using (logger.BeginScope("[scope is enabled]")) + { + logger.LogInformation("Hello World!"); + logger.LogInformation("Logs contain timestamp and log level."); + logger.LogInformation("Each log message is fit in a single line."); + } + } + } +} diff --git a/docs/core/extensions/snippets/logging/console-formatter-simple/console-formatter-simple.csproj b/docs/core/extensions/snippets/logging/console-formatter-simple/console-formatter-simple.csproj new file mode 100644 index 0000000000000..387194f795f6d --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-simple/console-formatter-simple.csproj @@ -0,0 +1,14 @@ + + + + Exe + net5.0 + Console.ExampleFormatters.Simple + + + + + + + + diff --git a/docs/core/extensions/snippets/logging/console-formatter-systemd/Program.cs b/docs/core/extensions/snippets/logging/console-formatter-systemd/Program.cs new file mode 100644 index 0000000000000..ca83510d55311 --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-systemd/Program.cs @@ -0,0 +1,27 @@ +using Microsoft.Extensions.Hosting; + +namespace Console.ExampleFormatters.Systemd +{ + class Program + { + static void Main(string[] args) + { + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddSystemdConsole(o => { + o.IncludeScopes = true; + o.TimestampFormat = "hh:mm:ss "; + }); + }); + + var logger = loggerFactory.CreateLogger(); + using (logger.BeginScope("[scope is enabled]")) + { + logger.LogInformation("Hello World!"); + logger.LogInformation("Logs contain timestamp and log level."); + logger.LogInformation("Systemd console logs never provide color options."); + logger.LogInformation("Systemd console logs always appear in a single line."); + } + } + } +} diff --git a/docs/core/extensions/snippets/logging/console-formatter-systemd/console-formatter-systemd.csproj b/docs/core/extensions/snippets/logging/console-formatter-systemd/console-formatter-systemd.csproj new file mode 100644 index 0000000000000..08431434fe220 --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-systemd/console-formatter-systemd.csproj @@ -0,0 +1,13 @@ + + + + Exe + net5.0 + Console.ExampleFormatters.Systemd + + + + + + + From cd612d6bee936760781245d018842ed1c78d289b Mon Sep 17 00:00:00 2001 From: David Pine Date: Thu, 22 Oct 2020 10:42:09 -0500 Subject: [PATCH 03/11] Working updates on draft improvements. --- docs/core/extensions/console-log-formatter.md | 479 ++++-------------- docs/core/extensions/logging.md | 1 + .../ConsoleLoggerExtensions.cs | 2 +- .../CustomColorFormatter.cs | 32 +- .../CustomColorOptions.cs | 2 +- .../CustomFormatter.cs | 11 +- .../console-formatter-custom/CustomOptions.cs | 2 +- .../console-formatter-custom/Program.cs | 17 +- .../logging/console-formatter-json/Program.cs | 6 +- .../Properties/launchSettings.json | 27 + .../logging/console-formatter-json/Startup.cs | 52 ++ .../console-formatter-json.csproj | 2 +- .../console-formatter-simple/Program.cs | 23 +- .../console-formatter-systemd/Program.cs | 20 +- docs/fundamentals/toc.yml | 2 + 15 files changed, 241 insertions(+), 437 deletions(-) create mode 100644 docs/core/extensions/snippets/logging/console-formatter-json/Properties/launchSettings.json create mode 100644 docs/core/extensions/snippets/logging/console-formatter-json/Startup.cs diff --git a/docs/core/extensions/console-log-formatter.md b/docs/core/extensions/console-log-formatter.md index c3483ecee47cc..50763da9cffca 100644 --- a/docs/core/extensions/console-log-formatter.md +++ b/docs/core/extensions/console-log-formatter.md @@ -1,437 +1,148 @@ --- -title: Implement a custom configuration provider in .NET -description: Learn how to implement a custom configuration provider in .NET applications. +title: Console log formatting +description: Learn how to use built-in console log formatting, or implement custom log formatting for your .NET applications. author: IEvangelist ms.author: dapine -ms.date: 10/21/2020 +ms.date: 10/22/2020 --- # Console log formatting -In .NET 5, support for custom formatting was added for console logs in the `Microsoft.Extensions.Logging.Console` namespace. There are three custom formatting options available: `Simple`, `Systemd`, and `Json`. In environments where scraping and redirecting console logs for structured logging, it is advantageous to have customization capabilities. +In .NET 5, support for custom formatting was added to console logs in the `Microsoft.Extensions.Logging.Console` namespace. There are three built-in formatting options available: [`Simple`](#simple), [`Systemd`](#systemd), and [`Json`](#json). > [!IMPORTANT] -> The enum allowed for selecting the desired log format, either human readable which was the `Default`, or single line which is known as `Systemd`. However, these were **not** customizable, and are now deprecated. +> Previously, the 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. -## Abstract: Write an abstract. In one **short** paragraph, describe what this topic will cover. +In this article you will learn about console log formatters. The sample source code demonstrate how to: -This article describes console log formatters. It goes through samples using the in-box console formatters, showing examples on how to register a new formatter, how to select a registered formatter to use (via both code and config) and how to author (implement) a custom formatter. While going through the custom console formatting topic we discuss more advanced topics such as how to ensure custom console logging gets updated configuration via IOptionsMonitor, and how to implement a color enabled custom formatter. +- Register a new formatter +- Select a registered formatter to use + - Either through code, or [configuration](configuration.md) +- Implement a custom formatter + - Update configuration via + - Enable custom color formatting -## Console log formatting APIs +## Register formatter -### What is a console formatter? (covered below) +The [`Console` logging provider](logging-providers.md#console) has built-in formatters, and exposes the ability to author your own custom formatter. To register any of the built-in formatters, use the corresponding `Add{Type}Console` extension method: -### what are the built-in console log formatters? (covered below) +| Built-in types | Method to register type | +|--|--| +| | | +| | | +| | | -### Register new formatter (covered below) +### Simple -The Console Logger provides built-in formatters and the ability author your own custom formatter. New APIs such as `AddSimpleConsole`, `AddSystemdConsole`, and `AddJsonConsole` were introduced in .NET 5.0 to provide different built-in formatting options for log messages to show in console. +To register, and use the simple console formatter: -#### Simple +:::code language="csharp" source="snippets/logging/console-formatter-simple/Program.cs"::: -Sample below shows how to configure the simple console formatter: +In the proceeding sample source code, the 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. -```csharp -using Microsoft.Extensions.Logging.Console; +### Systemd -namespace ConsoleApp46 -{ - class Program - { - public static void Main(string[] args) - { - using var loggerFactory = LoggerFactory.Create(builder => - { - builder.AddSimpleConsole(o => { - o.IncludeScopes = true; - o.SingleLine = true; - o.TimestampFormat = "hh:mm:ss "; - }); - }); +The console logger: - var logger = loggerFactory.CreateLogger(); - using (logger.BeginScope("[scope is enabled]")) - { - logger.LogInformation("Hello World!"); - logger.LogInformation("Logs contain timestamp and log level."); - logger.LogInformation("Each log message is fit in a single line."); - } - } - } -} -``` +- Uses the "Syslog" log level format and severities +- Does **not** format messages with colors +- Always logs messages in a single line -The `ConsoleFormatterName.Simple` formatter provides logs with 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 Systemd console logger uses Syslog log level format and severities, does not format messages with colors, and always logs messages in a single line. This has especially been useful for containers which normally use Systemd console logging. Now with .NET 5.0, 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. - -```csharp -using Microsoft.Extensions.Logging.Console; - -namespace ConsoleApp46 -{ - class Program - { - public static void Main(string[] args) - { - using var loggerFactory = LoggerFactory.Create(builder => - { - builder.AddSystemdConsole(o => { - o.IncludeScopes = true; - o.TimestampFormat = "hh:mm:ss "; - }); - }); - - var logger = loggerFactory.CreateLogger(); - using (logger.BeginScope("[scope is enabled]")) - { - logger.LogInformation("Hello World!"); - logger.LogInformation("Logs contain timestamp and log level."); - logger.LogInformation("Systemd console logs never provide color options."); - logger.LogInformation("Systemd console logs always appear 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. -#### Json +:::code language="csharp" source="snippets/logging/console-formatter-systemd/Program.cs"::: -The .NET community had long requested for a built-in performant console logger that writes logs in a Json format. The code below shows a console logging sample with ASP.NET Core sample. For example using template ASP.NET Core application: +### Json + +To write logs in a JSON format, the built-in `Json` console formatter is used. The sample source code shows how an ASP.NET Core app might register it. For example using template ASP.NET Core application: ```dotnetcli dotnet new webapp -o SampleWebApp ``` -When running the app, using the template code, we get the default log format below: +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. To change from default, in Program.cs as seen below we could configure logging by adding `AddJsonConsole` which allows to select and configure the Json console formatter: - -```csharp -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System.Text.Json; -using Microsoft.Extensions.WebEncoders.Testing; - -namespace SampleWebApp -{ - 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(); - }) - .ConfigureLogging(builder => - { - builder.AddJsonConsole(o => - { - o.IncludeScopes = false; - o.TimestampFormat = "hh:mm:ss "; - o.JsonWriterOptions = new JsonWriterOptions() - { - Encoder = JavaScriptTestEncoder.UnsafeRelaxedJsonEscaping, // TODO: 2 - Indented = true - }; - }); - }); - } -} -``` +By default, the `Simple` console log formatter is selected with default configuration. You change this by calling `AddJsonConsole` in the *Program.cs*: -When running the app again, with the above change, the log message would look something like the snippet shown below: - -```json -{ - "Timestamp": "09:08:33 ", - "EventId": 0, - "LogLevel": "Information", - "Category": "Microsoft.Hosting.Lifetime", - "Message": "Now listening on: https://localhost:5001", - "State": { - "Message": "Now listening on: https://localhost:5001", - "address": "https://localhost:5001", - "{OriginalFormat}": "Now listening on: {address}" - } -} -``` +:::code language="csharp" source="snippets/logging/console-formatter-json/Program.cs" highlight="17-26"::: -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 to `true`. - -### How to select which formatter to use (via both code and config)? - -So far the samples showed how to register a formatter via code, but another way this can be done is via configuration. Looking back at the ASP.NET Core application sample, if we update the `appsettings.json` file rather than adding ConfigureLogging in Program.cs we could get the same outcome. Here's what we would add in the `appsettings.json` file: - -```json -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - }, - "Console": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - }, - "FormatterName": "json", - "FormatterOptions": { - "SingleLine" : true, - "IncludeScopes": true, - "TimestampFormat": "HH:mm:ss ", - "UseUtcTimestamp": true, - "JsonWriterOptions": { - "Indented": true, - } - } - } - }, - "AllowedHosts": "*" -} -``` +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 to `true`. -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 would get selected, and its properties can be configured as long as they are provided as key inside the "FormatterOptions" element. The built-in formatter names are reserved under : +### 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"::: + +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 built-in formatter names are reserved under : - - - -It is also possible to register a custom formatter. In a later section we discuss that topic in more detail. - -### How do I author a new formatter? - -Log formatters need to be both registered (done by AddConsoleFormatter) and selected as the console formatter (done by AddConsole overloads) via the LoggingBuilder: - -```csharp -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Console; -using System; - -namespace SampleConsoleApp -{ - public static class ConsoleLoggerExtensions - { - public static ILoggingBuilder AddCustomFormatter( - this ILoggingBuilder builder, - Action configure) => - builder.AddConsole(o => o.FormatterName = "customName") - .AddConsoleFormatter(configure); - } - - public class CustomOptions : ConsoleFormatterOptions - { - public string CustomPrefix { get; set; } - } -} -``` +### Implement a custom formatter -The `AddConsoleFormatter` API registers a ConsoleFormatter (in this example the CustomFormatter) along with configuration and change token which keeps the formatter in sync with code and configuration updates to FormatterOptions properties (in this example CustomOptions). - -```csharp -namespace SampleConsoleApp -{ - class Program - { - public static void Main(string[] args) - { - using var loggerFactory = LoggerFactory.Create(builder => - { - builder.AddCustomFormatter(o => - { - o.CustomPrefix = " ~~~~~ "; - }); - }); - - var logger = loggerFactory.CreateLogger(); - 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."); - } - } - } -} -``` +To implement a custom formatter, you need to: -The next piece is to implement the CustomFormatter. A very lightweight implementation is available below: - -```csharp -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Logging.Console; -using Microsoft.Extensions.Options; -using System; -using System.IO; - -namespace SampleConsoleApp -{ - public class CustomFormatter : ConsoleFormatter, IDisposable - { - private IDisposable _optionsReloadToken; - - public CustomFormatter(IOptionsMonitor options) - : base("customName") // Case insensitive - { - ReloadLoggerOptions(options.CurrentValue); - _optionsReloadToken = options.OnChange(ReloadLoggerOptions); - } - - private void ReloadLoggerOptions(CustomOptions options) - { - FormatterOptions = options; - } - - public CustomOptions FormatterOptions { get; set; } - - public void Dispose() - { - _optionsReloadToken?.Dispose(); - } - - public override void Write(in LogEntry logEntry, IExternalScopeProvider scopeProvider, TextWriter textWriter) - { - string message = logEntry.Formatter(logEntry.State, logEntry.Exception); - if (logEntry.Exception == null && message == null) - { - return; - } - CustomLogicGoesHere(textWriter); - textWriter.WriteLine(message); - } - - private void CustomLogicGoesHere(TextWriter textWriter) - { - textWriter.Write(FormatterOptions.CustomPrefix); - } - } -} -``` +- Create a subclass of , this represents your custom formatter +- Register your custom formatter with + - + - -The above `CustomFormatter.Write` API dictates what text gets wrapped around each log message. A standard ConsoleFormatter, same as those provided in in the box like JsonConsoleFormatter, should be able to wrap around scopes, time stats and severity level of logs at least. Additionally they may be able to encode ANSI colors in the log messages or provide proper indentations as well. The above implementation for `CustomFormatter.Write` lacks these capabilities but is mainly to try out a hello world sample on custom console log formatting. To learn more about how to enable/customize scope/time/severity and optionally indentation/color capabilities take a deeper look at the implementation of the different console formatters available in the box in Microsoft.Extensions.Logging.Console, namely: [JsonConsoleFormatter](https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.cs), and [SimpleConsoleFormatter](https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.cs). +Create an extension method to handle this for you: -#### Custom Formatting with Color capabilities +:::code language="csharp" source="snippets/logging/console-formatter-custom/ConsoleLoggerExtensions.cs"::: -In order to properly enable color capabilities in your custom console logger, you may have its FormatterOptions derive from `SimpleConsoleFormatterOptions` as it has a `LoggerColorBehavior` property that can be useful for enabling colors in logs. +The `CustomOptions` are defined as follows: -In your working sample from previous section, first update CustomOptions to derive from SimpleConsoleFormatterOptions: +:::code language="csharp" source="snippets/logging/console-formatter-custom/CustomOptions.cs"::: -```csharp - public class CustomOptions : SimpleConsoleFormatterOptions - { - public string CustomPrefix { get; set; } - } -``` +In the proceeding code, the options are a subclass of . -Then update the CustomFormatter methods below: - -```csharp -// TODO: Add DisableColors: - private bool DisableColors => - FormatterOptions.ColorBehavior == LoggerColorBehavior.Disabled || - (FormatterOptions.ColorBehavior == LoggerColorBehavior.Default && System.Console.IsOutputRedirected); - - private void CustomLogicGoesHere(TextWriter textWriter) - { - if (DisableColors) - { - textWriter.Write(FormatterOptions.CustomPrefix); - } - else - { - textWriter.WriteWithColor(FormatterOptions.CustomPrefix, ConsoleColor.Black, ConsoleColor.Green); - } - } -``` +The `AddConsoleFormatter` API: -In order to remove the squiggly on `WriteColorMessage` add the below helper methods as well. These extension methods in `TextWriterExtensions` allow for conveniently embedding ANSI coded colors within formatted log messages: - -```csharp -using System; -using System.IO; - -namespace SampleConsoleApp -{ - public static class TextWriterExtensions - { - public static void WriteWithColor(this TextWriter textWriter, string message, ConsoleColor? background, ConsoleColor? foreground) - { - // Order: backgroundcolor, foregroundcolor, Message, reset foregroundcolor, reset backgroundcolor - if (background.HasValue) - { - textWriter.Write(GetBackgroundColorEscapeCode(background.Value)); - } - if (foreground.HasValue) - { - textWriter.Write(GetForegroundColorEscapeCode(foreground.Value)); - } - textWriter.WriteLine(message); - if (foreground.HasValue) - { - textWriter.Write(DefaultForegroundColor); // reset to default foreground color - } - if (background.HasValue) - { - textWriter.Write(DefaultBackgroundColor); // reset to the background color - } - } - internal const string DefaultForegroundColor = "\x1B[39m\x1B[22m"; // reset to default foreground color - internal const string DefaultBackgroundColor = "\x1B[49m"; // reset to the background color - internal static string GetForegroundColorEscapeCode(ConsoleColor color) - { - return color switch - { - ConsoleColor.Black => "\x1B[30m", - ConsoleColor.DarkRed => "\x1B[31m", - ConsoleColor.DarkGreen => "\x1B[32m", - ConsoleColor.DarkYellow => "\x1B[33m", - ConsoleColor.DarkBlue => "\x1B[34m", - ConsoleColor.DarkMagenta => "\x1B[35m", - ConsoleColor.DarkCyan => "\x1B[36m", - ConsoleColor.Gray => "\x1B[37m", - ConsoleColor.Red => "\x1B[1m\x1B[31m", - ConsoleColor.Green => "\x1B[1m\x1B[32m", - ConsoleColor.Yellow => "\x1B[1m\x1B[33m", - ConsoleColor.Blue => "\x1B[1m\x1B[34m", - ConsoleColor.Magenta => "\x1B[1m\x1B[35m", - ConsoleColor.Cyan => "\x1B[1m\x1B[36m", - ConsoleColor.White => "\x1B[1m\x1B[37m", - _ => DefaultForegroundColor // default foreground color - }; - } - - internal static string GetBackgroundColorEscapeCode(ConsoleColor color) - { - return color switch - { - ConsoleColor.Black => "\x1B[40m", - ConsoleColor.DarkRed => "\x1B[41m", - ConsoleColor.DarkGreen => "\x1B[42m", - ConsoleColor.DarkYellow => "\x1B[43m", - ConsoleColor.DarkBlue => "\x1B[44m", - ConsoleColor.DarkMagenta => "\x1B[45m", - ConsoleColor.DarkCyan => "\x1B[46m", - ConsoleColor.Gray => "\x1B[47m", - _ => DefaultBackgroundColor // Use default background color - }; - } - } -} -``` +- 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"::: + +The proceeding `CustomFormatter.Write` 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 proceeding implementation for `CustomFormatter.Write` lacks these capabilities. + +For inspiration on further customizing formatting, see the built-in 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 as it has a 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"::: + +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"::: -When you run your application again, the logs would show `CustomPrefix` message in green when FormatterOptions.ColorBehavior is not disabled. So for example, when configuring options in `AddCustomFormatter` below, when color behavior is set to disabled, the the same log prefix will no longer be shown in green. +When you run the application, the logs will show the `CustomPrefix` message in the color green when `FormatterOptions.ColorBehavior` is `Enabled`. diff --git a/docs/core/extensions/logging.md b/docs/core/extensions/logging.md index 24a28c7a33ab9..78b12af8ab135 100644 --- a/docs/core/extensions/logging.md +++ b/docs/core/extensions/logging.md @@ -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 diff --git a/docs/core/extensions/snippets/logging/console-formatter-custom/ConsoleLoggerExtensions.cs b/docs/core/extensions/snippets/logging/console-formatter-custom/ConsoleLoggerExtensions.cs index 256c04df889ba..e60193b3f2616 100644 --- a/docs/core/extensions/snippets/logging/console-formatter-custom/ConsoleLoggerExtensions.cs +++ b/docs/core/extensions/snippets/logging/console-formatter-custom/ConsoleLoggerExtensions.cs @@ -1,7 +1,7 @@ using Microsoft.Extensions.Logging; using System; -namespace Console.ExampleFormatters.Json +namespace Console.ExampleFormatters.Custom { public static class ConsoleLoggerExtensions { diff --git a/docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorFormatter.cs b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorFormatter.cs index c4a076d4fbdd4..b12e22cc21280 100644 --- a/docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorFormatter.cs +++ b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorFormatter.cs @@ -7,19 +7,23 @@ namespace Console.ExampleFormatters.Custom { - public class CustomColorFormatter : ConsoleFormatter, IDisposable + public sealed class CustomColorFormatter : ConsoleFormatter, IDisposable { private readonly IDisposable _optionsReloadToken; private CustomColorOptions _formatterOptions; - public CustomColorFormatter(IOptionsMonitor options) - : base("customName") // case insensitive - { - _optionsReloadToken = options.OnChange(ReloadLoggerOptions); - _formatterOptions = options.CurrentValue; - } + private bool ConsoleColorFormattingEnabled => + _formatterOptions.ColorBehavior == LoggerColorBehavior.Enabled || + _formatterOptions.ColorBehavior == LoggerColorBehavior.Default && + System.Console.IsOutputRedirected == false; - private void ReloadLoggerOptions(CustomOptions options) => + public CustomColorFormatter(IOptionsMonitor options) + // Case insensitive + : base("customName") => + (_optionsReloadToken, _formatterOptions) = + (options.OnChange(ReloadLoggerOptions), options.CurrentValue); + + private void ReloadLoggerOptions(CustomColorOptions options) => _formatterOptions = options; public override void Write( @@ -47,7 +51,17 @@ public override void Write( private void CustomLogicGoesHere(TextWriter textWriter) { - textWriter.Write(_formatterOptions.CustomPrefix); + if (ConsoleColorFormattingEnabled) + { + textWriter.WriteWithColor( + _formatterOptions.CustomPrefix, + ConsoleColor.Black, + ConsoleColor.Green); + } + else + { + textWriter.Write(_formatterOptions.CustomPrefix); + } } public void Dispose() => _optionsReloadToken?.Dispose(); diff --git a/docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorOptions.cs b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorOptions.cs index 1b2a48ef2b5e7..1f2213893f684 100644 --- a/docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorOptions.cs +++ b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomColorOptions.cs @@ -2,7 +2,7 @@ namespace Console.ExampleFormatters.Custom { - public class CustomOptions : SimpleConsoleFormatterOptions + public class CustomColorOptions : SimpleConsoleFormatterOptions { public string CustomPrefix { get; set; } } diff --git a/docs/core/extensions/snippets/logging/console-formatter-custom/CustomFormatter.cs b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomFormatter.cs index d612f29c436c4..f105838d4891d 100644 --- a/docs/core/extensions/snippets/logging/console-formatter-custom/CustomFormatter.cs +++ b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomFormatter.cs @@ -5,7 +5,7 @@ using System; using System.IO; -namespace Console.ExampleFormatters.Json +namespace Console.ExampleFormatters.Custom { public sealed class CustomFormatter : ConsoleFormatter, IDisposable { @@ -13,11 +13,10 @@ public sealed class CustomFormatter : ConsoleFormatter, IDisposable private CustomOptions _formatterOptions; public CustomFormatter(IOptionsMonitor options) - : base("customName") // case insensitive - { - _optionsReloadToken = options.OnChange(ReloadLoggerOptions); - _formatterOptions = options.CurrentValue; - } + // Case insensitive + : base("customName") => + (_optionsReloadToken, _formatterOptions) = + (options.OnChange(ReloadLoggerOptions), options.CurrentValue); private void ReloadLoggerOptions(CustomOptions options) => _formatterOptions = options; diff --git a/docs/core/extensions/snippets/logging/console-formatter-custom/CustomOptions.cs b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomOptions.cs index 1b55364b6a66c..33d491986d576 100644 --- a/docs/core/extensions/snippets/logging/console-formatter-custom/CustomOptions.cs +++ b/docs/core/extensions/snippets/logging/console-formatter-custom/CustomOptions.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Logging.Console; -namespace Console.ExampleFormatters.Json +namespace Console.ExampleFormatters.Custom { public class CustomOptions : ConsoleFormatterOptions { diff --git a/docs/core/extensions/snippets/logging/console-formatter-custom/Program.cs b/docs/core/extensions/snippets/logging/console-formatter-custom/Program.cs index 1be848cac5fcf..b0fef32abae49 100644 --- a/docs/core/extensions/snippets/logging/console-formatter-custom/Program.cs +++ b/docs/core/extensions/snippets/logging/console-formatter-custom/Program.cs @@ -1,20 +1,17 @@ -using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace Console.ExampleFormatters.Custom { class Program { - static void Main(string[] args) + static void Main() { - using var loggerFactory = LoggerFactory.Create(builder => - { - builder.AddCustomFormatter(options => - { - options.CustomPrefix = " ~~~~~ "; - }); - }); + using ILoggerFactory loggerFactory = + LoggerFactory.Create(builder => + builder.AddCustomFormatter(options => + options.CustomPrefix = " ~~~~~ ")); - var logger = loggerFactory.CreateLogger(); + ILogger logger = loggerFactory.CreateLogger(); using (logger.BeginScope("TODO: Add logic to enable scopes")) { logger.LogInformation("Hello World!"); diff --git a/docs/core/extensions/snippets/logging/console-formatter-json/Program.cs b/docs/core/extensions/snippets/logging/console-formatter-json/Program.cs index ab4072f8ac226..9d8de3e8e7385 100644 --- a/docs/core/extensions/snippets/logging/console-formatter-json/Program.cs +++ b/docs/core/extensions/snippets/logging/console-formatter-json/Program.cs @@ -1,5 +1,8 @@ -using System.Threading.Tasks; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace Console.ExampleFormatters.Json { @@ -18,7 +21,6 @@ static IHostBuilder CreateHostBuilder(string[] args) => options.TimestampFormat = "hh:mm:ss "; options.JsonWriterOptions = new JsonWriterOptions { - Encoder = JavaScriptTestEncoder.UnsafeRelaxedJsonEscaping, Indented = true }; })); diff --git a/docs/core/extensions/snippets/logging/console-formatter-json/Properties/launchSettings.json b/docs/core/extensions/snippets/logging/console-formatter-json/Properties/launchSettings.json new file mode 100644 index 0000000000000..bf164a203e5ac --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-json/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:59449/", + "sslPort": 44370 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "console-formatter-json": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/docs/core/extensions/snippets/logging/console-formatter-json/Startup.cs b/docs/core/extensions/snippets/logging/console-formatter-json/Startup.cs new file mode 100644 index 0000000000000..889116603ead1 --- /dev/null +++ b/docs/core/extensions/snippets/logging/console-formatter-json/Startup.cs @@ -0,0 +1,52 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Console.ExampleFormatters.Json +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllersWithViews(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseExceptionHandler("/Home/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); + } + app.UseHttpsRedirection(); + app.UseStaticFiles(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); + }); + } + } +} diff --git a/docs/core/extensions/snippets/logging/console-formatter-json/console-formatter-json.csproj b/docs/core/extensions/snippets/logging/console-formatter-json/console-formatter-json.csproj index 958aecf2896ed..57947a23960d8 100644 --- a/docs/core/extensions/snippets/logging/console-formatter-json/console-formatter-json.csproj +++ b/docs/core/extensions/snippets/logging/console-formatter-json/console-formatter-json.csproj @@ -1,4 +1,4 @@ - + Exe diff --git a/docs/core/extensions/snippets/logging/console-formatter-simple/Program.cs b/docs/core/extensions/snippets/logging/console-formatter-simple/Program.cs index 8351aa675d09a..c03595cbaa47f 100644 --- a/docs/core/extensions/snippets/logging/console-formatter-simple/Program.cs +++ b/docs/core/extensions/snippets/logging/console-formatter-simple/Program.cs @@ -1,22 +1,21 @@ -using Microsoft.Extensions.Logging.Console; +using Microsoft.Extensions.Logging; namespace Console.ExampleFormatters.Simple { class Program { - static void Main(string[] args) + static void Main() { - using var loggerFactory = LoggerFactory.Create(builder => - { - builder.AddSimpleConsole(o => - { - o.IncludeScopes = true; - o.SingleLine = true; - o.TimestampFormat = "hh:mm:ss "; - }); - }); + using ILoggerFactory loggerFactory = + LoggerFactory.Create(builder => + builder.AddSimpleConsole(options => + { + options.IncludeScopes = true; + options.SingleLine = true; + options.TimestampFormat = "hh:mm:ss "; + })); - var logger = loggerFactory.CreateLogger(); + ILogger logger = loggerFactory.CreateLogger(); using (logger.BeginScope("[scope is enabled]")) { logger.LogInformation("Hello World!"); diff --git a/docs/core/extensions/snippets/logging/console-formatter-systemd/Program.cs b/docs/core/extensions/snippets/logging/console-formatter-systemd/Program.cs index ca83510d55311..7d29bf39e4775 100644 --- a/docs/core/extensions/snippets/logging/console-formatter-systemd/Program.cs +++ b/docs/core/extensions/snippets/logging/console-formatter-systemd/Program.cs @@ -1,20 +1,20 @@ -using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace Console.ExampleFormatters.Systemd { class Program { - static void Main(string[] args) + static void Main() { - using var loggerFactory = LoggerFactory.Create(builder => - { - builder.AddSystemdConsole(o => { - o.IncludeScopes = true; - o.TimestampFormat = "hh:mm:ss "; - }); - }); + using ILoggerFactory loggerFactory = + LoggerFactory.Create(builder => + builder.AddSystemdConsole(options => + { + options.IncludeScopes = true; + options.TimestampFormat = "hh:mm:ss "; + })); - var logger = loggerFactory.CreateLogger(); + ILogger logger = loggerFactory.CreateLogger(); using (logger.BeginScope("[scope is enabled]")) { logger.LogInformation("Hello World!"); diff --git a/docs/fundamentals/toc.yml b/docs/fundamentals/toc.yml index aab7b63279ec4..3cb5e247fe89f 100644 --- a/docs/fundamentals/toc.yml +++ b/docs/fundamentals/toc.yml @@ -1558,6 +1558,8 @@ items: href: ../core/extensions/custom-logging-provider.md - name: High-performance logging href: ../core/extensions/high-performance-logging.md + - name: Console log formatting + href: ../core/extensions/console-log-formatter.md - name: HostBuilder (generic host) href: ../core/extensions/generic-host.md - name: Data access From 67860236a593552dd9f3b287451ba2a1b571446b Mon Sep 17 00:00:00 2001 From: David Pine Date: Thu, 22 Oct 2020 12:18:49 -0500 Subject: [PATCH 04/11] Added highlighting --- docs/core/extensions/console-log-formatter.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/core/extensions/console-log-formatter.md b/docs/core/extensions/console-log-formatter.md index 50763da9cffca..3eb088db5609a 100644 --- a/docs/core/extensions/console-log-formatter.md +++ b/docs/core/extensions/console-log-formatter.md @@ -34,11 +34,11 @@ The [`Console` logging provider](logging-providers.md#console) has built-in form ### Simple -To register, and use the simple console formatter: +To use the `Simple` console formatter, register it with `AddSimpleConsole`: -:::code language="csharp" source="snippets/logging/console-formatter-simple/Program.cs"::: +:::code language="csharp" source="snippets/logging/console-formatter-simple/Program.cs" highlight="11-16"::: -In the proceeding sample source code, the 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. +In the proceeding sample source code, the 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 @@ -50,14 +50,14 @@ The : @@ -101,7 +101,7 @@ To implement a custom formatter, you need to: Create an extension method to handle this for you: -:::code language="csharp" source="snippets/logging/console-formatter-custom/ConsoleLoggerExtensions.cs"::: +:::code language="csharp" source="snippets/logging/console-formatter-custom/ConsoleLoggerExtensions.cs" highlight="11-12"::: The `CustomOptions` are defined as follows: @@ -119,9 +119,9 @@ The `AddConsoleFormatter` API: Define a `CustomerFormatter` subclass of `ConsoleFormatter`: -:::code language="csharp" source="snippets/logging/console-formatter-custom/CustomFormatter.cs"::: +:::code language="csharp" source="snippets/logging/console-formatter-custom/CustomFormatter.cs" highlight="24-45"::: -The proceeding `CustomFormatter.Write` 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 proceeding implementation for `CustomFormatter.Write` lacks these capabilities. +The proceeding `CustomFormatter.Write` 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 proceeding implementation for `CustomFormatter.Write` lacks these capabilities. For inspiration on further customizing formatting, see the built-in implementations in the `Microsoft.Extensions.Logging.Console` namespace: @@ -135,7 +135,7 @@ In order to properly enable color capabilities in your custom logging formatter, Create a `CustomColorOptions` that derives from `SimpleConsoleFormatterOptions`: -:::code language="csharp" source="snippets/logging/console-formatter-custom/CustomColorOptions.cs"::: +:::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: @@ -143,6 +143,6 @@ Next, write some extension methods in a `TextWriterExtensions` class that allow 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"::: +:::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`. From d29762afaafe608f979f6850e0a45025a7d5a3f9 Mon Sep 17 00:00:00 2001 From: David Pine Date: Thu, 22 Oct 2020 12:22:18 -0500 Subject: [PATCH 05/11] Acronlinx pass --- docs/core/extensions/console-log-formatter.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/core/extensions/console-log-formatter.md b/docs/core/extensions/console-log-formatter.md index 3eb088db5609a..8ac0109cb8dd3 100644 --- a/docs/core/extensions/console-log-formatter.md +++ b/docs/core/extensions/console-log-formatter.md @@ -1,6 +1,6 @@ --- title: Console log formatting -description: Learn how to use built-in console log formatting, or implement custom log formatting for your .NET applications. +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 @@ -8,12 +8,12 @@ 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 built-in formatting options available: [`Simple`](#simple), [`Systemd`](#systemd), and [`Json`](#json). +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 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 demonstrate how to: +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 @@ -24,9 +24,9 @@ In this article you will learn about console log formatters. The sample source c ## Register formatter -The [`Console` logging provider](logging-providers.md#console) has built-in formatters, and exposes the ability to author your own custom formatter. To register any of the built-in formatters, use the corresponding `Add{Type}Console` extension method: +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: -| Built-in types | Method to register type | +| Available types | Method to register type | |--|--| | | | | | | @@ -54,7 +54,7 @@ This is commonly useful for containers, which often make use of `Systemd` consol ### Json -To write logs in a JSON format, the built-in `Json` console formatter is used. The sample source code shows how an ASP.NET Core app might register it. For example using template ASP.NET Core application: +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. For example using template ASP.NET Core application: ```dotnetcli dotnet new webapp -o Console.ExampleFormatters.Json @@ -84,7 +84,7 @@ The previous samples have shown how to register a formatter programmatically. Al :::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 built-in formatter names are reserved under : +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 : - - @@ -123,7 +123,7 @@ Define a `CustomerFormatter` subclass of `ConsoleFormatter`: The proceeding `CustomFormatter.Write` 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 proceeding implementation for `CustomFormatter.Write` lacks these capabilities. -For inspiration on further customizing formatting, see the built-in implementations in the `Microsoft.Extensions.Logging.Console` namespace: +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) From 8a03911f121e254b36f39f302b8069322c4f9ea0 Mon Sep 17 00:00:00 2001 From: David Pine Date: Thu, 22 Oct 2020 12:22:42 -0500 Subject: [PATCH 06/11] Minor updates, looking good --- docs/core/extensions/console-log-formatter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/core/extensions/console-log-formatter.md b/docs/core/extensions/console-log-formatter.md index 8ac0109cb8dd3..103b0b5ce5ccb 100644 --- a/docs/core/extensions/console-log-formatter.md +++ b/docs/core/extensions/console-log-formatter.md @@ -54,7 +54,7 @@ This is commonly useful for containers, which often make use of `Systemd` consol ### 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. For example using template ASP.NET Core application: +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. For example, using template ASP.NET Core application: ```dotnetcli dotnet new webapp -o Console.ExampleFormatters.Json From e4dfab11700c9d58891bda1a32c84e2829a2b7e1 Mon Sep 17 00:00:00 2001 From: David Pine Date: Thu, 22 Oct 2020 12:24:14 -0500 Subject: [PATCH 07/11] Added link to dotnet new --- docs/core/extensions/console-log-formatter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/core/extensions/console-log-formatter.md b/docs/core/extensions/console-log-formatter.md index 103b0b5ce5ccb..43f8825a18f3e 100644 --- a/docs/core/extensions/console-log-formatter.md +++ b/docs/core/extensions/console-log-formatter.md @@ -54,7 +54,7 @@ This is commonly useful for containers, which often make use of `Systemd` consol ### 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. For example, using template ASP.NET Core application: +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 From 0287c6a1ed61f11e525afb9bf29825e4e4587fc6 Mon Sep 17 00:00:00 2001 From: David Pine Date: Thu, 22 Oct 2020 12:30:57 -0500 Subject: [PATCH 08/11] Added see also section --- docs/core/extensions/console-log-formatter.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/core/extensions/console-log-formatter.md b/docs/core/extensions/console-log-formatter.md index 43f8825a18f3e..9d24fbefc0301 100644 --- a/docs/core/extensions/console-log-formatter.md +++ b/docs/core/extensions/console-log-formatter.md @@ -146,3 +146,9 @@ A custom color formatter that handles applying custom colors could be defined as :::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) From 60519efe55dc9941cf32f1efcc43a1839cafb635 Mon Sep 17 00:00:00 2001 From: David Pine Date: Thu, 22 Oct 2020 12:45:16 -0500 Subject: [PATCH 09/11] Fix heading depths --- docs/core/extensions/console-log-formatter.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/core/extensions/console-log-formatter.md b/docs/core/extensions/console-log-formatter.md index 9d24fbefc0301..11444918a5888 100644 --- a/docs/core/extensions/console-log-formatter.md +++ b/docs/core/extensions/console-log-formatter.md @@ -78,7 +78,7 @@ Run the app again, with the above change, the log message is now formatted as JS > [!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 to `true`. -### Set formatter with configuration +## 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: @@ -90,7 +90,7 @@ The two key values that need to be set are `"FormatterName"` and `"FormatterOpti - - -### Implement a custom formatter +## Implement a custom formatter To implement a custom formatter, you need to: @@ -129,7 +129,7 @@ For inspiration on further customizing formatting, see the existing implementati - [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 +## Implement custom color formatting In order to properly enable color capabilities in your custom logging formatter, you can extend the as it has a property that can be useful for enabling colors in logs. From 70e80f8cb65517cd2ccad16e8f69320a567a3ab3 Mon Sep 17 00:00:00 2001 From: David Pine Date: Thu, 22 Oct 2020 12:53:47 -0500 Subject: [PATCH 10/11] Added qualifying type for methods in table. --- docs/core/extensions/console-log-formatter.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/core/extensions/console-log-formatter.md b/docs/core/extensions/console-log-formatter.md index 11444918a5888..f346ddf13b82e 100644 --- a/docs/core/extensions/console-log-formatter.md +++ b/docs/core/extensions/console-log-formatter.md @@ -28,9 +28,9 @@ The [`Console` logging provider](logging-providers.md#console) has several prede | Available types | Method to register type | |--|--| -| | | -| | | -| | | +| | | +| | | +| | | ### Simple From 9858edc930205ac76f7e863f79c8d16c5520a9aa Mon Sep 17 00:00:00 2001 From: David Pine Date: Thu, 22 Oct 2020 19:38:09 -0500 Subject: [PATCH 11/11] Apply suggestions from code review Fix occurrences of proceeding :facepalm: --- docs/core/extensions/console-log-formatter.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/core/extensions/console-log-formatter.md b/docs/core/extensions/console-log-formatter.md index f346ddf13b82e..bed1aa4804a5f 100644 --- a/docs/core/extensions/console-log-formatter.md +++ b/docs/core/extensions/console-log-formatter.md @@ -38,7 +38,7 @@ 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 proceeding sample source code, the 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. +In the preceding sample source code, the 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 @@ -107,7 +107,7 @@ The `CustomOptions` are defined as follows: :::code language="csharp" source="snippets/logging/console-formatter-custom/CustomOptions.cs"::: -In the proceeding code, the options are a subclass of . +In the preceding code, the options are a subclass of . The `AddConsoleFormatter` API: @@ -121,7 +121,7 @@ Define a `CustomerFormatter` subclass of `ConsoleFormatter`: :::code language="csharp" source="snippets/logging/console-formatter-custom/CustomFormatter.cs" highlight="24-45"::: -The proceeding `CustomFormatter.Write` 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 proceeding implementation for `CustomFormatter.Write` lacks these capabilities. +The preceding `CustomFormatter.Write` 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` lacks these capabilities. For inspiration on further customizing formatting, see the existing implementations in the `Microsoft.Extensions.Logging.Console` namespace: