serilog / serilog-sinks-console

Write log events to System.Console as text or JSON, with ANSI theme support
Apache License 2.0
241 stars 72 forks source link

I would like to open a PR to provide support for 24-bit RGB color and log style formatting #138

Closed JasonLandbridge closed 1 year ago

JasonLandbridge commented 1 year ago

Hi there👋

So after diving in the code and doing some research, I figured out that AnsiConsoleTheme can essentially support 24-bit RGB color and log formatting (think italics, bold, underline, etc). :tada:

See this interesting ANSI Escape Sequences article about color codes and text formatting

So I played around with it and created a system where an AnsiConsoleTheme can be created with 24-bit RGB color and log formatting, completely with a Fluent API.

Screenshot taken in Jetbrains Rider with UI in dark mode:

image

It works really well, if I say so myself.

// Example of a theme:
public static AnsiConsoleTheme ColoredDarkAnsi { get; } = new(
        new Dictionary<ConsoleThemeStyle, string>
        {
            // Timestamp, classname, method name and line number
            [ConsoleThemeStyle.SecondaryText] = LogTheme
                .Foreground(Color.Gray)
                .Background(Color.Aqua)
                .FormatType(FormatType.ItalicMode)
                .ToStyle(),

            // Brackets, dots and colons
            [ConsoleThemeStyle.TertiaryText] = LogTheme.Style(Color.Gray),

            // Log message
            [ConsoleThemeStyle.Text] = LogTheme
                .Foreground(Color.LightGray)
                .ToStyle(),

            [ConsoleThemeStyle.Invalid] = LogTheme.Style(Color.Yellow),
            [ConsoleThemeStyle.Null] = LogTheme.Style(Color.LightGray),
            [ConsoleThemeStyle.Name] = LogTheme.Style(Color.White),

            // Log values
            [ConsoleThemeStyle.String] = LogTheme.Style(Color.DarkRed),
            [ConsoleThemeStyle.Number] = LogTheme.Style(Color.DarkGreen),
            [ConsoleThemeStyle.Boolean] = LogTheme.Style(Color.DarkKhaki),
            [ConsoleThemeStyle.Scalar] = LogTheme.Style(Color.Black),

            // Log Level coloring
            [ConsoleThemeStyle.LevelVerbose] = LogTheme.Style(Color.White, Color.DarkGray),
            [ConsoleThemeStyle.LevelDebug] = LogTheme.Style(Color.White, Color.DarkGray),
            [ConsoleThemeStyle.LevelInformation] = LogTheme.Style(Color.White, Color.FromArgb(23, 126, 137)),
            [ConsoleThemeStyle.LevelWarning] = LogTheme.Style(Color.Black, Color.FromArgb(255, 200, 87)),
            [ConsoleThemeStyle.LevelError] = LogTheme.Style(Color.White, Color.Red),
            [ConsoleThemeStyle.LevelFatal] = LogTheme.Style(Color.White, Color.DarkRed),
        });

The above essentially boils down to the following:


    public static string Style(Color? foreground, Color? background, FormatType formatType = Logging.FormatType.None)
    {
        var builder = new StringBuilder("\x1b["); // ESC character

        if (formatType is not Logging.FormatType.None)
            builder.Append(formatType.ToAnsiString() + ";"); // Formatting style, bold, italic etc

        if (foreground is not null)
            builder.Append(foreground.ToAnsiString(ColorLayerEnum.ForegroundColor)); // Font color

        if (foreground is not null && background is not null)
            builder.Append(";");

        if (foreground is not null)
            builder.Append(background.ToAnsiString(ColorLayerEnum.BackgroundColor)); // Font background color
        builder.Append("m"); // exit command
        return builder.ToString();
    }

Which outputs an Ansi string that can set a foreground, background and text format.

All the above is currently part of my own project PlexRipper but a sneak peak can be viewed here in the config folder

The following text formats work after a bit of testing, maybe I can have all working after a bit more testing:

public enum FormatType
{
    None = 0,

    /// <summary>
    /// Works,
    /// </summary>
    BoldMode = 1,

    /// <summary>
    /// Works, text is faint
    /// </summary>
    DimFaintMode = 2,

    /// <summary>
    /// Works,
    /// </summary>
    ItalicMode = 3,

    /// <summary>
    /// Works, the underline is the same color as the foreground.
    /// </summary>
    UnderlineMode = 4,

    /// <summary>
    /// Not working
    /// </summary>
    BlinkingMode = 5,

    /// <summary>
    /// Works, uses the foreground as the background color and vice versa
    /// </summary>
    InverseReverseMode = 6,

    /// <summary>
    /// Not working
    /// </summary>
    HiddenMode = 7,

    /// <summary>
    /// Not working
    /// </summary>
    Strikethrough = 8,
}

I'm currently on the latest Serilog.Sinks.Console 4.1.0 and would be more than happy to create a PR to have this merged!

Thanks!

nblumhardt commented 1 year ago

Thanks for the heads-up! This looks nice - though since it can be layered on top of the console sink, it seems like a good candidate to release as a separate package. Have you thought about how it would look as e.g. Serilog.Sinks.Console.ThemeBuilder or something along those lines?

JasonLandbridge commented 1 year ago

Thanks, yeah that might be a good idea! I will turn it into a separate package :+1:

nblumhardt commented 1 year ago

Awesome! Please do drop us a line when it's out there :-)