Formattazione del log della console

In .NET 5 il supporto alla formattazione personalizzata è stato aggiunto ai log della console nello spazio dei nomi Microsoft.Extensions.Logging.Console. Sono disponibili tre opzioni di formattazione predefinite: Simple, Systemd e Json.

Importante

In precedenza, l'enumerazione ConsoleLoggerFormat consentiva di selezionare il formato di log desiderato, sia leggibile dall’uomo, ovvero Default, sia a riga singola, noto anche come Systemd. Tuttavia, questi non erano personalizzabili e ora sono deprecati.

In questo articolo imparerai a conoscere i formattatori di log della console. Il codice sorgente di esempio illustra come:

  • Registrare un nuovo formattatore
  • Selezionare un formattatore registrato da usare
  • Implementare un formattatore personalizzato

Suggerimento

Tutto il codice sorgente di esempio di registrazione è disponibile nel browser Samples per il download. Per altre informazioni, vedere Esplorare gli esempi di codice: Registrazione in .NET.

Registrare il formattatore

Il Console provider di registrazione ha diversi formattatori predefiniti e offre la possibilità di creare un formattatore personalizzato. Per registrare uno dei formattatori disponibili, usa il metodo di estensione Add{Type}Console corrispondente:

Tipi disponibili Metodo per registrare il tipo
ConsoleFormatterNames.Json ConsoleLoggerExtensions.AddJsonConsole
ConsoleFormatterNames.Simple ConsoleLoggerExtensions.AddSimpleConsole
ConsoleFormatterNames.Systemd ConsoleLoggerExtensions.AddSystemdConsole

Semplice

Per usare il formattatore della console Simple, registralo con AddSimpleConsole:

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.");
}

Nel precedente codice sorgente di esempio il formattatore ConsoleFormatterNames.Simple è stato registrato. Offre ai log non solo la possibilità di eseguire il wrapping delle informazioni, ad esempio il livello di ora e di log in ogni messaggio di log, ma consente anche l’incorporamento dei colori ANSI e il rientro.

Quando viene eseguita questa app di esempio, i messaggi di log vengono formattati come illustrato di seguito:

Log della console di esempio scritti con il formattatore semplice.

Systemd

Il logger della console ConsoleFormatterNames.Systemd:

  • Usa il formato e la gravità del livello di log "Syslog"
  • Non formatta i messaggi con colori
  • Registra sempre i messaggi in una singola riga

Ciò è comunemente utile per i contenitori, che spesso usano la registrazione della console Systemd. Con .NET 5, il logger della console Simple abilita anche una versione compatta che registra in una singola riga e consente inoltre di disabilitare i colori, come illustrato in un esempio precedente.

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.");
}

L'esempio genera un output simile ai messaggi di log seguenti:

Log della console di esempio scritti con il formattatore Systemd.

Json

Per scrivere i log in un formato JSON, viene usato il formattatore della console Json. Il codice sorgente di esempio mostra come un'app ASP.NET Core potrebbe registrarlo. Usando il modello webapp, crea una nuova app ASP.NET Core con il comando dotnet new:

dotnet new webapp -o Console.ExampleFormatters.Json

Quando si esegue l'app, usando il codice modello, si ottiene il formato di log predefinito seguente:

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

Per impostazione predefinita, il formattatore di log della console Simple è selezionato con la configurazione predefinita. Puoi modificarlo chiamando AddJsonConsole nel Program.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();

In alternativa, puoi configurarlo anche usando la configurazione di registrazione, ad esempio quella presente nel file appsettings.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": "*"
}

Esegui di nuovo l'app, con la modifica precedente; il messaggio di log ora è formattato come 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}"
  }
}

Suggerimento

Per impostazione predefinita, il formattatore della console Json registra ogni messaggio in una singola riga. Per renderlo più leggibile durante la configurazione del formattatore, imposta JsonWriterOptions.Indented su true.

Attenzione

Quando usi il formattatore della console Json, non passare messaggi di log già serializzati come JSON. L'infrastruttura di registrazione stessa gestisce già la serializzazione dei messaggi di log, quindi se passi un messaggio di log già serializzato, verrà serializzato due volte, generando così un output non valido.

Impostare il formattatore con la configurazione

Gli esempi precedenti hanno illustrato come registrare un formattatore a livello di codice. In alternativa, questa operazione può essere eseguita con la configurazione. Considera il codice sorgente di esempio dell'applicazione Web precedente, se aggiorni il file appsettings.json anziché chiamare ConfigureLogging nel file Program.cs, puoi ottenere lo stesso risultato. Il file appsettings.json aggiornato configura il formattatore come segue:

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

I due valori chiave che devono essere impostati sono "FormatterName" e "FormatterOptions". Se un formattatore con il valore impostato per "FormatterName" è già registrato, tale formattatore viene selezionato e le relative proprietà possono essere configurate purché vengano fornite come chiave all'interno del nodo "FormatterOptions". I nomi dei formattatori predefiniti sono riservati in ConsoleFormatterNames:

Implementare un formattatore personalizzato

Per implementare un formattatore personalizzato, è necessario:

Creare un metodo di estensione per gestirlo automaticamente:

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);
}

I CustomOptions sono definiti come segue:

using Microsoft.Extensions.Logging.Console;

namespace Console.ExampleFormatters.Custom;

public sealed class CustomOptions : ConsoleFormatterOptions
{
    public string? CustomPrefix { get; set; }
}

Nel codice precedente, le opzioni sono una sottoclasse di ConsoleFormatterOptions.

L'API AddConsoleFormatter:

  • Registra una sottoclasse di ConsoleFormatter
  • Gestisce la configurazione:
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.");
}

Definire una sottoclasse CustomFormatter di ConsoleFormatter:

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();
}

L'API CustomFormatter.Write<TState> precedente determina il testo di cui viene eseguito il wrapping in ogni messaggio di log. Uno ConsoleFormatter standard deve essere in grado di eseguire il wrapping come minimo degli ambiti, dei timestamp e del livello di gravità dei log. Inoltre, puoi codificare i colori ANSI dei messaggi di log e specificare anche i rientri desiderati. L'implementazione di CustomFormatter.Write<TState> non dispone di queste funzionalità.

Per ulteriori informazioni sulla personalizzazione della formattazione, vedi le implementazioni esistenti nello spazio dei nomi Microsoft.Extensions.Logging.Console:

Opzioni di configurazione personalizzate

Per personalizzare ulteriormente l'estendibilità della registrazione, puoi configurare la classe ConsoleFormatterOptions derivata da qualsiasi provider di configurazione. Ad esempio, puoi usare il provider di configurazione JSON per definire le opzioni personalizzate. Definisci prima di tutto la sottoclasse ConsoleFormatterOptions.

using Microsoft.Extensions.Logging.Console;

namespace Console.ExampleFormatters.CustomWithConfig;

public sealed class CustomWrappingConsoleFormatterOptions : ConsoleFormatterOptions
{
    public string? CustomPrefix { get; set; }

    public string? CustomSuffix { get; set; }
}

La classe di opzioni del formattatore della console precedente definisce due proprietà personalizzate, che rappresentano un prefisso e un suffisso. Definisci quindi il file appsettings.json che configurerà le opzioni del formattatore della console.

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

Nel file di configurazione JSON precedente:

  • Il nodo "Logging" definisce un "Console".
  • Il nodo "Console" specifica un "FormatterName" di "CustomTimePrefixingFormatter", che esegue il mapping a un formattatore personalizzato.
  • Il nodo "FormatterOptions" definisce un "CustomPrefix" e "CustomSuffix", nonché alcune altre opzioni derivate.

Suggerimento

Il percorso JSON $.Logging.Console.FormatterOptions è riservato e verrà eseguito il mapping a un ConsoleFormatterOptions personalizzato quando viene aggiunto usando il metodo di estensione AddConsoleFormatter. In questo modo è possibile definire proprietà personalizzate, oltre a quelle disponibili.

Considera il seguente CustomDatePrefixingFormatter:

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();
}

Nell'implementazione del formattatore precedente:

Per usare le opzioni di configurazione personalizzate, con implementazioni di formattatore personalizzate, aggiungi quando chiami ConfigureLogging(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.");
}

L'output della console seguente è simile a quello previsto dall'uso di questo CustomTimePrefixingFormatter.

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

Implementare la formattazione personalizzata dei colori

Per abilitare correttamente le funzionalità di colore nel formattatore di registrazione personalizzato, puoi estendere SimpleConsoleFormatterOptions poiché dispone di una proprietà SimpleConsoleFormatterOptions.ColorBehavior che può essere utile per abilitare i colori nei log.

Creare un CustomColorOptions che deriva da SimpleConsoleFormatterOptions:

using Microsoft.Extensions.Logging.Console;

namespace Console.ExampleFormatters.Custom;

public class CustomColorOptions : SimpleConsoleFormatterOptions
{
    public string? CustomPrefix { get; set; }
}

Scrivi quindi alcuni metodi di estensione in una classe TextWriterExtensions che consentono di incorporare facilmente colori codificati ANSI all'interno di messaggi di log formattati:

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
        };
}

Un formattatore di colori personalizzato che gestisce l'applicazione di colori personalizzati può essere definito come segue:

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();
}

Quando esegui l'applicazione, i log mostreranno il messaggio CustomPrefix in colore verde quando FormatterOptions.ColorBehavior è Enabled.

Nota

Quando LoggerColorBehavior è Disabled, i messaggi di log non interpretano i codici di colore ANSI incorporati all'interno dei messaggi di log. Vengono invece restituiti i messaggi non elaborati. Considera l'esempio seguente:

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

In questo modo viene restituita la stringa verbatim e non viene colorata.

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

Vedi anche