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