Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Theme for coloring all line #35

Open
Lukino2000 opened this issue Mar 11, 2018 · 13 comments
Open

Theme for coloring all line #35

Lukino2000 opened this issue Mar 11, 2018 · 13 comments

Comments

@Lukino2000
Copy link

is there anyway to obtain an output like this using theme?

coloredconsolesinkdemo

@nblumhardt
Copy link
Member

Hi @Lukino2000 - no, this isn't possible in the current version of the sink. SystemConsoleTheme.Colored is the best replacement available for the old "colored" console theme.

Just to gauge interest - how important is the line coloring for you/your app? It's possible we could extend the theming system to support it, but there aren't all that many possible themes that can be created using this mechanism so we've held off on attempting this so far.

@LostSoulfly
Copy link

While I personally don't have a need for coloring lines, I'd like to be able to change the output color for an individual log event, such as coloring certain currency logs different colors (for example -- to make it easier to distinguish between GBP and USD), without having to create a theme.

@dluc
Copy link

dluc commented Jun 25, 2018

I'd love to see this, i.e. the whole line using the color of the level used, it would make the output much easier to read. I would never use the ability to customize the color of a single line, but colorizing parameters would be great, e.g. highlighting the exception class, a correlation ID, a server IP etc.
Off topic, it would also be nice to have a theme for consoles with white background.

@JFesta
Copy link

JFesta commented Jul 23, 2018

This is a nice to have feature for a console application currently under development: I'm using Serilog to write execution status info to the output console with this simple template: {Message}{NewLine}{Exception}; I don't want to show the log level to the user, but nonetheless I need to make him aware if there are any anomalies or errors, and the best way to do it is by coloring the entire line.

@sajagi
Copy link

sajagi commented Sep 17, 2018

This is a nice to have feature for a console application currently under development: I'm using Serilog to write execution status info to the output console with this simple template: {Message}{NewLine}{Exception}; I don't want to show the log level to the user, but nonetheless I need to make him aware if there are any anomalies or errors, and the best way to do it is by coloring the entire line.

This is exactly my use case too. For short-running applications this makes sense - esp. when you want to use a single framework to logging into file and having a nice formatted console output for the user.

@adamchester
Copy link
Member

adamchester commented Sep 17, 2018

I haven’t been back for a while, but I did a spike on making the template token renderer public. I was attempting to see how a few different proposed features might work here:

dev...adamchester:spike-themed-exception

Edit: this has an example of how to colour lines by log level

@nblumhardt
Copy link
Member

Righto, thanks for all the input! If anyone's keen to discuss design options, with a view to submitting a PR, I can help figure out how we can get this in 👍

@jakubsuchybio
Copy link

jakubsuchybio commented Nov 13, 2018

I'd like to take a look at that @nblumhardt
First I'd rework ConsoleThemeStyle to be extensible (reason will be later) and to have support for hierarchy.
With that I would add new properties for template variables (e.g. Message, Exception, etc). Which could also be more specific by adding level (e.g. Message.Verbose, Exception.Fatal, etc)

Because of the specific by level and extensibility there must be some hierarchy, so that when something isn't specified, it has to have some fallback value. (e.g. Message.Verbose not present -> use Message, Message.Verbose not present -> Message not present -> use Text)

And by extensibility I mean, that it would also support variables from enrichers like ThreadId from Serilog.Enrichers.Thread.
This extensibility would be for a user of serilog, which want to have custom theme, so he could set color for everything in template.

I am still thinking what algorithm and data structure to use for it internally so that it is most efficient. It has to have possibly O(1) speed access with as little constant coeficient as possible.

@HunteRoi
Copy link

May I... up this? 👀

@ardove
Copy link

ardove commented Jun 4, 2019

One more up vote from me! We are using serilog for a dotnet global tool so we can write to multiple sinks and it'd be useful to colorize the entire log line, in addition the background color of a log line when writing to the console.

@ardove
Copy link

ardove commented Jun 13, 2019

For those watching, we were able to implement a naive version that supports structure and full line colorization that meets our requirements using the following code. To support customized colorization at just the level or just the log line, you'd likely need your own implementation(s) of ITextFormatter which colorize the variety of parts.

I'm certain that someone could generalize this to make it more broadly applicable using a them instead of hardcoding colorization requirements.

Hope this helps the next person who needs this!

public class ColoredConsoleSink : ILogEventSink
    {
        private readonly ConsoleColor _defaultForeground = Console.ForegroundColor;
        private readonly ConsoleColor _defaultBackground = Console.BackgroundColor;

        private readonly ITextFormatter _formatter;

        public ColoredConsoleSink(ITextFormatter formatter)
        {
            _formatter = formatter;
        }

        public void Emit(LogEvent logEvent)
        {
            if (logEvent.Level >= LogEventLevel.Fatal)
            {
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.BackgroundColor = ConsoleColor.Red;
            }
            else if (logEvent.Level >= LogEventLevel.Error)
            {
                Console.ForegroundColor = ConsoleColor.Red;
            }
            else if (logEvent.Level >= LogEventLevel.Warning)
            {
                Console.ForegroundColor = ConsoleColor.Yellow;
            }

            _formatter.Format(logEvent, Console.Out);
            Console.Out.Flush();

            Console.ForegroundColor = _defaultForeground;
            Console.BackgroundColor = _defaultBackground;
        }
    }

And the extension method to enable it:

public static class ColoredConsoleSinkExtensions
    {
        public static LoggerConfiguration ColoredConsole(
            this LoggerSinkConfiguration loggerConfiguration,
            LogEventLevel minimumLevel = LogEventLevel.Verbose,
            string outputTemplate = "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}",
            IFormatProvider formatProvider = null)
        {
            return loggerConfiguration.Sink(new ColoredConsoleSink(new MessageTemplateTextFormatter(outputTemplate, formatProvider)), minimumLevel);
        }
    }

@bwedding
Copy link

bwedding commented Jul 3, 2019

I wrote an extension to accomplish this. I don't know if this is better or worse than the code above, but anyone is free to use it. It will accept colors. As written it only supports logging at information level. Output looks like this:

colorlog

using System;
using System.Collections.Generic;
using Serilog;
using Serilog.Sinks.SystemConsole.Themes;

namespace main1
{
    static class LoggerExtensions
    {
        public const string BackgroundBlack    = "\u001b[40m";
        public const string BackgroundRed      = "\u001b[41m";
        public const string BackgroundGreen    = "\u001b[42m";
        public const string BackgroundYellow   = "\u001b[43m";
        public const string BackgroundBlue     = "\u001b[44m";
        public const string BackgroundMagenta  = "\u001b[45m";
        public const string BackgroundCyan     = "\u001b[46m";
        public const string BackgroundWhite    = "\u001b[47m";
        public const string BackgroundBrightBlack      = "\u001b[40;1m";
        public const string BackgroundBrightRed        = "\u001b[41;1m";
        public const string BackgroundBrightGreen      = "\u001b[42;1m";
        public const string BackgroundBrightYellow     = "\u001b[43;1m";
        public const string BackgroundBrightBlue       = "\u001b[44;1m";
        public const string BackgroundBrightMagenta    = "\u001b[45;1m";
        public const string BackgroundBrightCyan       = "\u001b[46;1m";
        public const string BackgroundBrightWhite      = "\u001b[47;1m";

        public static List<int> AllIndexesOf(this string str, string value)
        {
            if (String.IsNullOrEmpty(value))
                throw new ArgumentException("the string to find may not be empty", "value");
            List<int> indexes = new List<int>();
            for (int index = 0; ; index += value.Length)
            {
                index = str.IndexOf(value, index);
                if (index == -1)
                    return indexes;
                indexes.Add(index);
            }
        }
        public static void BkColor(
          this ILogger logger,
          string messageTemplate,
          params object[] args)
        {
            // Get the color they chose
            string CurrentColor = (string)args[args.GetLength(0)-1];

            // Get rid of the color parameter now as it will break the Serilog parser
            args[args.GetLength(0)-1] = "";  

            // Prepend our color code to every argument (tested with strings and numbers)
            for (int i = 0; i < args.GetLength(0); i++)
            {
                args[i] = CurrentColor + args[i];
            }

            // Find all the arguments looking for the close bracket
            List<int> indexes = messageTemplate.AllIndexesOf("}");
            int iterations = 0;
            // rebuild messageTemplate with our color-coded arguments
            // Note: we have to increase the index on each iteration based on the previous insertion of
            // a color code
            foreach (var i in indexes)
            {
                messageTemplate = messageTemplate.Insert(i + 1 + (iterations++ * CurrentColor.Length), CurrentColor);
            }

            // Prefix the entire message template with color code
            string bkg = CurrentColor + messageTemplate;

            // Log it with a context
            logger.ForContext("IsImportant", true)
              .Information(bkg, args);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Debug()
            .WriteTo.Async(a => a.Console(theme: AnsiConsoleTheme.Code))
            .CreateLogger();

            Log.Information("Hello, Async Serilog!");
            Log.Fatal("This is really bad!");
            Log.Logger.BkColor("Hello, {Name}! How are you {Today}? Pi is {Pi}", "World", "Today", 3.14159,LoggerExtensions.BackgroundBrightRed);
            Log.Logger.BkColor("Hello World", LoggerExtensions.BackgroundBrightGreen);
            Log.Logger.BkColor("Hello World. Today is {date}", DateTime.Now, LoggerExtensions.BackgroundBrightMagenta);
            Log.CloseAndFlush();
            Console.ReadKey();
        }
    }
}

Complete code at this gist:
https://gist.github.com/bwedding/fd12e1ae1a4045b6c797e9d13319d751

@Hedgineering
Copy link

Hedgineering commented Jan 8, 2025

This is somewhat tangentially related but I used Serilog.Expressions along with ANSI coloring to achieve full line coloring results. Here's how you can recreate what I did:
image

I defined the following class to help build ANSI escape sequences to apply special effects such as coloring, bold, italics, underline, etc. in a more readable way. It also serves as a helpful tutorial for how ANSI works and how it's used as a standard across several different terminals.

namespace AnsiHelpersCollection
{
    public class AnsiHelpers
    {
        // ANSI is a standard used by text input programs such as command line or terminal emulators to control text formatting, color, and other output options.
        // Here's a simple example of how to use ANSI escape codes to change the color of text in the terminal.
        // See for more info on ANSI: 
        // - https://gist.github.com/ConnerWill/d4b6c776b509add763e17f9f113fd25b
        // - https://youtu.be/yQ9Ns6Z4Q-s
        //
        // It doesn't matter what escape character you use
        // Any of these can be used to prefix a semicolon-separated list of modifiers with a trailing 'm' to indicate the end of the sequence.
        // That combination of effects will be applied until the next reset code is encountered.
        public static string unicode_escape = "\u001b[";
        public static string hex_escape = "\x1b[";

        public static string ansi_reset_all_code = "0";
        // Note that it does not matter which escape character you use. But for all Ansi effects, the prefix must be a valid escape character (unicode or hex escape works).
        public static string reset_all = $"{unicode_escape}{ansi_reset_all_code}m";

        // Modifiers
        public static string bold = "1";
        public static string bold_reset = "22";
        public static string dim = "2";
        public static string dim_reset = "22";
        public static string italic = "3";
        public static string italic_reset = "23";
        public static string underline = "4";
        public static string underline_reset = "24";
        public static string blink = "5";
        public static string blink_reset = "25";
        public static string reverse = "7";
        public static string reverse_reset = "27";
        public static string hidden = "8";
        public static string hidden_reset = "28";
        public static string strikethrough = "9";
        public static string strikethrough_reset = "29";

        // Below is list of foreground and background codes:
        public static string fg_black = "30";
        public static string bg_black = "40";
        public static string fg_red = "31";
        public static string bg_red = "41";
        public static string fg_green = "32";
        public static string bg_green = "42";
        public static string fg_yellow = "33";
        public static string bg_yellow = "43";
        public static string fg_blue = "34";
        public static string bg_blue = "44";
        public static string fg_magenta = "35";
        public static string bg_magenta = "45";
        public static string fg_cyan = "36";
        public static string bg_cyan = "46";
        public static string fg_white = "37";
        public static string bg_white = "47";

        // And a list of foreground and background codes if a terminal supports axiterm colors:
        public static string fg_bright_black = "90";
        public static string bg_bright_black = "100";
        public static string fg_bright_red = "91";
        public static string bg_bright_red = "101";
        public static string fg_bright_green = "92";
        public static string bg_bright_green = "102";
        public static string fg_bright_yellow = "93";
        public static string bg_bright_yellow = "103";
        public static string fg_bright_blue = "94";
        public static string bg_bright_blue = "104";
        public static string fg_bright_magenta = "95";
        public static string bg_bright_magenta = "105";
        public static string fg_bright_cyan = "96";
        public static string bg_bright_cyan = "106";
        public static string fg_bright_white = "97";
        public static string bg_bright_white = "107";

        /// <summary>
        /// The effects of the basic colors/modifiers and axiterm colors can be stacked using this combine function
        /// </summary>
        /// <param name="modifiers"></param>
        /// <returns></returns>
        public static string Combine(params string[] modifiers)
        {
            return $"{hex_escape}{string.Join(";", modifiers)}m";
        }

        /// <summary>
        /// If your terminal supports 256 colors, you can use the following function to set the color of text.
        /// See for color codes: https://user-images.githubusercontent.com/995050/47952855-ecb12480-df75-11e8-89d4-ac26c50e80b9.png
        /// </summary>
        /// <param name="color"></param>
        /// <param name="getBackgroundCode"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static string ColorId256ToAnsi(int color, bool getBackgroundCode = false)
        {
            if (color < 0 || color > 255)
            {
                throw new ArgumentOutOfRangeException(nameof(color), "Color must be between 0 and 255.");
            }
            return $"{unicode_escape}{(getBackgroundCode ? "48" : "38")};5;{color}m";
        }

        /// <summary>
        /// If your terminal supports true color, you can use the following function to set the color of text using RGB values.
        /// </summary>
        /// <param name="r"></param>
        /// <param name="g"></param>
        /// <param name="b"></param>
        /// <param name="getBackgroundCode"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static string RgbToAnsi(int r, int g, int b, bool getBackgroundCode = false)
        {
            if (r < 0 || r > 255)
            {
                throw new ArgumentOutOfRangeException(nameof(r), "Red must be between 0 and 255.");
            }
            if (g < 0 || g > 255)
            {
                throw new ArgumentOutOfRangeException(nameof(g), "Green must be between 0 and 255.");
            }
            if (b < 0 || b > 255)
            {
                throw new ArgumentOutOfRangeException(nameof(b), "Blue must be between 0 and 255.");
            }
            return $"{unicode_escape}{(getBackgroundCode ? "48" : "38")};2;{r};{g};{b}m";
        }

        public static void TestTerminalAnsiSupport()
        {
            Console.WriteLine(
                $"Here's some basic ANSI coloring: {Combine([bg_red, fg_white, bold])}Hello, World!{reset_all}{Combine([bold])} more bold text {Combine([bold_reset])}{reset_all} and more normal text ");

            Console.WriteLine(
                $"If your terminal supports axiterm bright colors: \n\t{Combine([bg_red, fg_white])}this should be white on normal red text and{reset_all}\n\t{Combine([bg_bright_red, fg_white])} this should be white on bright red text (slightly brighter background color) {reset_all} and more normal text ");
            
            // Note that the 256 and rgb colors can be placed fully qualified, one after another, to apply both effects simultaneously.
            Console.WriteLine(
                $"If your terminal supports 256 color, {ColorId256ToAnsi(154, getBackgroundCode: false)}{ColorId256ToAnsi(90, getBackgroundCode: true)}this should be some green on purple text {reset_all}");
            Console.WriteLine(
                $"If your terminal supports Truecolor (RGB values), {RgbToAnsi(131, 1, 1, getBackgroundCode: false)}{RgbToAnsi(255, 129, 3, getBackgroundCode: true)}this should be some maroon on orange text {reset_all}");

        }
    }
}

By the way, here's a python version of the same thing that might be a little easier to read if you want to understand the logic

# ANSI is a standard used by text input programs such as command line or terminal emulators to control text formatting, color, and other output options.
# Here's a simple example of how to use ANSI escape codes to change the color of text in the terminal.
# See for more info on ANSI: 
# - https://gist.github.com/ConnerWill/d4b6c776b509add763e17f9f113fd25b
# - https://youtu.be/yQ9Ns6Z4Q-s

# It doesn't matter what escape character you use
# Any of these can be used to prefix a semicolon-separated list of modifiers with a trailing 'm' to indicate the end of the sequence.
# That combination of effects will be applied until the next reset code is encountered.
unicode_escape = "\u001b["
octal_escape = "\033["
hex_escape = "\x1b["

ansi_fg_reset_all = f"0"

# Modifiers
bold, bold_reset = "1", "22"
dim, dim_reset = "2", "22"
italic, italic_reset = "3", "23"
underline, underline_reset = "4", "24"
blink, blink_reset = "5", "25"
reverse, reverse_reset = "7", "27"
hidden, hidden_reset = "8", "28"
strikethrough, strikethrough_reset = "9", "29"

# Below is list of foreground and background codes:
fg_black, bg_black      =  f"30", f"40"
fg_red, bg_red          =  f"31", f"41"
fg_green, bg_green      =  f"32", f"42"
fg_yellow, bg_yellow    =  f"33", f"43"
fg_blue, bg_blue        =  f"34", f"44"
fg_magenta, bg_magenta  =  f"35", f"45"
fg_cyan, bg_cyan        =  f"36", f"46"
fg_white, bg_white      =  f"37", f"47"

# And a list of foreground and background codes if a terminal supports axiterm colors:
fg_bright_black, bg_bright_black      =  f"90", f"100"
fg_bright_red, bg_bright_red          =  f"91", f"101"
fg_bright_green, bg_bright_green      =  f"92", f"102"
fg_bright_yellow, bg_bright_yellow    =  f"93", f"103"
fg_bright_blue, bg_bright_blue        =  f"94", f"104"
fg_bright_magenta, bg_bright_magenta  =  f"95", f"105"
fg_bright_cyan, bg_bright_cyan        =  f"96", f"106"
fg_bright_white, bg_bright_white      =  f"97", f"107"

# The effects of the basic colors/modifiers and axiterm colors can be stacked using this combine function
def combine(modifiers):
    return f"{hex_escape}{';'.join(modifiers)}m"

# If your terminal supports 256 colors, you can use the following function to set the color of text.
# See for color codes: https://user-images.githubusercontent.com/995050/47952855-ecb12480-df75-11e8-89d4-ac26c50e80b9.png
def set_256_color(color, is_bg=False):
    assert 0 <= color <= 255, f"Color must be between 0 and 255, not {color}"
    return f"{unicode_escape}{'48' if is_bg else '38'};5;{color}m"

# If your terminal supports true color, you can use the following function to set the color of text.
def set_rgb_color(r, g, b, is_bg=False):
    assert 0 <= r <= 255, f"Red must be between 0 and 255, not {r}"
    assert 0 <= g <= 255, f"Green must be between 0 and 255, not {g}"
    assert 0 <= b <= 255, f"Blue must be between 0 and 255, not {b}"
    return f"{unicode_escape}{'48' if is_bg else '38'};2;{r};{g};{b}m"

# Reset all effects
reset_all = combine([ansi_fg_reset_all])

print(f"{combine([bg_red, fg_white, bold])}Hello, World!{reset_all}{combine([bg_bright_red, fg_white])} more bold text {combine([bold_reset])}{reset_all} and more normal text ")

# Note that the 256 and rgb colors can be placed fully qualified, one after another, to apply both effects simultaneously.
print(f"{set_256_color(154, is_bg=False)}{set_256_color(90, is_bg=True)}Here is some green on purple text{reset_all}")
print(f"{set_rgb_color(131, 1, 1, is_bg=False)}{set_rgb_color(255, 129, 3, is_bg=True)}Here is some maroon on orange text{reset_all}")

Here's my main .csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Serilog" Version="4.2.0" />
    <PackageReference Include="Serilog.Enrichers.CallerInfo" Version="1.0.5" />
    <PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" />
    <PackageReference Include="Serilog.Expressions" Version="5.0.0" />
    <PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\AnsiHelpersProj\AnsiHelpersProj.csproj" />
  </ItemGroup>

</Project>

Here's my main program

using AnsiHelpersCollection; // Defined above
using Serilog;
using Serilog.Enrichers.CallerInfo;
using Serilog.Events;
using Serilog.Templates;
using Serilog.Templates.Themes;

namespace SerilogWithCaller;

class Program
{
    public static void Main(string[] args) 
    {
        // Use for ColorId256 color codes: https://user-images.githubusercontent.com/995050/47952855-ecb12480-df75-11e8-89d4-ac26c50e80b9.png
        var themeDict = (IReadOnlyDictionary<TemplateThemeStyle, string>)new Dictionary<TemplateThemeStyle, string>()
        {
            [TemplateThemeStyle.Text] = AnsiHelpers.Combine([AnsiHelpers.bg_black, AnsiHelpers.fg_bright_green]),
            [TemplateThemeStyle.SecondaryText] =
                AnsiHelpers.ColorId256ToAnsi(246, getBackgroundCode: false), // light gray foreground text
            [TemplateThemeStyle.TertiaryText] =
                AnsiHelpers.ColorId256ToAnsi(242, getBackgroundCode: false), // dark gray foreground text
            [TemplateThemeStyle.Invalid] = AnsiHelpers.Combine([AnsiHelpers.fg_yellow, AnsiHelpers.bold]),
            [TemplateThemeStyle.Null] =
                AnsiHelpers.ColorId256ToAnsi(38, getBackgroundCode: false), // light aquamarine foreground text
            [TemplateThemeStyle.Name] = AnsiHelpers.ColorId256ToAnsi(81, getBackgroundCode: false), // cyan foreground text
            [TemplateThemeStyle.String] =
                AnsiHelpers.ColorId256ToAnsi(216, getBackgroundCode: false), // beige foreground text
            [TemplateThemeStyle.Number] =
                AnsiHelpers.ColorId256ToAnsi(151, getBackgroundCode: false), // pale greenish blue foreground text
            [TemplateThemeStyle.Boolean] =
                AnsiHelpers.ColorId256ToAnsi(38, getBackgroundCode: false), // light aquamarine foreground text
            [TemplateThemeStyle.Scalar] =
                AnsiHelpers.ColorId256ToAnsi(79, getBackgroundCode: false), // aquamarine foreground text
            [TemplateThemeStyle.LevelVerbose] = AnsiHelpers.ColorId256ToAnsi(75, getBackgroundCode: false), // light blue
            [TemplateThemeStyle.LevelDebug] = AnsiHelpers.Combine([AnsiHelpers.fg_white]),
            [TemplateThemeStyle.LevelInformation] = AnsiHelpers.Combine([AnsiHelpers.bg_black, AnsiHelpers.fg_green]),
            [TemplateThemeStyle.LevelWarning] = AnsiHelpers.Combine([AnsiHelpers.fg_yellow]),
            [TemplateThemeStyle.LevelError] =
                $"{AnsiHelpers.Combine([AnsiHelpers.fg_bright_white])}{AnsiHelpers.Combine([AnsiHelpers.bg_bright_red])}",
            [TemplateThemeStyle.LevelFatal] =
                $"{AnsiHelpers.ColorId256ToAnsi(154, getBackgroundCode: false)}{AnsiHelpers.Combine([AnsiHelpers.bg_magenta])}", // lime green text on magenta background
        };

        var timeAndThreadPrefix = "{@t:yyyy-MM-dd HH:mm:ss.fff zzz} [{@p['ThreadId']}] ";

        // We split the namespace to get only the class name to prevent long names from overflowing the console
        var messageSuffix =
            " [{Substring(@p['Namespace'], LastIndexOf(@p['Namespace'], '.') + 1)} - {@p['Method']}] - {@m} {@x}";

        // Learn more about Serilog expression template language: 
        // - https://github.com/serilog/serilog-expressions?tab=readme-ov-file#formatting-with-expressiontemplate
        // - https://github.com/serilog/serilog-expressions?tab=readme-ov-file#language-reference
        var consoleLogExpressionTemplate = new ExpressionTemplate(
            $"{{#if @l = 'Verbose'}}" +
            $"{AnsiHelpers.reset_all}{themeDict[TemplateThemeStyle.LevelVerbose]}" + timeAndThreadPrefix + "VERBOSE" +
            messageSuffix + $"{AnsiHelpers.reset_all}\n" +
            $"{{#else if @l = 'Debug'}}" +
            $"{AnsiHelpers.reset_all}{themeDict[TemplateThemeStyle.LevelDebug]}" + timeAndThreadPrefix + "DEBUG" +
            messageSuffix + $"{AnsiHelpers.reset_all}\n" +
            $"{{#else if @l = 'Information'}}" +
            $"{AnsiHelpers.reset_all}{themeDict[TemplateThemeStyle.LevelInformation]}" + timeAndThreadPrefix + "INFO" +
            messageSuffix + $"{AnsiHelpers.reset_all}\n" +
            $"{{#else if @l = 'Warning'}}" +
            $"{AnsiHelpers.reset_all}{themeDict[TemplateThemeStyle.LevelWarning]}" + timeAndThreadPrefix + "WARN" +
            messageSuffix + $"{AnsiHelpers.reset_all}\n" +
            $"{{#else if @l = 'Error'}}" +
            $"{AnsiHelpers.reset_all}{themeDict[TemplateThemeStyle.LevelError]}" + timeAndThreadPrefix + "ERROR" +
            messageSuffix + $"{AnsiHelpers.reset_all}\n" +
            $"{{#else if @l = 'Fatal'}}" +
            $"{AnsiHelpers.reset_all}{themeDict[TemplateThemeStyle.LevelFatal]}" + timeAndThreadPrefix + "FATAL" +
            messageSuffix + $"{AnsiHelpers.reset_all}\n" +
            $"{{#else}}" +
            $"{AnsiHelpers.reset_all}{themeDict[TemplateThemeStyle.Text]}" + timeAndThreadPrefix + "OTHER" + messageSuffix +
            $"{AnsiHelpers.reset_all}\n" +
            $"{{#end}}"
        );


        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Verbose()
        .Enrich.WithCallerInfo(includeFileInfo: true, new List<string> {
            "SerilogWithCaller"
        })
        .Enrich.WithThreadId()
        .WriteTo.Console(consoleLogExpressionTemplate, restrictedToMinimumLevel:LogEventLevel.Verbose)
        .CreateLogger();

        var eng = new Engine();
        eng.DoSomething();

        Console.WriteLine();

    }

}

Here's Engine.cs

using Serilog;

namespace SerilogWithCaller;
public class Engine
{
    public void DoSomething()
    {
        var val = new { longitude = "some value", latitude = "some other value" };

        Log.Verbose("Some Verbosity... {val}", val);
        Log.Debug("Some debug");
        Log.Information("Doing something...");
        Log.Warning("Some warning...");
        Log.Error(new Exception("Some exception text"), "Some error...");
        Log.Fatal("Some fatal...");
    }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests