| #region Apache License |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to you under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| #endregion |
| |
| using System; |
| using System.Text; |
| using System.Globalization; |
| |
| using log4net.Core; |
| using log4net.Layout; |
| using log4net.Util; |
| |
| namespace log4net.Appender |
| { |
| /// <summary> |
| /// Appends logging events to the terminal using ANSI color escape sequences. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// AnsiColorTerminalAppender 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 level of message to be set. |
| /// </para> |
| /// <note> |
| /// This appender expects the terminal to understand the VT100 control set |
| /// in order to interpret the color codes. If the terminal or console does not |
| /// understand the control codes the behavior is not defined. |
| /// </note> |
| /// <para> |
| /// By default, all output is written to the console's standard output stream. |
| /// The <see cref="Target"/> property can be set to direct the output to the |
| /// error stream. |
| /// </para> |
| /// <para> |
| /// NOTE: This appender writes each message to the <c>System.Console.Out</c> or |
| /// <c>System.Console.Error</c> that is set at the time the event is appended. |
| /// Therefore it is possible to programmatically redirect the output of this appender |
| /// (for example NUnit does this to capture program output). While this is the desired |
| /// behavior of this appender it may have security implications in your application. |
| /// </para> |
| /// <para> |
| /// When configuring the ANSI colored terminal appender, a mapping should be |
| /// specified to map a logging level to a color. For example: |
| /// </para> |
| /// <code lang="XML" escaped="true"> |
| /// <mapping> |
| /// <level value="ERROR" /> |
| /// <foreColor value="White" /> |
| /// <backColor value="Red" /> |
| /// <attributes value="Bright,Underscore" /> |
| /// </mapping> |
| /// <mapping> |
| /// <level value="DEBUG" /> |
| /// <backColor value="Green" /> |
| /// </mapping> |
| /// </code> |
| /// <para> |
| /// The Level is the standard log4net logging level and ForeColor and BackColor can be any |
| /// of the following values: |
| /// <list type="bullet"> |
| /// <item><term>Blue</term><description></description></item> |
| /// <item><term>Green</term><description></description></item> |
| /// <item><term>Red</term><description></description></item> |
| /// <item><term>White</term><description></description></item> |
| /// <item><term>Yellow</term><description></description></item> |
| /// <item><term>Purple</term><description></description></item> |
| /// <item><term>Cyan</term><description></description></item> |
| /// </list> |
| /// These color values cannot be combined together to make new colors. |
| /// </para> |
| /// <para> |
| /// The attributes can be any combination of the following: |
| /// <list type="bullet"> |
| /// <item><term>Bright</term><description>foreground is brighter</description></item> |
| /// <item><term>Dim</term><description>foreground is dimmer</description></item> |
| /// <item><term>Underscore</term><description>message is underlined</description></item> |
| /// <item><term>Blink</term><description>foreground is blinking (does not work on all terminals)</description></item> |
| /// <item><term>Reverse</term><description>foreground and background are reversed</description></item> |
| /// <item><term>Hidden</term><description>output is hidden</description></item> |
| /// <item><term>Strikethrough</term><description>message has a line through it</description></item> |
| /// </list> |
| /// While any of these attributes may be combined together not all combinations |
| /// work well together, for example setting both <i>Bright</i> and <i>Dim</i> attributes makes |
| /// no sense. |
| /// </para> |
| /// </remarks> |
| /// <author>Patrick Wagstrom</author> |
| /// <author>Nicko Cadell</author> |
| public class AnsiColorTerminalAppender : AppenderSkeleton |
| { |
| #region Colors Enum |
| |
| /// <summary> |
| /// The enum of possible display attributes |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// The following flags can be combined together to |
| /// form the ANSI color attributes. |
| /// </para> |
| /// </remarks> |
| /// <seealso cref="AnsiColorTerminalAppender" /> |
| [Flags] |
| public enum AnsiAttributes : int |
| { |
| /// <summary> |
| /// text is bright |
| /// </summary> |
| Bright = 1, |
| /// <summary> |
| /// text is dim |
| /// </summary> |
| Dim = 2, |
| |
| /// <summary> |
| /// text is underlined |
| /// </summary> |
| Underscore = 4, |
| |
| /// <summary> |
| /// text is blinking |
| /// </summary> |
| /// <remarks> |
| /// Not all terminals support this attribute |
| /// </remarks> |
| Blink = 8, |
| |
| /// <summary> |
| /// text and background colors are reversed |
| /// </summary> |
| Reverse = 16, |
| |
| /// <summary> |
| /// text is hidden |
| /// </summary> |
| Hidden = 32, |
| |
| /// <summary> |
| /// text is displayed with a strikethrough |
| /// </summary> |
| Strikethrough = 64 |
| } |
| |
| /// <summary> |
| /// The enum of possible foreground or background color values for |
| /// use with the color mapping method |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// The output can be in one for the following ANSI colors. |
| /// </para> |
| /// </remarks> |
| /// <seealso cref="AnsiColorTerminalAppender" /> |
| public enum AnsiColor : int |
| { |
| /// <summary> |
| /// color is black |
| /// </summary> |
| Black = 0, |
| |
| /// <summary> |
| /// color is red |
| /// </summary> |
| Red = 1, |
| |
| /// <summary> |
| /// color is green |
| /// </summary> |
| Green = 2, |
| |
| /// <summary> |
| /// color is yellow |
| /// </summary> |
| Yellow = 3, |
| |
| /// <summary> |
| /// color is blue |
| /// </summary> |
| Blue = 4, |
| |
| /// <summary> |
| /// color is magenta |
| /// </summary> |
| Magenta = 5, |
| |
| /// <summary> |
| /// color is cyan |
| /// </summary> |
| Cyan = 6, |
| |
| /// <summary> |
| /// color is white |
| /// </summary> |
| White = 7 |
| } |
| |
| #endregion |
| |
| #region Public Instance Constructors |
| |
| /// <summary> |
| /// Initializes a new instance of the <see cref="AnsiColorTerminalAppender" /> class. |
| /// </summary> |
| /// <remarks> |
| /// The instance of the <see cref="AnsiColorTerminalAppender" /> class is set up to write |
| /// to the standard output stream. |
| /// </remarks> |
| public AnsiColorTerminalAppender() |
| { |
| } |
| |
| #endregion Public Instance Constructors |
| |
| #region Public Instance Properties |
| |
| /// <summary> |
| /// Target is the value of the console output stream. |
| /// </summary> |
| /// <value> |
| /// Target is the value of the console output stream. |
| /// This is either <c>"Console.Out"</c> or <c>"Console.Error"</c>. |
| /// </value> |
| /// <remarks> |
| /// <para> |
| /// Target is the value of the console output stream. |
| /// This is either <c>"Console.Out"</c> or <c>"Console.Error"</c>. |
| /// </para> |
| /// </remarks> |
| virtual public string Target |
| { |
| get { return m_writeToErrorStream ? ConsoleError : ConsoleOut; } |
| set |
| { |
| string trimmedTargetName = value.Trim(); |
| |
| if (string.Compare(ConsoleError, trimmedTargetName, true, CultureInfo.InvariantCulture) == 0) |
| { |
| m_writeToErrorStream = true; |
| } |
| else |
| { |
| m_writeToErrorStream = false; |
| } |
| } |
| } |
| |
| /// <summary> |
| /// Add a mapping of level to color |
| /// </summary> |
| /// <param name="mapping">The mapping to add</param> |
| /// <remarks> |
| /// <para> |
| /// Add a <see cref="LevelColors"/> mapping to this appender. |
| /// Each mapping defines the foreground and background colours |
| /// for a level. |
| /// </para> |
| /// </remarks> |
| public void AddMapping(LevelColors mapping) |
| { |
| m_levelMapping.Add(mapping); |
| } |
| |
| #endregion Public Instance Properties |
| |
| #region Override implementation of AppenderSkeleton |
| |
| /// <summary> |
| /// This method is called by the <see cref="AppenderSkeleton.DoAppend(LoggingEvent)"/> method. |
| /// </summary> |
| /// <param name="loggingEvent">The event to log.</param> |
| /// <remarks> |
| /// <para> |
| /// Writes the event to the console. |
| /// </para> |
| /// <para> |
| /// The format of the output will depend on the appender's layout. |
| /// </para> |
| /// </remarks> |
| override protected void Append(log4net.Core.LoggingEvent loggingEvent) |
| { |
| string loggingMessage = RenderLoggingEvent(loggingEvent); |
| |
| // see if there is a specified lookup. |
| LevelColors levelColors = m_levelMapping.Lookup(loggingEvent.Level) as LevelColors; |
| if (levelColors != null) |
| { |
| // Prepend the Ansi Color code |
| loggingMessage = levelColors.CombinedColor + loggingMessage; |
| } |
| |
| // 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.Length > 1) |
| { |
| if (loggingMessage.EndsWith("\r\n") || loggingMessage.EndsWith("\n\r")) |
| { |
| loggingMessage = loggingMessage.Insert(loggingMessage.Length - 2, PostEventCodes); |
| } |
| else if (loggingMessage.EndsWith("\n") || loggingMessage.EndsWith("\r")) |
| { |
| loggingMessage = loggingMessage.Insert(loggingMessage.Length - 1, PostEventCodes); |
| } |
| else |
| { |
| loggingMessage = loggingMessage + PostEventCodes; |
| } |
| } |
| else |
| { |
| if (loggingMessage[0] == '\n' || loggingMessage[0] == '\r') |
| { |
| loggingMessage = PostEventCodes + loggingMessage; |
| } |
| else |
| { |
| loggingMessage = loggingMessage + PostEventCodes; |
| } |
| } |
| |
| #if NETCF_1_0 |
| // 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 |
| |
| } |
| |
| /// <summary> |
| /// This appender requires a <see cref="Layout"/> to be set. |
| /// </summary> |
| /// <value><c>true</c></value> |
| /// <remarks> |
| /// <para> |
| /// This appender requires a <see cref="Layout"/> to be set. |
| /// </para> |
| /// </remarks> |
| override protected bool RequiresLayout |
| { |
| get { return true; } |
| } |
| |
| /// <summary> |
| /// Initialize the options for this appender |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// Initialize the level to color mappings set on this appender. |
| /// </para> |
| /// </remarks> |
| public override void ActivateOptions() |
| { |
| base.ActivateOptions(); |
| m_levelMapping.ActivateOptions(); |
| } |
| |
| #endregion Override implementation of AppenderSkeleton |
| |
| #region Public Static Fields |
| |
| /// <summary> |
| /// The <see cref="AnsiColorTerminalAppender.Target"/> to use when writing to the Console |
| /// standard output stream. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// The <see cref="AnsiColorTerminalAppender.Target"/> to use when writing to the Console |
| /// standard output stream. |
| /// </para> |
| /// </remarks> |
| public const string ConsoleOut = "Console.Out"; |
| |
| /// <summary> |
| /// The <see cref="AnsiColorTerminalAppender.Target"/> to use when writing to the Console |
| /// standard error output stream. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// The <see cref="AnsiColorTerminalAppender.Target"/> to use when writing to the Console |
| /// standard error output stream. |
| /// </para> |
| /// </remarks> |
| public const string ConsoleError = "Console.Error"; |
| |
| #endregion Public Static Fields |
| |
| #region Private Instances Fields |
| |
| /// <summary> |
| /// Flag to write output to the error stream rather than the standard output stream |
| /// </summary> |
| private bool m_writeToErrorStream = false; |
| |
| /// <summary> |
| /// Mapping from level object to color value |
| /// </summary> |
| private LevelMapping m_levelMapping = new LevelMapping(); |
| |
| /// <summary> |
| /// Ansi code to reset terminal |
| /// </summary> |
| private const string PostEventCodes = "\x1b[0m"; |
| |
| #endregion Private Instances Fields |
| |
| #region LevelColors LevelMapping Entry |
| |
| /// <summary> |
| /// 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. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// Defines the mapping between a level and the color it should be displayed in. |
| /// </para> |
| /// </remarks> |
| public class LevelColors : LevelMappingEntry |
| { |
| private AnsiColor m_foreColor; |
| private AnsiColor m_backColor; |
| private AnsiAttributes m_attributes; |
| private string m_combinedColor = ""; |
| |
| /// <summary> |
| /// The mapped foreground color for the specified level |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// Required property. |
| /// The mapped foreground color for the specified level |
| /// </para> |
| /// </remarks> |
| public AnsiColor ForeColor |
| { |
| get { return m_foreColor; } |
| set { m_foreColor = value; } |
| } |
| |
| /// <summary> |
| /// The mapped background color for the specified level |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// Required property. |
| /// The mapped background color for the specified level |
| /// </para> |
| /// </remarks> |
| public AnsiColor BackColor |
| { |
| get { return m_backColor; } |
| set { m_backColor = value; } |
| } |
| |
| /// <summary> |
| /// The color attributes for the specified level |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// Required property. |
| /// The color attributes for the specified level |
| /// </para> |
| /// </remarks> |
| public AnsiAttributes Attributes |
| { |
| get { return m_attributes; } |
| set { m_attributes = value; } |
| } |
| |
| /// <summary> |
| /// Initialize the options for the object |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// Combine the <see cref="ForeColor"/> and <see cref="BackColor"/> together |
| /// and append the attributes. |
| /// </para> |
| /// </remarks> |
| public override void ActivateOptions() |
| { |
| base.ActivateOptions(); |
| |
| StringBuilder buf = new StringBuilder(); |
| |
| // Reset any existing codes |
| buf.Append("\x1b[0;"); |
| |
| // set the foreground color |
| buf.Append(30 + (int)m_foreColor); |
| buf.Append(';'); |
| |
| // set the background color |
| buf.Append(40 + (int)m_backColor); |
| |
| // set the attributes |
| if ((m_attributes & AnsiAttributes.Bright) > 0) |
| { |
| buf.Append(";1"); |
| } |
| if ((m_attributes & AnsiAttributes.Dim) > 0) |
| { |
| buf.Append(";2"); |
| } |
| if ((m_attributes & AnsiAttributes.Underscore) > 0) |
| { |
| buf.Append(";4"); |
| } |
| if ((m_attributes & AnsiAttributes.Blink) > 0) |
| { |
| buf.Append(";5"); |
| } |
| if ((m_attributes & AnsiAttributes.Reverse) > 0) |
| { |
| buf.Append(";7"); |
| } |
| if ((m_attributes & AnsiAttributes.Hidden) > 0) |
| { |
| buf.Append(";8"); |
| } |
| if ((m_attributes & AnsiAttributes.Strikethrough) > 0) |
| { |
| buf.Append(";9"); |
| } |
| |
| buf.Append('m'); |
| |
| m_combinedColor = buf.ToString(); |
| } |
| |
| /// <summary> |
| /// The combined <see cref="ForeColor"/>, <see cref="BackColor"/> and |
| /// <see cref="Attributes"/> suitable for setting the ansi terminal color. |
| /// </summary> |
| internal string CombinedColor |
| { |
| get { return m_combinedColor; } |
| } |
| } |
| |
| #endregion // LevelColors LevelMapping Entry |
| } |
| } |