Movatterモバイル変換


[0]ホーム

URL:


Skip to main content

This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Download Microsoft EdgeMore info about Internet Explorer and Microsoft Edge
Table of contentsExit editor mode

Console log formatting

Feedback

In this article

In .NET 5, support for custom formatting was added to console logs in theMicrosoft.Extensions.Logging.Console namespace. There are three predefined formatting options available:Simple,Systemd, andJson.

Important

Previously, theConsoleLoggerFormat enum allowed for selecting the desired log format, either human readable which was theDefault, or single line which is also known asSystemd. However, these werenot customizable, and are now deprecated.

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

  • Register a new formatter
  • Select a registered formatter to use
  • Implement a custom formatter

Tip

All of the logging example source code is available in theSamples Browser for download. For more information, seeBrowse code samples: Logging in .NET.

Register formatter

TheConsole logging provider has several predefined formatters, and exposes the ability to author your own custom formatter. To register any of the available formatters, use the correspondingAdd{Type}Console extension method:

Available typesMethod to register type
ConsoleFormatterNames.JsonConsoleLoggerExtensions.AddJsonConsole
ConsoleFormatterNames.SimpleConsoleLoggerExtensions.AddSimpleConsole
ConsoleFormatterNames.SystemdConsoleLoggerExtensions.AddSystemdConsole

Simple

To use theSimple console formatter, register it withAddSimpleConsole:

using Microsoft.Extensions.Logging;using ILoggerFactory loggerFactory =    LoggerFactory.Create(builder =>        builder.AddSimpleConsole(options =>        {            options.IncludeScopes = true;            options.SingleLine = true;            options.TimestampFormat = "HH:mm:ss ";        }));ILogger<Program> logger = loggerFactory.CreateLogger<Program>();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.");}

In the preceding sample source code, theConsoleFormatterNames.Simple 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.

When this sample app is run, the log messages are formatted as shown below:

Example console logs written with the simple formatter.

Systemd

TheConsoleFormatterNames.Systemd console logger:

  • Uses the "Syslog" log level format and severities
  • Doesnot format messages with colors
  • Always logs messages in a single line

This is commonly useful for containers, which often make use ofSystemd console logging. With .NET 5, theSimple 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.

using Microsoft.Extensions.Logging;using ILoggerFactory loggerFactory =    LoggerFactory.Create(builder =>        builder.AddSystemdConsole(options =>        {            options.IncludeScopes = true;            options.TimestampFormat = "HH:mm:ss ";        }));ILogger<Program> logger = loggerFactory.CreateLogger<Program>();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.");}

The example produces output similar to the following log messages:

Example console logs written with the Systemd formatter.

Json

To write logs in a JSON format, theJson console formatter is used. The sample source code shows how an ASP.NET Core app might register it. Using thewebapp template, create a new ASP.NET Core app with thedotnet new command:

dotnet new webapp -o Console.ExampleFormatters.Json

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

info: Console.ExampleFormatters.Json.Startup[0]      Hello .NET friends!info: Microsoft.Hosting.Lifetime[14]      Now listening on: https://localhost:5001info: Microsoft.Hosting.Lifetime[14]      Now listening on: http://localhost:5000info: Microsoft.Hosting.Lifetime[0]      Application started. Press Ctrl+C to shut down.info: Microsoft.Hosting.Lifetime[0]      Hosting environment: Developmentinfo: Microsoft.Hosting.Lifetime[0]      Content root path: .\snippets\logging\console-formatter-json

By default, theSimple console log formatter is selected with default configuration. You change this by callingAddJsonConsole in theProgram.cs:

using System.Text.Json;HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);builder.Logging.AddJsonConsole(options =>{    options.IncludeScopes = false;    options.TimestampFormat = "HH:mm:ss ";    options.JsonWriterOptions = new JsonWriterOptions    {        Indented = true    };});using IHost host = builder.Build();var logger =    host.Services        .GetRequiredService<ILoggerFactory>()        .CreateLogger<Program>();logger.LogInformation("Hello .NET friends!");await host.RunAsync();

Alternatively, you can also configure this using logging configuration, such as that found in theappsettings.json file:

{    "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:

{  "Timestamp": "02:28:19 ",  "EventId": 0,  "LogLevel": "Information",  "Category": "Console.ExampleFormatters.Json.Startup",  "Message": "Hello .NET friends!",  "State": {    "Message": "Hello .NET friends!",    "{OriginalFormat}": "Hello .NET friends!"  }}{  "Timestamp": "02:28:21 ",  "EventId": 14,  "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}"  }}{  "Timestamp": "02:28:21 ",  "EventId": 14,  "LogLevel": "Information",  "Category": "Microsoft.Hosting.Lifetime",  "Message": "Now listening on: http://localhost:5000",  "State": {    "Message": "Now listening on: http://localhost:5000",    "address": "http://localhost:5000",    "{OriginalFormat}": "Now listening on: {address}"  }}{  "Timestamp": "02:28:21 ",  "EventId": 0,  "LogLevel": "Information",  "Category": "Microsoft.Hosting.Lifetime",  "Message": "Application started. Press Ctrl\u002BC to shut down.",  "State": {    "Message": "Application started. Press Ctrl\u002BC to shut down.",    "{OriginalFormat}": "Application started. Press Ctrl\u002BC to shut down."  }}{  "Timestamp": "02:28:21 ",  "EventId": 0,  "LogLevel": "Information",  "Category": "Microsoft.Hosting.Lifetime",  "Message": "Hosting environment: Development",  "State": {    "Message": "Hosting environment: Development",    "envName": "Development",    "{OriginalFormat}": "Hosting environment: {envName}"  }}{  "Timestamp": "02:28:21 ",  "EventId": 0,  "LogLevel": "Information",  "Category": "Microsoft.Hosting.Lifetime",  "Message": "Content root path: .\\snippets\\logging\\console-formatter-json",  "State": {    "Message": "Content root path: .\\snippets\\logging\\console-formatter-json",    "contentRoot": ".\\snippets\\logging\\console-formatter-json",    "{OriginalFormat}": "Content root path: {contentRoot}"  }}

Tip

TheJson console formatter, by default, logs each message in a single line. In order to make it more readable while configuring the formatter, setJsonWriterOptions.Indented totrue.

Caution

When using the Json console formatter, do not pass in log messages that have already been serialized as JSON. The logging infrastructure itself already manages the serialization of log messages, so if you're to pass in a log message that is already serialized—it will be double serialized, thus causing malformed output.

Set formatter with configuration

The previous samples have shown how to register a formatter programmatically. Alternatively, this can be done withconfiguration. Consider the previous web application sample source code, if you update theappsettings.json file rather than callingConfigureLogging in theProgram.cs file, you could get the same outcome. The updatedappsettings.json file would configure the formatter as follows:

{    "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 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 underConsoleFormatterNames:

Implement a custom formatter

To implement a custom formatter, you need to:

Create an extension method to handle this for you:

using Microsoft.Extensions.Logging;namespace Console.ExampleFormatters.Custom;public static class ConsoleLoggerExtensions{    public static ILoggingBuilder AddCustomFormatter(        this ILoggingBuilder builder,        Action<CustomOptions> configure) =>        builder.AddConsole(options => options.FormatterName = "customName")            .AddConsoleFormatter<CustomFormatter, CustomOptions>(configure);}

TheCustomOptions are defined as follows:

using Microsoft.Extensions.Logging.Console;namespace Console.ExampleFormatters.Custom;public sealed class CustomOptions : ConsoleFormatterOptions{    public string? CustomPrefix { get; set; }}

In the preceding code, the options are a subclass ofConsoleFormatterOptions.

TheAddConsoleFormatter API:

  • Registers a subclass ofConsoleFormatter
  • Handles configuration:
using Console.ExampleFormatters.Custom;using Microsoft.Extensions.Logging;using ILoggerFactory loggerFactory =    LoggerFactory.Create(builder =>        builder.AddCustomFormatter(options =>            options.CustomPrefix = " ~~~~~ "));ILogger<Program> logger = loggerFactory.CreateLogger<Program>();using (logger.BeginScope("TODO: Add logic to enable scopes")){    logger.LogInformation("Hello World!");    logger.LogInformation("TODO: Add logic to enable timestamp and log level info.");}

Define aCustomFormatter subclass ofConsoleFormatter:

using Microsoft.Extensions.Logging;using Microsoft.Extensions.Logging.Abstractions;using Microsoft.Extensions.Logging.Console;using Microsoft.Extensions.Options;namespace Console.ExampleFormatters.Custom;public sealed class CustomFormatter : ConsoleFormatter, IDisposable{    private readonly IDisposable? _optionsReloadToken;    private CustomOptions _formatterOptions;    public CustomFormatter(IOptionsMonitor<CustomOptions> options)        // Case insensitive        : base("customName") =>        (_optionsReloadToken, _formatterOptions) =            (options.OnChange(ReloadLoggerOptions), options.CurrentValue);    private void ReloadLoggerOptions(CustomOptions options) =>        _formatterOptions = options;    public override void Write<TState>(        in LogEntry<TState> logEntry,        IExternalScopeProvider? scopeProvider,        TextWriter textWriter)    {        string? message =            logEntry.Formatter?.Invoke(                logEntry.State, logEntry.Exception);        if (message is null)        {            return;        }        CustomLogicGoesHere(textWriter);        textWriter.WriteLine(message);    }    private void CustomLogicGoesHere(TextWriter textWriter)    {        textWriter.Write(_formatterOptions.CustomPrefix);    }    public void Dispose() => _optionsReloadToken?.Dispose();}

The precedingCustomFormatter.Write<TState> API dictates what text gets wrapped around each log message. A standardConsoleFormatter 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 theCustomFormatter.Write<TState> lacks these capabilities.

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

Custom configuration options

To further customize the logging extensibility, your derivedConsoleFormatterOptions class can be configured from anyconfiguration provider. For example, you could use theJSON configuration provider to define your custom options. First define yourConsoleFormatterOptions subclass.

using Microsoft.Extensions.Logging.Console;namespace Console.ExampleFormatters.CustomWithConfig;public sealed class CustomWrappingConsoleFormatterOptions : ConsoleFormatterOptions{    public string? CustomPrefix { get; set; }    public string? CustomSuffix { get; set; }}

The preceding console formatter options class defines two custom properties, representing a prefix and suffix. Next, define theappsettings.json file that will configure your console formatter options.

{    "Logging": {        "LogLevel": {            "Default": "Information",            "Microsoft": "Warning",            "Microsoft.Hosting.Lifetime": "Information"        },        "Console": {            "LogLevel": {                "Default": "Information",                "Microsoft": "Warning",                "Microsoft.Hosting.Lifetime": "Information"            },            "FormatterName": "CustomTimePrefixingFormatter",            "FormatterOptions": {                "CustomPrefix": "|-<[",                "CustomSuffix": "]>-|",                "SingleLine": true,                "IncludeScopes": true,                "TimestampFormat": "HH:mm:ss.ffff ",                "UseUtcTimestamp": true,                "JsonWriterOptions": {                    "Indented": true                }            }        }    },    "AllowedHosts": "*"}

In the preceding JSON config file:

  • The"Logging" node defines a"Console".
  • The"Console" node specifies a"FormatterName" of"CustomTimePrefixingFormatter", which maps to a custom formatter.
  • The"FormatterOptions" node defines a"CustomPrefix", and"CustomSuffix", as well as a few other derived options.

Tip

The$.Logging.Console.FormatterOptions JSON path is reserved, and will map to a customConsoleFormatterOptions when added using theAddConsoleFormatter extension method. This provides the ability to define custom properties, in addition to the ones available.

Consider the followingCustomDatePrefixingFormatter:

using Microsoft.Extensions.Logging;using Microsoft.Extensions.Logging.Abstractions;using Microsoft.Extensions.Logging.Console;using Microsoft.Extensions.Options;namespace Console.ExampleFormatters.CustomWithConfig;public sealed class CustomTimePrefixingFormatter : ConsoleFormatter, IDisposable{    private readonly IDisposable? _optionsReloadToken;    private CustomWrappingConsoleFormatterOptions _formatterOptions;    public CustomTimePrefixingFormatter(        IOptionsMonitor<CustomWrappingConsoleFormatterOptions> options)        // Case insensitive        : base(nameof(CustomTimePrefixingFormatter))    {        _optionsReloadToken = options.OnChange(ReloadLoggerOptions);        _formatterOptions = options.CurrentValue;    }    private void ReloadLoggerOptions(CustomWrappingConsoleFormatterOptions options) =>        _formatterOptions = options;    public override void Write<TState>(        in LogEntry<TState> logEntry,        IExternalScopeProvider? scopeProvider,        TextWriter textWriter)    {        string message =            logEntry.Formatter(                logEntry.State, logEntry.Exception);        if (message == null)        {            return;        }        WritePrefix(textWriter);        textWriter.Write(message);        WriteSuffix(textWriter);    }    private void WritePrefix(TextWriter textWriter)    {        DateTime now = _formatterOptions.UseUtcTimestamp            ? DateTime.UtcNow            : DateTime.Now;        textWriter.Write($"""            {_formatterOptions.CustomPrefix} {now.ToString(_formatterOptions.TimestampFormat)}            """);    }    private void WriteSuffix(TextWriter textWriter) =>        textWriter.WriteLine($" {_formatterOptions.CustomSuffix}");    public void Dispose() => _optionsReloadToken?.Dispose();}

In the preceding formatter implementation:

To use custom configuration options, with custom formatter implementations, add when callingConfigureLogging(IHostBuilder, Action<HostBuilderContext,ILoggingBuilder>).

using Console.ExampleFormatters.CustomWithConfig;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Hosting;using Microsoft.Extensions.Logging;HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);builder.Logging.AddConsole()    .AddConsoleFormatter<        CustomTimePrefixingFormatter, CustomWrappingConsoleFormatterOptions>();using IHost host = builder.Build();ILoggerFactory loggerFactory = host.Services.GetRequiredService<ILoggerFactory>();ILogger<Program> logger = loggerFactory.CreateLogger<Program>();using (logger.BeginScope("Logging scope")){    logger.LogInformation("Hello World!");    logger.LogInformation("The .NET developer community happily welcomes you.");}

The following console output is similar to what you might expect to see from using thisCustomTimePrefixingFormatter.

|-<[ 15:03:15.6179 Hello World! ]>-||-<[ 15:03:15.6347 The .NET developer community happily welcomes you. ]>-|

Implement custom color formatting

In order to properly enable color capabilities in your custom logging formatter, you can extend theSimpleConsoleFormatterOptions as it has aSimpleConsoleFormatterOptions.ColorBehavior property that can be useful for enabling colors in logs.

Create aCustomColorOptions that derives fromSimpleConsoleFormatterOptions:

using Microsoft.Extensions.Logging.Console;namespace Console.ExampleFormatters.Custom;public class CustomColorOptions : SimpleConsoleFormatterOptions{    public string? CustomPrefix { get; set; }}

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

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        var backgroundColor = background.HasValue ? GetBackgroundColorEscapeCode(background.Value) : null;        var foregroundColor = foreground.HasValue ? GetForegroundColorEscapeCode(foreground.Value) : null;        if (backgroundColor != null)        {            textWriter.Write(backgroundColor);        }        if (foregroundColor != null)        {            textWriter.Write(foregroundColor);        }        textWriter.WriteLine(message);        if (foregroundColor != null)        {            textWriter.Write(DefaultForegroundColor);        }        if (backgroundColor != null)        {            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        };}

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

using Microsoft.Extensions.Logging;using Microsoft.Extensions.Logging.Abstractions;using Microsoft.Extensions.Logging.Console;using Microsoft.Extensions.Options;namespace Console.ExampleFormatters.Custom;public sealed class CustomColorFormatter : ConsoleFormatter, IDisposable{    private readonly IDisposable? _optionsReloadToken;    private CustomColorOptions _formatterOptions;    private bool ConsoleColorFormattingEnabled =>        _formatterOptions.ColorBehavior == LoggerColorBehavior.Enabled ||        _formatterOptions.ColorBehavior == LoggerColorBehavior.Default &&        System.Console.IsOutputRedirected == false;    public CustomColorFormatter(IOptionsMonitor<CustomColorOptions> options)        // Case insensitive        : base("customName") =>        (_optionsReloadToken, _formatterOptions) =            (options.OnChange(ReloadLoggerOptions), options.CurrentValue);    private void ReloadLoggerOptions(CustomColorOptions options) =>        _formatterOptions = options;    public override void Write<TState>(        in LogEntry<TState> logEntry,        IExternalScopeProvider? scopeProvider,        TextWriter textWriter)    {        if (logEntry.Exception is null)        {            return;        }        string? message =            logEntry.Formatter?.Invoke(                logEntry.State, logEntry.Exception);        if (message is null)        {            return;        }        CustomLogicGoesHere(textWriter);        textWriter.WriteLine(message);    }    private void CustomLogicGoesHere(TextWriter textWriter)    {        if (ConsoleColorFormattingEnabled)        {            textWriter.WriteWithColor(                _formatterOptions.CustomPrefix ?? string.Empty,                ConsoleColor.Black,                ConsoleColor.Green);        }        else        {            textWriter.Write(_formatterOptions.CustomPrefix);        }    }    public void Dispose() => _optionsReloadToken?.Dispose();}

When you run the application, the logs will show theCustomPrefix message in the color green whenFormatterOptions.ColorBehavior isEnabled.

Note

WhenLoggerColorBehavior isDisabled, log messages donot interpret embedded ANSI color codes within log messages. Instead, they output the raw message. For example, consider the following:

logger.LogInformation("Random log \x1B[42mwith green background\x1B[49m message");

This would output the verbatim string, and it isnot colorized.

Random log \x1B[42mwith green background\x1B[49m message

See also

Collaborate with us on GitHub
The source for this content can be found on GitHub, where you can also create and review issues and pull requests. For more information, seeour contributor guide.

Feedback

Was this page helpful?

YesNoNo

Need help with this topic?

Want to try using Ask Learn to clarify or guide you through this topic?

Suggest a fix?

  • Last updated on

In this article

Was this page helpful?

YesNo
NoNeed help with this topic?

Want to try using Ask Learn to clarify or guide you through this topic?

Suggest a fix?