serilog-sinks-console icon indicating copy to clipboard operation
serilog-sinks-console copied to clipboard

Theme for coloring all line

Open Lukino2000 opened this issue 6 years ago • 12 comments

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

coloredconsolesinkdemo

Lukino2000 avatar Mar 11 '18 21:03 Lukino2000

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.

nblumhardt avatar Mar 12 '18 01:03 nblumhardt

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.

BradleyShaner avatar Mar 18 '18 00:03 BradleyShaner

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.

dluc avatar Jun 25 '18 22:06 dluc

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.

JFesta avatar Jul 23 '18 23:07 JFesta

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.

sajagi avatar Sep 17 '18 13:09 sajagi

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:

https://github.com/serilog/serilog-sinks-console/compare/dev...adamchester:spike-themed-exception

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

adamchester avatar Sep 17 '18 17:09 adamchester

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 :+1:

nblumhardt avatar Sep 22 '18 00:09 nblumhardt

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.

jakubsuchybio avatar Nov 13 '18 07:11 jakubsuchybio

May I... up this? :eyes:

HunteRoi avatar May 28 '19 02:05 HunteRoi

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 avatar Jun 04 '19 21:06 ardove

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

ardove avatar Jun 13 '19 17:06 ardove

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

bwedding avatar Jul 03 '19 22:07 bwedding