Echoing your System.Console output to an HTML log file, colours and all

by acha11 25. May 2009 09:00

The addition of colour support to the .NET console in 2.0 made me happy. A lot of the coding I do these days is console based: I've written a bunch of utilities that trawl through the big (~1.3 million LOC) code base our vendor's delivering, generating reports that highlight possible issues. Tasteful, understated use of colour in the output reports really improves their usability. Sure, I want a console version of the <blink/>and <marquee/> tags, but colour will keep me quiet for a while.

The problem comes when you want to archive coloured text from a console window. If you launch a console app from a command prompt and redirect its output to a text file (e.g. "myapp.exe > myappoutput.txt"), all you get is a plain text file; the colour information is lost. The same happens when you mark a region in the console window and copy to the clipboard; your text makes it to the clipboard, but the colouring is left behind.

The solution's pretty simple. I wrote a thin wrapper around System.Console which adds a new feature: the ability to echo console output to an HTML file, with formatting and colouring preserved.

So now when my console apps use colour (a little something like this):

ConsoleColor_Screenshot

I get HTML export with colouring for free:

image

Simple, but effective. Sure, I'm a little ashamed I'm not dynamically injecting instructions into an IL stream or anything, but on the other hand, this works. And it's multi-coloured. Top that.

Sample code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ExtendedConsole;

namespace ExtendedConsoleDemo
{
    class Program
    {
         // This member and the Console property getter are named so that existing static
         // calls to System.Console will instead be compiled into instance calls to this
         // HtmlExportConsole. No need to do it this way, of course.
         static HtmlExportConsole _console = new HtmlExportConsole("consoleLog.html");

        static HtmlExportConsole Console
        {
            get
            {
                return _console;
            }
        }
            
        static void Main(string[] args)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Here's some red");

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.Write("yellow");

            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("... green");
        }
    }
}

HtmlExportConsole

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web;

namespace ExtendedConsole
{
    /// <summary>
    /// This class is a thin wrapper around System.Console that adds the ability to echo your console output to an HTML file with formatting and colouring preserved. Not all methods
    /// on System.Console are supported at the moment. The simplest approach is just to add a thin wrapper for each method you need, as you need it (just as I have with KeyAvailable).
    ///
    /// When retrofitting an existing console app to use HtmlExportConsole instead of System.Console, I just declare a local getter of type HtmlExportConsole named "Console" - this
    /// local property effectively hides the System.Console static class, so that my existing calls, e.g. Console.Write(), get routed to HtmlExportConsole.Write() instead.
    ///
    /// To enable/disable output to an HTML file, set the bool EnableFileOut property.
    /// </summary>
    public class HtmlExportConsole : IDisposable
    {
        ConsoleColor _foregroundColor;
        ConsoleColor? _lastWrittenConsoleColor;
        StreamWriter _exportStreamWriter;
        public bool EnableConsoleOut { get; set; }
        public bool EnableFileOut { get; set; }

        public bool KeyAvailable
        {
            get
            {
                return Console.KeyAvailable;
            }
        }

        public HtmlExportConsole(string htmlOutputFilePath)
        {
            _foregroundColor = Console.ForegroundColor;
            _lastWrittenConsoleColor = null;
            _exportStreamWriter = new StreamWriter(htmlOutputFilePath);
            _exportStreamWriter.AutoFlush = true;

            EnableConsoleOut = true;
            EnableFileOut = true;

            _exportStreamWriter.Write("<html><head><style>BODY { font-family: Lucida Console; background-color: black; }</style></head><body>");
        }

        public ConsoleColor ForegroundColor
        {
            set
            {
                Console.ForegroundColor = value;
                _foregroundColor = value;
            }
        }

        public void Write(ConsoleColor foregroundColor, string value)
        {
            _foregroundColor = foregroundColor;

            Write(value);
        }

        public void Write(string value)
        {
            InternalWrite(value, false);
        }

        private void InternalWrite(string value, bool lineBreakAfter)
        {
            if (EnableConsoleOut)
            {
                Console.ForegroundColor = _foregroundColor;
                Console.Write(value + (lineBreakAfter ? Environment.NewLine : ""));
            }

            if (EnableFileOut)
            {
                if (_lastWrittenConsoleColor != _foregroundColor)
                {
                    if (_lastWrittenConsoleColor != null)
                    {
                        _exportStreamWriter.Write(String.Format(@"</font>"));
                    }

                    _exportStreamWriter.Write(String.Format(@"<font color=""{0}"">", _foregroundColor));
                }

                _exportStreamWriter.Write(HttpUtility.HtmlEncode(value).Replace(" ", "&nbsp;") + (lineBreakAfter ? "<br>" + Environment.NewLine : ""));

                _lastWrittenConsoleColor = _foregroundColor;
            }
        }

        public void WriteLine()
        {
            WriteLine("");
        }

        public void WriteLine(ConsoleColor foregroundColor, string value)
        {
            _foregroundColor = foregroundColor;

            WriteLine(value);
        }

        public void WriteLine(string value)
        {
            InternalWrite(value, true);
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (EnableFileOut)
            {
                _exportStreamWriter.Write("</body></html>");
            }

            _exportStreamWriter.Dispose();
        }

        #endregion
    }
}

Tags:

Comments

Comments are closed

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

RecentComments

Comment RSS