Click here to Skip to main content
15,896,259 members
Articles / Programming Languages / C#

An enhanced wrapper around the LOG4NET logging framework

Rate me:
Please Sign up or sign in to vote.
3.77/5 (15 votes)
26 Mar 2005CPOL7 min read 100.9K   1.1K   57  
An article about an enhanced wrapper around the popular logging framework LOG4NET.
namespace ZetaLogger
{
	#region Using directives.
	// ----------------------------------------------------------------------

	using System;
	using System.Configuration;
	using System.Collections;
	using System.Diagnostics;
	using System.Globalization;
	using System.Net;
	using System.IO;
	using System.Reflection;
	using System.Text.RegularExpressions;
	using System.Web;

	using log4net;
	using log4net.spi;
	using log4net.Appender;
	using log4net.Repository.Hierarchy;

	// ----------------------------------------------------------------------
	#endregion

	/////////////////////////////////////////////////////////////////////////

	/// <summary>
	/// Arguments to the log central event.
	/// </summary>
	/// <remarks>	
	/// Please contact the author Uwe Keim (mailto:uwe.keim@zeta-software.de)
	/// for questions regarding this class.
	/// </remarks>
	public class LogCentralLogEventArgs :
		EventArgs
	{
		#region Constructors.
		// ------------------------------------------------------------------

		/// <summary>
		/// Constructor.
		/// </summary>
		public LogCentralLogEventArgs( 
			LogType type,
			object message,
			Exception t )
		{
			this.type = type;
			this.message = message;
			this.error = t;
		}

		/// <summary>
		/// Constructor.
		/// </summary>
		public LogCentralLogEventArgs( 
			LogType type,
			object message )
		{
			this.type = type;
			this.message = message;
			this.error = null;
		}

		/// <summary>
		/// Constructor.
		/// </summary>
		public LogCentralLogEventArgs( 
			LogType type )
		{
			this.type = type;
			this.message = null;
			this.error = null;
		}

		/// <summary>
		/// Constructor.
		/// </summary>
		public LogCentralLogEventArgs()
		{
			this.type = LogType.Unknown;
			this.message = null;
			this.error = null;
		}

		// ------------------------------------------------------------------
		#endregion

		#region Public properties.
		// ------------------------------------------------------------------

		/// <summary>
		/// The type of the texts to log.
		/// </summary>
		public enum LogType
		{
			/// <summary>
			/// Unknown type.
			/// </summary>
			Unknown,

			/// <summary>
			/// Info.
			/// </summary>
			Info,

			/// <summary>
			/// Error.
			/// </summary>
			Error,

			/// <summary>
			/// Debug.
			/// </summary>
			Debug,

			/// <summary>
			/// Warning.
			/// </summary>
			Warn,

			/// <summary>
			/// Fatal.
			/// </summary>
			Fatal
		}

		/// <summary>
		/// The type of log event.
		/// </summary>
		public LogType Type
		{
			get
			{
				return type;
			}
		}

		/// <summary>
		/// The message that is logged. You cannot modify the
		/// message in your event handler, because the logging
		/// already has occured.
		/// </summary>
		public object Message
		{
			get
			{
				return message;
			}
		}

		/// <summary>
		/// The exception that occured.
		/// </summary>
		public Exception Error
		{
			get
			{
				return error;
			}
		}

		// ------------------------------------------------------------------
		#endregion

		#region Private members.
		// ------------------------------------------------------------------

		/// <summary>
		/// Make them private for read-only.
		/// </summary>
		private readonly LogType type;
		private readonly object message;
		private readonly Exception error;

		// ------------------------------------------------------------------
		#endregion
	}

	/// <summary>
	/// Delegate that is called upon logging from LogXxx().
	/// </summary>
	/// <remarks>	
	/// Please contact the author Uwe Keim (mailto:uwe.keim@zeta-software.de)
	/// for questions regarding this delegate.
	/// </remarks>
	public delegate void LogCentralLogEventHandler( 
	object sender,
	LogCentralLogEventArgs e );

	/////////////////////////////////////////////////////////////////////////

	/// <summary>
	/// Arguments to the event that tries to query additional information
	/// about the environment or the current session info.
	/// </summary>
	/// <remarks>	
	/// Please contact the author Uwe Keim (mailto:uwe.keim@zeta-software.de)
	/// for questions regarding this class.
	/// </remarks>
	public class LogCentralRequestMoreInformationEventArgs :
		LogCentralLogEventArgs
	{
		#region Constructors.
		// ------------------------------------------------------------------

		/// <summary>
		/// Constructor.
		/// </summary>
		public LogCentralRequestMoreInformationEventArgs( 
			LogType type,
			object message,
			Exception t ) :
			base( type, message, t )
		{
		}

		/// <summary>
		/// Constructor.
		/// </summary>
		public LogCentralRequestMoreInformationEventArgs( 
			LogType type,
			object message ) :
			base( type, message )
		{
		}

		/// <summary>
		/// Constructor.
		/// </summary>
		public LogCentralRequestMoreInformationEventArgs( 
			LogType type ) :
			base( type )
		{
		}

		/// <summary>
		/// Constructor.
		/// </summary>
		public LogCentralRequestMoreInformationEventArgs() :
			base()
		{
		}

		// ------------------------------------------------------------------
		#endregion

		#region Public properties.
		// ------------------------------------------------------------------

		/// <summary>
		/// The event handler that gets this object passed can
		/// place additional information here.
		/// </summary>
		public string MoreInformationMessage
		{
			get
			{
				return moreInformationMessage;
			}
			set
			{
				moreInformationMessage = value;
			}
		}

		// ------------------------------------------------------------------
		#endregion

		#region Private member.
		// ------------------------------------------------------------------

		/// <summary>
		/// The event handler that gets this object passed can
		/// place additional information here.
		/// </summary>
		private string moreInformationMessage = null;

		// ------------------------------------------------------------------
		#endregion
	}

	/// <summary>
	/// Delegate that is called when the logging framework tries to query 
	/// additional information about the environment or the current session 
	/// info. Useful e.g. for passing infos about the currently logged in
	/// user or other things.
	/// </summary>
	/// <remarks>	
	/// Please contact the author Uwe Keim (mailto:uwe.keim@zeta-software.de)
	/// for questions regarding this delegate.
	/// </remarks>
	public delegate void LogCentralRequestMoreInformationEventHandler( 
	object sender,
	LogCentralRequestMoreInformationEventArgs e );

	/////////////////////////////////////////////////////////////////////////

	/// <summary>
	/// Central base for logging, so that even library functions
	/// have access to logging functionality.
	/// </summary>
	/// <remarks>	
	/// Please contact the author Uwe Keim (mailto:uwe.keim@zeta-software.de)
	/// for questions regarding this class.
	/// </remarks>
	public class LogCentral
	{
		#region Static member.
		// ------------------------------------------------------------------

		/// <summary>
		/// Access the current instance of the class.
		/// </summary>
		public static LogCentral Current
		{
			get
			{
				lock ( typeof(LogCentral) )
				{
					if ( current==null )
					{
						current = new LogCentral();
						current.ConfigureLogging();
					}

					return current;
				}
			}
		}

		/// <summary>
		/// Can be static here, even for a web application, because
		/// no session-/user-dependent information is stored inside
		/// this class.
		/// </summary>
		private static LogCentral current = null;

		// ------------------------------------------------------------------
		#endregion

		#region Enhanced logging.
		// ------------------------------------------------------------------

		/// <summary>
		/// The event that tries to request more information. This event
		/// is raised before a message is actually being logged. Use this
		/// event to add your own handler to provide more detailed information
		/// to the message being logged.
		/// </summary>
		public event LogCentralRequestMoreInformationEventHandler 
			RequestMoreInformation
		{
			add
			{
				if ( requestMoreInformationEvents==null )
				{
					requestMoreInformationEvents = new ArrayList();
				}

				requestMoreInformationEvents.Add( value );
			}
			remove
			{
				if ( requestMoreInformationEvents!=null )
				{
					if ( requestMoreInformationEvents.IndexOf( value )>=0 )
					{
						requestMoreInformationEvents.Remove( value );
					}
				}
			}
		}

		/// <summary>
		/// Collect the events.
		/// </summary>
		private ArrayList requestMoreInformationEvents;

		/// <summary>
		/// Logging event. This event is raised after
		/// a call to one of the LogDebug, LogError, etc. functions occured.
		/// </summary>
		public event LogCentralLogEventHandler 
			Log = null;

		/// <summary>
		/// Log a debug message.
		/// </summary>
		public void LogDebug( 
			object message )
		{
			// Never allow passwords to be displayed, no matter where.
			message = ProtectPasswords( message);

			bool isInsideLogging = this.isInsideLogging;
			this.isInsideLogging = true;
			try
			{
				if ( !isInsideLogging )
				{
					message = DispatchRequestMoreInformationEvents(
						LogCentralLogEventArgs.LogType.Debug, 
						message );
				}

				logImplementation.Debug( message );
			
				if ( WebTrace!=null )
				{
					WebTrace.Write( "debug", message==null?string.Empty:message.ToString() );
				}

				Trace.WriteLine( MakeTraceMessage( message ), "debug" );

				if ( !isInsideLogging && Log!=null )
				{
					Log( 
						this,
						new LogCentralLogEventArgs( 
						LogCentralLogEventArgs.LogType.Debug, 
						MakeTraceMessage( message ) ) );
				}
			}
			finally
			{
				this.isInsideLogging = false;
			}
		}

		/// <summary>
		/// Log a debug message.
		/// </summary>
		public void LogDebug( 
			object message, 
			Exception t )
		{
			// Never allow passwords to be displayed, no matter where.
			message = ProtectPasswords( message);

			bool isInsideLogging = this.isInsideLogging;
			this.isInsideLogging = true;
			try
			{
				if ( !isInsideLogging )
				{
					message = DispatchRequestMoreInformationEvents(
						LogCentralLogEventArgs.LogType.Debug, 
						message,
						t );
				}

				logImplementation.Debug( message, t );
			
				if ( WebTrace!=null )
				{
					WebTrace.Write( "debug", message==null?string.Empty:message.ToString(), t );
				}

				Trace.WriteLine( MakeTraceMessage( message, t ), "debug" );

				if ( !isInsideLogging && Log!=null )
				{
					Log( 
						this,
						new LogCentralLogEventArgs( 
						LogCentralLogEventArgs.LogType.Debug, 
						MakeTraceMessage( message, t ),
						t ) );
				}
			}
			finally
			{
				this.isInsideLogging = false;
			}
		}

		/// <summary>
		/// Log a fatal message.
		/// </summary>
		public void LogFatal( 
			object message )
		{
			// Never allow passwords to be displayed, no matter where.
			message = ProtectPasswords( message);

			bool isInsideLogging = this.isInsideLogging;
			this.isInsideLogging = true;
			try
			{
				if ( !isInsideLogging )
				{
					message = DispatchRequestMoreInformationEvents(
						LogCentralLogEventArgs.LogType.Fatal, 
						message );
				}

				logImplementation.Fatal( message );
			
				if ( WebTrace!=null )
				{
					WebTrace.Write( "fatal", message==null?string.Empty:message.ToString() );
				}

				Trace.WriteLine( MakeTraceMessage( message ), "fatal" );

				if ( !isInsideLogging && Log!=null )
				{
					Log( 
						this,
						new LogCentralLogEventArgs( 
						LogCentralLogEventArgs.LogType.Fatal, 
						MakeTraceMessage( message ) ) );
				}
			}
			finally
			{
				this.isInsideLogging = false;
			}
		}

		/// <summary>
		/// Log a fatal message.
		/// </summary>
		public void LogFatal( 
			object message, 
			Exception t )
		{
			// Never allow passwords to be displayed, no matter where.
			message = ProtectPasswords( message);

			bool isInsideLogging = this.isInsideLogging;
			this.isInsideLogging = true;
			try
			{
				if ( !isInsideLogging )
				{
					message = DispatchRequestMoreInformationEvents(
						LogCentralLogEventArgs.LogType.Fatal, 
						message,
						t );
				}

				logImplementation.Fatal( message, t );
			
				if ( WebTrace!=null )
				{
					WebTrace.Write( "fatal", message==null?string.Empty:message.ToString(), t );
				}

				Trace.WriteLine( MakeTraceMessage( message, t ), "fatal" );

				if ( !isInsideLogging && Log!=null )
				{
					Log( 
						this,
						new LogCentralLogEventArgs( 
						LogCentralLogEventArgs.LogType.Fatal, 
						MakeTraceMessage( message, t ),
						t ) );
				}
			}
			finally
			{
				this.isInsideLogging = false;
			}
		}

		/// <summary>
		/// Log an error message.
		/// </summary>
		public void LogError( 
			object message )
		{
			// Never allow passwords to be displayed, no matter where.
			message = ProtectPasswords( message);

			bool isInsideLogging = this.isInsideLogging;
			this.isInsideLogging = true;
			try
			{
				if ( !isInsideLogging )
				{
					message = DispatchRequestMoreInformationEvents(
						LogCentralLogEventArgs.LogType.Error, 
						message );
				}

				logImplementation.Error( message );
			
				if ( WebTrace!=null )
				{
					WebTrace.Write( "error", message==null?string.Empty:message.ToString() );
				}

				Trace.WriteLine( MakeTraceMessage( message ), "error" );

				if ( !isInsideLogging && Log!=null )
				{
					Log( 
						this,
						new LogCentralLogEventArgs( 
						LogCentralLogEventArgs.LogType.Error, 
						MakeTraceMessage( message ) ) );
				}
			}
			finally
			{
				this.isInsideLogging = false;
			}
		}

		/// <summary>
		/// Log an error message.
		/// </summary>
		public void LogError( 
			object message, 
			Exception t )
		{
			// Never allow passwords to be displayed, no matter where.
			message = ProtectPasswords( message);

			bool isInsideLogging = this.isInsideLogging;
			this.isInsideLogging = true;
			try
			{
				if ( !isInsideLogging )
				{
					message = DispatchRequestMoreInformationEvents(
						LogCentralLogEventArgs.LogType.Error, 
						message,
						t );
				}

				logImplementation.Error( message, t );
			
				if ( WebTrace!=null )
				{
					WebTrace.Write( "error", message==null?string.Empty:message.ToString(), t );
				}

				Trace.WriteLine( MakeTraceMessage( message, t ), "error" );

				if ( !isInsideLogging && Log!=null )
				{
					Log( 
						this,
						new LogCentralLogEventArgs( 
						LogCentralLogEventArgs.LogType.Error, 
						MakeTraceMessage( message, t ),
						t ) );
				}
			}
			finally
			{
				this.isInsideLogging = false;
			}
		}

		/// <summary>
		/// Log an info message.
		/// </summary>
		public void LogInfo( 
			object message )
		{
			// Never allow passwords to be displayed, no matter where.
			message = ProtectPasswords( message);

			bool isInsideLogging = this.isInsideLogging;
			this.isInsideLogging = true;
			try
			{
				if ( !isInsideLogging )
				{
					message = DispatchRequestMoreInformationEvents(
						LogCentralLogEventArgs.LogType.Info, 
						message );
				}

				logImplementation.Info( message );
			
				if ( WebTrace!=null )
				{
					WebTrace.Write( "info", message==null?string.Empty:message.ToString() );
				}

				Trace.WriteLine( MakeTraceMessage( message ), "info" );

				if ( !isInsideLogging && Log!=null )
				{
					Log( 
						this,
						new LogCentralLogEventArgs( 
						LogCentralLogEventArgs.LogType.Info, 
						MakeTraceMessage( message ) ) );
				}
			}
			finally
			{
				this.isInsideLogging = false;
			}
		}

		/// <summary>
		/// Log an info message.
		/// </summary>
		public void LogInfo( 
			object message, 
			Exception t )
		{
			// Never allow passwords to be displayed, no matter where.
			message = ProtectPasswords( message);

			bool isInsideLogging = this.isInsideLogging;
			this.isInsideLogging = true;
			try
			{
				if ( !isInsideLogging )
				{
					message = DispatchRequestMoreInformationEvents(
						LogCentralLogEventArgs.LogType.Info, 
						message,
						t );
				}

				logImplementation.Info( message, t );
			
				if ( WebTrace!=null )
				{
					WebTrace.Write( "info", message==null?string.Empty:message.ToString(), t );
				}

				Trace.WriteLine( MakeTraceMessage( message, t ), "info" );

				if ( !isInsideLogging && Log!=null )
				{
					Log( 
						this,
						new LogCentralLogEventArgs( 
						LogCentralLogEventArgs.LogType.Info, 
						MakeTraceMessage( message, t ),
						t ) );
				}
			}
			finally
			{
				this.isInsideLogging = false;
			}
		}

		/// <summary>
		/// Log a warning message.
		/// </summary>
		public void LogWarn( 
			object message )
		{
			// Never allow passwords to be displayed, no matter where.
			message = ProtectPasswords( message);

			bool isInsideLogging = this.isInsideLogging;
			this.isInsideLogging = true;
			try
			{
				if ( !isInsideLogging )
				{
					message = DispatchRequestMoreInformationEvents(
						LogCentralLogEventArgs.LogType.Warn, 
						message );
				}

				logImplementation.Warn( message );
			
				if ( WebTrace!=null )
				{
					WebTrace.Write( "warn", message==null?string.Empty:message.ToString() );
				}

				Trace.WriteLine( MakeTraceMessage( message ), "warn" );

				if ( !isInsideLogging && Log!=null )
				{
					Log( 
						this,
						new LogCentralLogEventArgs( 
						LogCentralLogEventArgs.LogType.Warn, 
						MakeTraceMessage( message ) ) );
				}
			}
			finally
			{
				this.isInsideLogging = false;
			}
		}

		/// <summary>
		/// Log a warning message.
		/// </summary>
		public void LogWarn( 
			object message, 
			Exception t )
		{
			// Never allow passwords to be displayed, no matter where.
			message = ProtectPasswords( message);

			bool isInsideLogging = this.isInsideLogging;
			this.isInsideLogging = true;
			try
			{
				if ( !isInsideLogging )
				{
					message = DispatchRequestMoreInformationEvents(
						LogCentralLogEventArgs.LogType.Warn, 
						message,
						t );
				}

				logImplementation.Warn( message, t );
			
				if ( WebTrace!=null )
				{
					WebTrace.Write( "warn", message==null?string.Empty:message.ToString(), t );
				}

				Trace.WriteLine( MakeTraceMessage( message, t ), "warn" );

				if ( !isInsideLogging && Log!=null )
				{
					Log( 
						this,
						new LogCentralLogEventArgs( 
						LogCentralLogEventArgs.LogType.Warn, 
						MakeTraceMessage( message, t ),
						t ) );
				}
			}
			finally
			{
				this.isInsideLogging = false;
			}
		}

		// ------------------------------------------------------------------
		#endregion

		#region Logging.
		// ------------------------------------------------------------------

		/// <summary>
		/// Constructor.
		/// </summary>
		private LogCentral()
		{
			// Add default handlers.
			this.RequestMoreInformation += 
				new LogCentralRequestMoreInformationEventHandler(
				LogCentral_RequestMoreInformationHttp );
			this.RequestMoreInformation += 
				new LogCentralRequestMoreInformationEventHandler(
				LogCentral_RequestMoreInformationNetworkEnvironment );
			this.RequestMoreInformation += 
				new LogCentralRequestMoreInformationEventHandler(
				LogCentral_RequestMoreInformationEnvironment );
			this.RequestMoreInformation += 
				new LogCentralRequestMoreInformationEventHandler(
				LogCentral_RequestMoreInformationAssembly );
		}

		/// <summary>
		/// The actual connection to LOG4NET.
		/// </summary>
		private ILog logImplementation = LogManager.GetLogger( 
			System.Reflection.MethodBase.GetCurrentMethod().DeclaringType );

		/// <summary>
		/// By using this flag, we avoid stack overflow inside the called
		/// event handlers during logging.
		/// </summary>
		private bool isInsideLogging = false;

		/// <summary>
		/// Automatically configures logging.
		/// This function is called automatically, too.
		/// </summary>
		public void ConfigureLogging()
		{
			lock ( this )
			{
				bool configured = false;

				// --

				// Tells the logging system the correct path.
				Assembly a = Assembly.GetEntryAssembly();

				if ( a!=null && a.Location!=null )
				{
					string path = a.Location + ".config";

					if ( File.Exists( path ) )
					{
						log4net.Config.DOMConfigurator.Configure(
							new FileInfo( path ) );
						configurationFilePath = path;
						configured = true;
					}
					else
					{
						path = FindConfigInPath( Path.GetDirectoryName( a.Location ) );
						if ( File.Exists( path ) )
						{
							log4net.Config.DOMConfigurator.Configure(
								new FileInfo( path ) );
							configurationFilePath = path;
							configured = true;
						}
					}
				}

				// --

				// Also, try web.config.
				if ( !configured )
				{
					if ( HttpContext.Current!=null &&
						HttpContext.Current.Server!=null &&
						HttpContext.Current.Request!=null )
					{
						string path = HttpContext.Current.Server.MapPath( 
							HttpContext.Current.Request.ApplicationPath );

						path = path.TrimEnd( '\\' ) + "\\Web.config";

						if ( File.Exists( path ) )
						{
							log4net.Config.DOMConfigurator.Configure(
								new FileInfo( path ) );
							configurationFilePath = path;
							configured = true;
						}
					}
				}

				// --

				if ( !configured )
				{
					// Tells the logging system the correct path.
					a = Assembly.GetExecutingAssembly();

					if ( a!=null && a.Location!=null )
					{
						string path = a.Location + ".config";

						if ( File.Exists( path ) )
						{
							log4net.Config.DOMConfigurator.Configure(
								new FileInfo( path ) );
							configurationFilePath = path;
							configured = true;
						}
						else
						{
							path = FindConfigInPath( Path.GetDirectoryName( a.Location ) );
							if ( File.Exists( path ) )
							{
								log4net.Config.DOMConfigurator.Configure(
									new FileInfo( path ) );
								configurationFilePath = path;
								configured = true;
							}
						}
					}
				}

				// --

				if ( !configured )
				{
					// Tells the logging system the correct path.
					a = Assembly.GetCallingAssembly();

					if ( a!=null && a.Location!=null )
					{
						string path = a.Location + ".config";

						if ( File.Exists( path ) )
						{
							log4net.Config.DOMConfigurator.Configure(
								new FileInfo( path ) );
							configurationFilePath = path;
							configured = true;
						}
						else
						{
							path = FindConfigInPath( Path.GetDirectoryName( a.Location ) );
							if ( File.Exists( path ) )
							{
								log4net.Config.DOMConfigurator.Configure(
									new FileInfo( path ) );
								configurationFilePath = path;
								configured = true;
							}
						}
					}
				}

				// --

				if ( !configured )
				{
					// Look in the path where this library is stored.
					a = Assembly.GetAssembly( typeof(LogCentral) );

					if ( a!=null && a.Location!=null )
					{
						string path = FindConfigInPath( Path.GetDirectoryName( a.Location ) );
						if ( File.Exists( path ) )
						{
							log4net.Config.DOMConfigurator.Configure(
								new FileInfo( path ) );
							configurationFilePath = path;
							configured = true;
						}
					}			
				}

				// --
				// Make some changes to the appenders.

				if ( configured )
				{
					// Find all file appenders and make all 
					// relative paths as absolute paths.

					Logger logger = logImplementation.Logger as Logger;

					if ( logger!=null )
					{
						if ( logger.Hierarchy.Root.Appenders!=null )
						{
							foreach ( IAppender appender in 
								logger.Hierarchy.Root.Appenders )
							{
								FileAppender fileAppender =
									appender as FileAppender;

								if ( fileAppender!=null )
								{
									// If a relative path is specified,
									// make absolute.
									if ( !Path.IsPathRooted( fileAppender.File ) )
									{
										/*
										string path = 
											Path.GetFullPath(
											Path.Combine( 
											Path.GetDirectoryName( configurationFilePath ),
											fileAppender.File ) );

										fileAppender.File = path;
										*/
									}
								}
							}
						}
					}
				}
			}	 
		}

		/// <summary>
		/// Get the full path to the configuration file of this
		/// application. Usually a file with ".CONFIG" extension.
		/// </summary>
		public string ConfigurationFilePath
		{
			get
			{
				lock ( this )
				{
					return configurationFilePath;
				}	 
			}
		}

		/// <summary>
		/// Searches for a configuration file in the given path.
		/// </summary>
		private string FindConfigInPath( 
			string path )
		{
			string[] files = Directory.GetFiles( path );

			if ( files!=null && files.Length>0 )
			{
				foreach ( string file in files )
				{
					if ( Path.GetExtension( file ).Trim( '.' ).ToLower(
						CultureInfo.CurrentCulture )=="config" )
					{
						return file;
					}
				}
			}

			// Not found.
			return string.Empty;
		}

		/// <summary>
		/// If called from within a web context, this returns non-null.
		/// </summary>
		private System.Web.TraceContext WebTrace
		{
			get
			{
				if ( System.Web.HttpContext.Current==null )
				{
					return null;
				}
				else
				{
					return System.Web.HttpContext.Current.Trace;
				}
			}
		}

		/// <summary>
		/// The path where the configuration is read from.
		/// This value is set upon a call to ConfigureLogging().
		/// </summary>
		private string configurationFilePath;

		/// <summary>
		/// Combines the more information strings and the regular
		/// message string.
		/// </summary>
		private object CombineMoreInformationMessage( 
			object message,
			string moreInformationMessage )
		{
			if ( moreInformationMessage==null )
			{
				return message;
			}
			else
			{
				if ( message==null )
				{
					return Indent( moreInformationMessage );
				}
				else
				{
					return message.ToString().TrimEnd() +
						"\r\n\r\n" + 
						Indent( moreInformationMessage.Trim() );
				}
			}		
		}

		/// <summary>
		/// Dispatches the RequestMoreInformation event to
		/// all connected event handlers (if any).
		/// </summary>
		private object DispatchRequestMoreInformationEvents(
			LogCentralLogEventArgs.LogType type,
			object message )
		{
			return DispatchRequestMoreInformationEvents(
				type,
				message,
				null );
		}

		/// <summary>
		/// Dispatches the RequestMoreInformation event to
		/// all connected event handlers (if any).
		/// </summary>
		private object DispatchRequestMoreInformationEvents(
			LogCentralLogEventArgs.LogType type,
			object message,
			Exception t )
		{
			if ( requestMoreInformationEvents==null ||
				requestMoreInformationEvents.Count<=0 )
			{
				return message;
			}
			else
			{
				object cumulatedMessage = message;

				foreach ( LogCentralRequestMoreInformationEventHandler evt
							  in requestMoreInformationEvents )
				{
					if ( evt!=null )
					{
						LogCentralRequestMoreInformationEventArgs args = 
							new LogCentralRequestMoreInformationEventArgs( 
							type,
							message,
							t );

						evt( this, args );

						// Yes, add additional information.
						cumulatedMessage = CombineMoreInformationMessage( 
							cumulatedMessage,
							args.MoreInformationMessage );
					}	 
				}

				return cumulatedMessage;
			}		
		}

		// ------------------------------------------------------------------
		#endregion

		#region Pair class.
		// ------------------------------------------------------------------

		/// <summary>
		/// A simple pair, used for name-value pairs.
		/// </summary>
		private class ObjectPair
		{
			/// <summary>
			/// Constructor.
			/// </summary>
			public ObjectPair()
			{
			}

			/// <summary>
			/// Constructor.
			/// </summary>
			public ObjectPair(
				object name )
			{
				this.name = name;
				this.value = name;
			}

			/// <summary>
			/// Constructor.
			/// </summary>
			public ObjectPair(
				object name,
				object val )
			{
				this.name = name;
				this.value = val;
			}

			/// <summary>
			/// Access the name.
			/// </summary>
			public object Name
			{
				get
				{
					return this.name;
				}
				set
				{
					this.name = value;
				}
			}

			/// <summary>
			/// Access the value.
			/// </summary>
			public object Value
			{
				get
				{
					return this.value;
				}
				set
				{
					this.value = value;
				}
			}

			private object name;
			private object value;
		}

		// ------------------------------------------------------------------
		#endregion

		#region Helper.
		// ------------------------------------------------------------------

		/// <summary>
		/// Make a string to trace from a given message and exception.
		/// </summary>
		public static string MakeTraceMessage( 
			Exception t )
		{
			return MakeTraceMessage( null, t );
		}

		/// <summary>
		/// Make a string to trace from a given message and exception.
		/// </summary>
		public static string MakeTraceMessage( 
			object message, 
			Exception t )
		{
			if ( message==null )
			{
				if ( t==null )
				{
					return string.Empty;
				}
				else
				{
					return string.Format(
						CultureInfo.CurrentCulture,
						"{0}\r\n\r\n-----------------\r\n\r\n{1}\r\n\r\n-----------------\r\n\r\n{2}",
						t.GetType()==null||t.GetType().FullName==null?string.Empty:t.GetType().FullName,
						t.Message==null?string.Empty:t.Message.Trim(),
						t.StackTrace==null?string.Empty:t.StackTrace.Trim() );
				}
			}
			else
			{
				if ( t==null )
				{
					return message.ToString();
				}
				else
				{
					// Comment.
					return string.Format(
						CultureInfo.CurrentCulture,
						"{0}\r\n\r\n{1}\r\n\r\n-----------------\r\n\r\n{2}\r\n\r\n-----------------\r\n\r\n{3}",
						message,
						t.GetType()==null||t.GetType().FullName==null?string.Empty:t.GetType().FullName,
						t.Message==null?string.Empty:t.Message.Trim(),
						t.StackTrace==null?string.Empty:t.StackTrace.Trim() );
				}
			}
		}

		/// <summary>
		/// Make a string to trace from a given message.
		/// </summary>
		public static string MakeTraceMessage( 
			object message )
		{
			return message==null?string.Empty:message.ToString();
		}

		/// <summary>
		/// Never allow passwords to be displayed, no matter where.
		/// </summary>
		public static object ProtectPasswords(
			object message )
		{
			if ( message==null || message.ToString().Length<=0 )
			{
				return message;
			}
			else
			{
				string pattern = @"\b(?:password|passwort|kennwort|pwd)\b";

				if ( Regex.IsMatch( 
					message.ToString(), 
					pattern, 
					RegexOptions.IgnoreCase|
					RegexOptions.Singleline ) )
				{
					return string.Format(
						"##### Due to security restrictions (illegal words contained), the message to log has been discarded. The message contained {0} characters.",
						message.ToString().Length );
				}
				else
				{
					return message;
				}
			}	 
		}

		/// <summary>
		/// Indents block of lines.
		/// </summary>
		/// <param name="textToIndent">The textToIndent to indent.</param>
		/// <returns>Returns the indented textToIndent.</returns>
		public static string Indent( 
			string textToIndent )
		{
			return Indent( textToIndent, "    " );
		}

		/// <summary>
		/// Indents block of lines.
		/// </summary>
		/// <param name="textToIndent">The textToIndent to indent.</param>
		/// <param name="linePrefix">The prefix to add before every found line.</param>
		/// <returns>Returns the indented textToIndent.</returns>
		public static string Indent( 
			string textToIndent, 
			string linePrefix )
		{
			if ( textToIndent==null )
			{
				return textToIndent;
			}
			else
			{
				textToIndent = textToIndent.Replace( "\r\n", "\n" );
				textToIndent = textToIndent.Replace( "\r", "\n" );

				if ( textToIndent.IndexOf( '\n' )<0 )
				{
					return linePrefix + textToIndent;
				}
				else
				{
					string[] lines = textToIndent.Split( '\n' );

					string result = string.Empty;

					foreach ( string line in lines )
					{
						if ( result.Length>0 )
						{
							result += "\r\n";
						}

						result += linePrefix + line;
					}

					return result;
				}
			}
		}
		
		// ------------------------------------------------------------------
		#endregion

		#region Information provider.
		// ------------------------------------------------------------------

		/// <summary>
		/// Provide my own handler for basic information.
		/// </summary>
		/// <remarks>
		/// This handler is called when the logging framework wants more
		/// information about the current environment.
		/// </remarks>
		private void LogCentral_RequestMoreInformationHttp(
			object sender,
			LogCentralRequestMoreInformationEventArgs e )
		{
			if ( e.Type==LogCentralLogEventArgs.LogType.Error ||
				e.Type==LogCentralLogEventArgs.LogType.Fatal ||
				e.Type==LogCentralLogEventArgs.LogType.Warn )
			{
				// Provide some HTTP context information.
				if ( HttpContext.Current!=null )
				{	
					e.MoreInformationMessage = string.Empty;

					// --
					// Request object.

					HttpRequest req = HttpContext.Current.Request;

					if ( req!=null )
					{
						string infos = string.Format(
							CultureInfo.CurrentCulture,
							"Absolute URI: {0},\r\n" +
							"Referer URI: {1},\r\n" +
							"User host address: {2},\r\n" +
							"User host name: {3},\r\n" +
							"User agent: {4},\r\n" +
							"HTTP method: {5}."
							,
							req.Url==null||req.Url.AbsoluteUri==null?"(null)":req.Url.AbsoluteUri,
							req.UrlReferrer==null||req.UrlReferrer.AbsoluteUri==null?"(null)":req.UrlReferrer.AbsoluteUri,
							req.UserHostAddress==null?"(null)":req.UserHostAddress,
							req.UserHostName==null?"(null)":req.UserHostName,
							req.UserAgent==null?"(null)":req.UserAgent,
							req.HttpMethod==null?"(null)":req.HttpMethod );

					if ( e.MoreInformationMessage.Length>0 )
						{
							e.MoreInformationMessage += "\r\n";
						}
						e.MoreInformationMessage += infos;
					}

					// --
					// Page object.

					System.Web.UI.Page page = 
						HttpContext.Current.Handler as System.Web.UI.Page;

					if ( page!=null && req!=null )
					{
						string viewStateKey = page.ViewStateUserKey;
						if ( viewStateKey==null || viewStateKey.Length<=0 )
						{
							viewStateKey = "__VIEWSTATE";
						}

						string viewStateValue = null;
						int viewStateValueLength = 0;
						if ( viewStateKey!=null )
						{
							viewStateValue = req[viewStateKey];

							if ( viewStateValue!=null )
							{
								viewStateValueLength = viewStateValue.Length;
							}

							if ( viewStateValue!=null && viewStateValue.Length>200 )
							{
								viewStateValue = viewStateValue.Substring( 0, 200 ) + "... (cut)";
							}
						}

						string infos = string.Format(
							CultureInfo.CurrentCulture,
							"View state key: {0}\r\n" +
							"View state value length: {1} characters\r\n" +
							"View state value: {2}."
							,
							viewStateKey==null?"(null)":viewStateKey,
							viewStateValueLength,
							viewStateValue==null?"(null)":viewStateValue
							);

						if ( e.MoreInformationMessage.Length>0 )
						{
							e.MoreInformationMessage += "\r\n\r\n";
						}
						e.MoreInformationMessage += infos;
					}

					// --
					// Session object.

					System.Web.SessionState.HttpSessionState session = 
						HttpContext.Current.Session;

					if ( session!=null )
					{
						string infos = string.Format(
							CultureInfo.CurrentCulture,
							"Session ID: {0}\r\n" +
							"Is new session: {1}\r\n" +
							"Is cookieless session: {2}\r\n" +
							"Session element count: {3}\r\n" +
							"Session mode: {4}\r\n" +
							"Session timeout: {5}."
							,
							session.SessionID==null?"(null)":session.SessionID,
							session.IsNewSession,
							session.IsCookieless,
							session.Count,
							session.Mode,
							session.Timeout
							);

						if ( e.MoreInformationMessage.Length>0 )
						{
							e.MoreInformationMessage += "\r\n\r\n";
						}
						e.MoreInformationMessage += infos;
					}				
				}
			}
		}

		/// <summary>
		/// Provide my own handler for basic information.
		/// </summary>
		/// <remarks>
		/// This handler is called when the logging framework wants more
		/// information about the current environment.
		/// </remarks>
		private void LogCentral_RequestMoreInformationAssembly(
			object sender,
			LogCentralRequestMoreInformationEventArgs e )
		{
			if ( e.Type==LogCentralLogEventArgs.LogType.Error ||
				e.Type==LogCentralLogEventArgs.LogType.Fatal ||
				e.Type==LogCentralLogEventArgs.LogType.Warn )
			{
				ArrayList assemblies = new ArrayList();
				assemblies.Add( new ObjectPair( "Entry assembly", Assembly.GetEntryAssembly() ) );
				assemblies.Add( new ObjectPair( "Executing assembly", Assembly.GetExecutingAssembly() ) );
				assemblies.Add( new ObjectPair( "Calling assembly", Assembly.GetCallingAssembly() ) );

				string infos = string.Empty;

				foreach ( ObjectPair pair in assemblies )
				{
					string assemblyType = pair.Name.ToString();
					Assembly assembly = pair.Value as Assembly;

					if ( assembly!=null )
					{
						string info = string.Format(
							CultureInfo.CurrentCulture,
							"Assembly type: {0},\r\n" +
							"Assembly full name: {1},\r\n" +
							"Assembly location: {2},\r\n" +
							"Assembly date: {3},\r\n" +
							"Assembly version: {4}."
							,
							assemblyType,
							assembly.FullName==null?"(null)":assembly.FullName,
							assembly.Location==null?"(null)":assembly.Location,
							assembly.Location==null?"(null)":File.GetLastWriteTime( assembly.Location ).ToString( CultureInfo.CurrentCulture ),
							assembly.GetName()==null||assembly.GetName().Version==null?"(null)":assembly.GetName().Version.ToString() );

						if ( infos.Length>0 )
						{
							infos += "\r\n\r\n";
						}

						infos += info;
					}	 
				}

				if ( infos.Length>0 )
				{
					e.MoreInformationMessage = infos;
				}	 
			}
		}

		/// <summary>
		/// Provide my own handler for basic information.
		/// </summary>
		/// <remarks>
		/// This handler is called when the logging framework wants more
		/// information about the current environment.
		/// </remarks>
		private void LogCentral_RequestMoreInformationEnvironment(
			object sender,
			LogCentralRequestMoreInformationEventArgs e )
		{
			if ( e.Type==LogCentralLogEventArgs.LogType.Error ||
				e.Type==LogCentralLogEventArgs.LogType.Fatal ||
				e.Type==LogCentralLogEventArgs.LogType.Warn )
			{
				// Provide some environment context information.

				string infos = string.Format(
					CultureInfo.CurrentCulture,
					"User domain name: {0},\r\n" +
					"User name: {1},\r\n" +
					"Machine name: {2},\r\n" +
					"OS version: {3},\r\n" +
					"CLR version: {4},\r\n" +
					"Command line: {5},\r\n" +
					"Current directory: {6}.",
					Environment.UserDomainName==null?"(null)":Environment.UserDomainName,
					Environment.UserName==null?"(null)":Environment.UserName,
					Environment.MachineName==null?"(null)":Environment.MachineName,
					Environment.OSVersion==null?"(null)":Environment.OSVersion.ToString(),
					Environment.Version==null?"(null)":Environment.Version.ToString(),
					Environment.CommandLine==null?"(null)":Environment.CommandLine,
					Environment.CurrentDirectory==null?"(null)":Environment.CurrentDirectory );

				e.MoreInformationMessage = infos;
			}
		}

		/// <summary>
		/// Provide my own handler for basic information.
		/// </summary>
		/// <remarks>
		/// This handler is called when the logging framework wants more
		/// information about the current environment.
		/// </remarks>
		private void LogCentral_RequestMoreInformationNetworkEnvironment(
			object sender,
			LogCentralRequestMoreInformationEventArgs e )
		{
			if ( e.Type==LogCentralLogEventArgs.LogType.Error ||
				e.Type==LogCentralLogEventArgs.LogType.Fatal ||
				e.Type==LogCentralLogEventArgs.LogType.Warn )
			{
				string hostName = Dns.GetHostName();

				string infos = string.Format(
					CultureInfo.CurrentCulture,
					"Host name: {0}",
					hostName==null?"(null)":hostName );

				if ( hostName!=null )
				{
					IPHostEntry hostEntry = Dns.Resolve( hostName );

					if ( hostEntry!=null )
					{
						foreach ( IPAddress ipAddress in hostEntry.AddressList )
						{
							infos += string.Format(
								CultureInfo.CurrentCulture,
								",\r\nIP address: {0}",
								ipAddress==null?"(null)":ipAddress.ToString() );
						}
					}	 
				}

				infos = infos.TrimEnd().TrimEnd( ',', '.' ).TrimEnd() + ".";

				e.MoreInformationMessage = infos;
			}
		}

		// ------------------------------------------------------------------
		#endregion
	}

	/////////////////////////////////////////////////////////////////////////
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer Zeta Software GmbH
Germany Germany
Uwe does programming since 1989 with experiences in Assembler, C++, MFC and lots of web- and database stuff and now uses ASP.NET and C# extensively, too. He has also teached programming to students at the local university.

➡️ Give me a tip 🙂

In his free time, he does climbing, running and mountain biking. In 2012 he became a father of a cute boy and in 2014 of an awesome girl.

Some cool, free software from us:

Windows 10 Ereignisanzeige  
German Developer Community  
Free Test Management Software - Intuitive, competitive, Test Plans.  
Homepage erstellen - Intuitive, very easy to use.  
Offline-Homepage-Baukasten

Comments and Discussions