You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4net-dev@logging.apache.org by ni...@apache.org on 2005/01/31 21:41:28 UTC

cvs commit: logging-log4net/src/Appender ColoredConsoleAppender.cs

nicko       2005/01/31 12:41:28

  Modified:    src/Appender ColoredConsoleAppender.cs
  Log:
  Updated to support writing more than 30,000 chars. Changed to use Stream that calls WriteFile rather than WriteConsoleW
  
  Revision  Changes    Path
  1.14      +126 -47   logging-log4net/src/Appender/ColoredConsoleAppender.cs
  
  Index: ColoredConsoleAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/ColoredConsoleAppender.cs,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- ColoredConsoleAppender.cs	17 Jan 2005 20:18:41 -0000	1.13
  +++ ColoredConsoleAppender.cs	31 Jan 2005 20:41:28 -0000	1.14
  @@ -64,7 +64,7 @@
   	/// When configuring the colored console appender, mapping should be
   	/// specified to map a logging level to a color. For example:
   	/// </para>
  -	/// <code lang="XML" excaped="true">
  +	/// <code lang="XML" escaped="true">
   	/// <mapping>
   	/// 	<level value="ERROR" />
   	/// 	<foreColor value="White" />
  @@ -240,7 +240,7 @@
   		/// <remarks>
   		/// <para>
   		/// Add a <see cref="LevelColors"/> mapping to this appender.
  -		/// Each mapping defines the foreground and background colours
  +		/// Each mapping defines the foreground and background colors
   		/// for a level.
   		/// </para>
   		/// </remarks>
  @@ -267,48 +267,86 @@
   		/// </remarks>
   		override protected void Append(log4net.Core.LoggingEvent loggingEvent) 
   		{
  -			IntPtr consoleHandle = IntPtr.Zero;
  -			if (m_writeToErrorStream)
  -			{
  -				// Write to the error stream
  -				consoleHandle = GetStdHandle(STD_ERROR_HANDLE);
  -			}
  -			else
  +			if (m_consoleOutputWriter != null)
   			{
  -				// Write to the output stream
  -				consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
  -			}
  +				IntPtr consoleHandle = IntPtr.Zero;
  +				if (m_writeToErrorStream)
  +				{
  +					// Write to the error stream
  +					consoleHandle = GetStdHandle(STD_ERROR_HANDLE);
  +				}
  +				else
  +				{
  +					// Write to the output stream
  +					consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
  +				}
   
  -			// set the output parameters
  -			// Default to white on black
  -			ushort colorInfo = (ushort)Colors.White;
  -
  -			// see if there is a specified lookup.
  -			LevelColors levelColors = m_levelMapping.Lookup(loggingEvent.Level) as LevelColors;
  -			if (levelColors != null)
  -			{
  -				colorInfo = levelColors.CombinedColor;
  -			}
  +				// Default to white on black
  +				ushort colorInfo = (ushort)Colors.White;
  +
  +				// see if there is a specified lookup
  +				LevelColors levelColors = m_levelMapping.Lookup(loggingEvent.Level) as LevelColors;
  +				if (levelColors != null)
  +				{
  +					colorInfo = levelColors.CombinedColor;
  +				}
  +
  +				// Render the event to a string
  +				string strLoggingMessage = RenderLoggingEvent(loggingEvent);
   
  -			// get the current console color.
  -			CONSOLE_SCREEN_BUFFER_INFO bufferInfo;
  -			GetConsoleScreenBufferInfo(consoleHandle, out bufferInfo);
  -
  -			// set the console.
  -			SetConsoleTextAttribute(consoleHandle, colorInfo);
  -
  -			string strLoggingMessage = RenderLoggingEvent(loggingEvent);
  -
  -			// write the output.
  -			UInt32 ignoreWrittenCount = 0;
  -			WriteConsoleW(	consoleHandle,
  -							strLoggingMessage,
  -							(UInt32)strLoggingMessage.Length,
  -							out (UInt32)ignoreWrittenCount,
  -							IntPtr.Zero);
  +				// get the current console color - to restore later
  +				CONSOLE_SCREEN_BUFFER_INFO bufferInfo;
  +				GetConsoleScreenBufferInfo(consoleHandle, out bufferInfo);
  +
  +				// set the console colors
  +				SetConsoleTextAttribute(consoleHandle, colorInfo);
  +
  +				// Using WriteConsoleW seems to be unreliable.
  +				// If a large buffer is written, say 15,000 chars
  +				// Followed by a larger buffer, say 20,000 chars
  +				// then WriteConsoleW will fail, last error 8
  +				// 'Not enough storage is available to process this command.'
  +				// 
  +				// Although the documentation states that the buffer must
  +				// be less that 64KB (i.e. 32,000 WCHARs) the longest string
  +				// that I can write out a the first call to WriteConsoleW
  +				// is only 30,704 chars.
  +				//
  +				// Unlike the WriteFile API the WriteConsoleW method does not 
  +				// seem to be able to partially write out from the input buffer.
  +				// It does have a lpNumberOfCharsWritten parameter, but this is
  +				// either the length of the input buffer if any output was written,
  +				// or 0 when an error occurs.
  +				//
  +				// All results above were observed on Windows XP SP1 running
  +				// .NET runtime 1.1 SP1.
  +				//
  +				// Old call to WriteConsoleW:
  +				//
  +				// WriteConsoleW(
  +				//     consoleHandle,
  +				//     strLoggingMessage,
  +				//     (UInt32)strLoggingMessage.Length,
  +				//     out (UInt32)ignoreWrittenCount,
  +				//     IntPtr.Zero);
  +				//
  +				// Instead of calling WriteConsoleW we use WriteFile which 
  +				// handles large buffers correctly. Because WriteFile does not
  +				// handle the codepage conversion as WriteConsoleW does we 
  +				// need to use a System.IO.StreamWriter with the appropriate
  +				// Encoding. The WriteFile calls are wrapped up in the
  +				// System.IO.__ConsoleStream internal class obtained through
  +				// the System.Console.OpenStandardOutput method.
  +				//
  +				// See the ActivateOptions method below for the code that
  +				// retrieves and wraps the stream.
   
  -			// reset the console back to its previous color scheme.
  -			SetConsoleTextAttribute(consoleHandle, bufferInfo.wAttributes);
  +				// Write to the output stream
  +				m_consoleOutputWriter.Write(strLoggingMessage);
  +
  +				// Restore the console back to its previous color scheme
  +				SetConsoleTextAttribute(consoleHandle, bufferInfo.wAttributes);
  +			}
   		}
   
   		/// <summary>
  @@ -337,6 +375,34 @@
   		{
   			base.ActivateOptions();
   			m_levelMapping.ActivateOptions();
  +
  +			System.IO.Stream consoleOutputStream = null;
  +
  +			// Use the Console methods to open a Stream over the console std handle
  +			if (m_writeToErrorStream)
  +			{
  +				// Write to the error stream
  +				consoleOutputStream = Console.OpenStandardError();
  +			}
  +			else
  +			{
  +				// Write to the output stream
  +				consoleOutputStream = Console.OpenStandardOutput();
  +			}
  +
  +			// Lookup the codepage encoding for the console
  +			System.Text.Encoding consoleEncoding = System.Text.Encoding.GetEncoding(GetConsoleOutputCP());
  +
  +			// Create a writer around the console stream
  +			m_consoleOutputWriter = new System.IO.StreamWriter(consoleOutputStream, consoleEncoding, 0x100);
  +
  +			m_consoleOutputWriter.AutoFlush = true;
  +
  +			// SuppressFinalize on m_consoleOutputWriter because all it will do is flush
  +			// and close the file handle. Because we have set AutoFlush the additional flush
  +			// is not required. The console file handle should not be closed, so we don't call
  +			// Dispose, Close or the finalizer.
  +			GC.SuppressFinalize(m_consoleOutputWriter);
   		}
   
   		#endregion // Override implementation of AppenderSkeleton
  @@ -381,11 +447,24 @@
   		/// </summary>
   		private LevelMapping m_levelMapping = new LevelMapping();
   
  +		/// <summary>
  +		/// The console output stream writer to write to
  +		/// </summary>
  +		/// <remarks>
  +		/// <para>
  +		/// This writer is not thread safe.
  +		/// </para>
  +		/// </remarks>
  +		private System.IO.StreamWriter m_consoleOutputWriter = null;
  +
   		#endregion // Private Instances Fields
   
   		#region Win32 Methods
   
   		[DllImport("Kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
  +		private static extern int GetConsoleOutputCP();
  +
  +		[DllImport("Kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
   		private static extern bool SetConsoleTextAttribute(
   			IntPtr consoleHandle,
   			ushort attributes);
  @@ -395,13 +474,13 @@
   			IntPtr consoleHandle,
   			out CONSOLE_SCREEN_BUFFER_INFO bufferInfo);
   
  -		[DllImport("Kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
  -		private static extern bool WriteConsoleW(
  -			IntPtr hConsoleHandle,
  -			[MarshalAs(UnmanagedType.LPWStr)] string strBuffer,
  -			UInt32 bufferLen,
  -			out UInt32 written,
  -			IntPtr reserved);
  +//		[DllImport("Kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
  +//		private static extern bool WriteConsoleW(
  +//			IntPtr hConsoleHandle,
  +//			[MarshalAs(UnmanagedType.LPWStr)] string strBuffer,
  +//			UInt32 bufferLen,
  +//			out UInt32 written,
  +//			IntPtr reserved);
   
   		//private const UInt32 STD_INPUT_HANDLE = unchecked((UInt32)(-10));
   		private const UInt32 STD_OUTPUT_HANDLE = unchecked((UInt32)(-11));