using System; using System.Globalization; using System.Runtime.InteropServices; using log4net.Layout; namespace log4net.Appender { /// /// Appends logging events to the console with ANSI escape sequences. /// /// /// /// AnsiColoredConsoleAppender appends log events to the standard output stream /// or the error output stream using a layout specified by the /// user. It also allows the color of a specific type of message to be set. /// /// /// By default, all output is written to the console's standard output stream. /// The property can be set to direct the output to the /// error stream. /// /// /// NOTE: This appender writes directly to the application's attached console /// not to the System.Console.Out or System.Console.Error TextWriter. /// The System.Console.Out and System.Console.Error streams can be /// programatically redirected (for example NUnit does this to capture program ouput). /// When this output is redirected it will look funny because the ANSI escape codes will /// also be captured. If you want to respect these redirections the /// must be used. /// /// /// When configuring the ANSI colored console appender, mapping should be /// specified to map a logging level to a color. For example: /// /// /// <mapping> /// <level value="ERROR" /> /// <foreColor value="White" /> /// <backColor value="Red" /> /// <attrs value="Bright" /> /// </mapping> /// <mapping> /// <level value="DEBUG" /> /// <backColor value="Green" /> /// </mapping> /// /// /// The Level is the standard log4net logging level and ForeColor and BackColor can be any /// combination of: /// /// Bluecolor is blue /// Greencolor is red /// Redcolor is green /// Whitecolor is white /// Yellowcolor is yellow /// Purplecolor is purple /// Cyancolor is cyan /// /// /// /// The attributes can be any of the following: /// /// Brightforeground is brighter /// Dimforeground is dimmer /// Underscoremessage is underlines /// Blinkforeground is blinking (does not work on all terminals) /// Reverseforeground and background are reversed /// Hiddenoutput is hidden /// Strikethroughmessage has a line through it /// /// /// This code is based heavily on the code for ColoredConsoleAppender by Rick Hobbs and Nicko Cadell. /// /// /// Patrick Wagstrom public class AnsiColoredConsoleAppender : AppenderSkeleton { #region Colors Enum /// /// The enum of possible color values for use with the color mapping method /// [Flags] public enum Attributes : int { Reset = 0, Bright = 1, Dim = 2, Underscore = 4, Blink = 5, Reverse = 7, Hidden = 8, Strikethrough = 9 } [Flags] public enum Foregrounds : int { Black = 30, Red = 31, Green = 32, Yellow = 33, Blue = 34, Magenta = 35, Cyan = 36, White = 37 } [Flags] public enum Backgrounds : int { Black = 40, Red = 41, Green = 42, Yellow = 43, Blue = 44, Magenta = 45, Cyan = 46, White = 47 } #endregion #region Public Instance Constructors /// /// Initializes a new instance of the class. /// /// /// The instance of the class is set up to write /// to the standard output stream. /// public AnsiColoredConsoleAppender() { } /// /// Initializes a new instance of the class /// with the specified layout. /// /// the layout to use for this appender /// /// The instance of the class is set up to write /// to the standard output stream. /// public AnsiColoredConsoleAppender(ILayout layout) : this(layout, false) { } /// /// Initializes a new instance of the class /// with the specified layout. /// /// the layout to use for this appender /// flag set to true to write to the console error stream /// /// When is set to true, output is written to /// the standard error output stream. Otherwise, output is written to the standard /// output stream. /// public AnsiColoredConsoleAppender(ILayout layout, bool writeToErrorStream) { Layout = layout; m_writeToErrorStream = writeToErrorStream; } #endregion Public Instance Constructors #region Public Instance Properties /// /// Target is the value of the console output stream. /// This is either "Console.Out" or "Console.Error". /// /// /// Target is the value of the console output stream. /// This is either "Console.Out" or "Console.Error". /// virtual public string Target { get { return m_writeToErrorStream ? ConsoleError : ConsoleOut; } set { string v = value.Trim(); if (string.Compare(ConsoleError, v, true, CultureInfo.InvariantCulture) == 0) { m_writeToErrorStream = true; } else { m_writeToErrorStream = false; } } } /// /// Add a mapping of level to color - done by the config file /// /// The mapping to add public void AddMapping(AnsiColoredConsoleAppenderLevelColorMapping mapping) { // ushort usMapping = (ushort)((int)mapping.ForeColor + (((int)mapping.BackColor) << 4) ); m_Level2ColorMap[mapping.Level] = mapping; } /// /// A class to act as a mapping between the level that a logging call is made at and /// the color it should be displayed as. /// public class AnsiColoredConsoleAppenderLevelColorMapping { private log4net.Core.Level m_level; private Attributes m_foreAttributes; private Foregrounds m_foreColor; private Backgrounds m_backColor; /// /// The level to map to a color /// public log4net.Core.Level Level { get { return m_level; } set { m_level = value; } } /// /// The mapped foreground color for the specified level /// public Foregrounds ForeColor { get { return m_foreColor; } set { m_foreColor = value; } } /// /// The mapped background color for the specified level /// public Backgrounds BackColor { get { return m_backColor; } set { m_backColor = value; } } /// /// The mapped attributes for the specified level /// public Attributes Attrs { get { return m_foreAttributes; } set { m_foreAttributes = value; } } /// /// The Ansi color codes for this object /// public string AnsiCodes { get { return "\x1b[" + (int)m_backColor + ";" + (int)m_foreAttributes + ";" + (int)m_foreColor + "m"; } } } #endregion Public Instance Properties #region Override implementation of AppenderSkeleton /// /// This method is called by the method. /// /// The event to log. /// /// /// Writes the event to the console. /// /// /// The format of the output will depend on the appender's layout. /// /// override protected void Append(log4net.Core.LoggingEvent loggingEvent) { string postEventCodes = "\x1b[0m"; string eventCodes = ""; // see if there is a lookup. AnsiColoredConsoleAppenderLevelColorMapping colLookup = (AnsiColoredConsoleAppenderLevelColorMapping)m_Level2ColorMap[loggingEvent.Level]; if(colLookup != null) { eventCodes = colLookup.AnsiCodes; } string loggingMessage = eventCodes + RenderLoggingEvent(loggingEvent); // on most terminals there are weird effects if we don't clear the background color // before the new line. This checks to see if it ends with a newline, and if // so, inserts the clear codes before the newline, otherwise the clear codes // are inserted afterwards. if (loggingMessage[loggingMessage.Length - 1] == '\n') { loggingMessage = loggingMessage.Insert(loggingMessage.Length - 1, postEventCodes); } else { loggingMessage = loggingMessage + postEventCodes; } #if NETCF // Write to the output stream Console.Write(loggingMessage) ) #else if (m_writeToErrorStream) { // Write to the error stream Console.Error.Write(loggingMessage); } else { // Write to the output stream Console.Write(loggingMessage); } #endif } /// /// This appender requires a to be set. /// /// true override protected bool RequiresLayout { get { return true; } } #endregion Override implementation of AppenderSkeleton #region Public Static Fields /// /// The to use when writing to the Console /// standard output stream. /// public const string ConsoleOut = "Console.Out"; /// /// The to use when writing to the Console /// standard error output stream. /// public const string ConsoleError = "Console.Error"; #endregion Public Static Fields #region Private Instances Fields /// /// Flag to write output to the error stream rather than the standard output stream /// private bool m_writeToErrorStream = false; /// /// Mapping from level object to colour value /// private System.Collections.Hashtable m_Level2ColorMap = new System.Collections.Hashtable(); #endregion Private Instances Fields } }