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 rg...@apache.org on 2009/05/25 19:46:43 UTC

svn commit: r778461 - /logging/log4net/trunk/src/Appender/FileAppender.cs

Author: rgrabowski
Date: Mon May 25 17:46:43 2009
New Revision: 778461

URL: http://svn.apache.org/viewvc?rev=778461&view=rev
Log:
Fix for LOG4NET-164. Added MutexLock which allows for faster inter-process file locking compared to MinimalLock.

Modified:
    logging/log4net/trunk/src/Appender/FileAppender.cs

Modified: logging/log4net/trunk/src/Appender/FileAppender.cs
URL: http://svn.apache.org/viewvc/logging/log4net/trunk/src/Appender/FileAppender.cs?rev=778461&r1=778460&r2=778461&view=diff
==============================================================================
--- logging/log4net/trunk/src/Appender/FileAppender.cs (original)
+++ logging/log4net/trunk/src/Appender/FileAppender.cs Mon May 25 17:46:43 2009
@@ -20,7 +20,7 @@
 using System;
 using System.IO;
 using System.Text;
-
+using System.Threading;
 using log4net.Util;
 using log4net.Layout;
 using log4net.Core;
@@ -162,7 +162,7 @@
 			}
 			void IDisposable.Dispose() 
 			{
-				this.Close();
+				Close();
 			}
 			public override void Write(byte[] buffer, int offset, int count) 
 			{
@@ -356,6 +356,56 @@
 				get { return m_appender; }
 				set { m_appender = value; }
 			}
+
+            /// <summary>
+            /// Helper method that creates a FileStream under CurrentAppender's SecurityContext.
+            /// </summary>
+            /// <remarks>
+            /// <para>
+            /// Typically called during OpenFile or AcquireLock. 
+            /// </para>
+            /// <para>
+            /// If the directory portion of the <paramref name="filename"/> does not exist, it is created
+            /// via Directory.CreateDirecctory.
+            /// </para>
+            /// </remarks>
+            /// <param name="filename"></param>
+            /// <param name="append"></param>
+            /// <param name="fileShare"></param>
+            /// <returns></returns>
+            protected Stream CreateStream(string filename, bool append, FileShare fileShare)
+            {
+                using (CurrentAppender.SecurityContext.Impersonate(this))
+                {
+                    // Ensure that the directory structure exists
+                    string directoryFullName = Path.GetDirectoryName(filename);
+
+                    // Only create the directory if it does not exist
+                    // doing this check here resolves some permissions failures
+                    if (!Directory.Exists(directoryFullName))
+                    {
+                        Directory.CreateDirectory(directoryFullName);
+                    }
+
+                    FileMode fileOpenMode = append ? FileMode.Append : FileMode.Create;
+                    return new FileStream(filename, fileOpenMode, FileAccess.Write, FileShare.Read);
+                }
+            }
+
+            /// <summary>
+            /// Helper method to close <paramref name="stream"/> under CurrentAppender's SecurityContext.
+            /// </summary>
+            /// <remarks>
+            /// Does not set <paramref name="stream"/> to null.
+            /// </remarks>
+            /// <param name="stream"></param>
+            protected void CloseStream(Stream stream)
+            {
+                using (CurrentAppender.SecurityContext.Impersonate(this))
+                {
+                    stream.Close();
+                }
+           }
 		}
 
 		/// <summary>
@@ -389,21 +439,7 @@
 			{
 				try
 				{
-					using(CurrentAppender.SecurityContext.Impersonate(this))
-					{
-						// Ensure that the directory structure exists
-						string directoryFullName = Path.GetDirectoryName(filename);
-
-						// Only create the directory if it does not exist
-						// doing this check here resolves some permissions failures
-						if (!Directory.Exists(directoryFullName))
-						{
-							Directory.CreateDirectory(directoryFullName);
-						}
-
-						FileMode fileOpenMode = append ? FileMode.Append : FileMode.Create;
-						m_stream = new FileStream(filename, fileOpenMode, FileAccess.Write, FileShare.Read);
-					}
+                    m_stream = CreateStream(filename, append, FileShare.Read);
 				}
 				catch (Exception e1)
 				{
@@ -421,10 +457,8 @@
 			/// </remarks>
 			public override void CloseFile()
 			{
-				using(CurrentAppender.SecurityContext.Impersonate(this))
-				{
-					m_stream.Close();
-				}
+                CloseStream(m_stream);
+                m_stream = null;
 			}
 
 			/// <summary>
@@ -522,22 +556,7 @@
 				{
 					try
 					{
-						using(CurrentAppender.SecurityContext.Impersonate(this))
-						{
-							// Ensure that the directory structure exists
-							string directoryFullName = Path.GetDirectoryName(m_filename);
-
-							// Only create the directory if it does not exist
-							// doing this check here resolves some permissions failures
-							if (!Directory.Exists(directoryFullName))
-							{
-								Directory.CreateDirectory(directoryFullName);
-							}
-
-							FileMode fileOpenMode = m_append ? FileMode.Append : FileMode.Create;
-							m_stream = new FileStream(m_filename, fileOpenMode, FileAccess.Write, FileShare.Read);
-							m_append=true;
-						}
+                        m_stream = CreateStream(m_filename, m_append, FileShare.Read);
 					}
 					catch (Exception e1)
 					{
@@ -558,14 +577,108 @@
 			/// </remarks>
 			public override void ReleaseLock()
 			{
-				using(CurrentAppender.SecurityContext.Impersonate(this))
-				{
-					m_stream.Close();
-					m_stream=null;
-				}
+                CloseStream(m_stream);
+                m_stream = null;
 			}
 		}
 
+        /// <summary>
+        /// Provides cross-process file locking.
+        /// </summary>
+        /// <author>Ron Grabowski</author>
+        /// <author>Steve Wranovsky</author>
+        public class MutexLock : LockingModelBase
+        {
+            private Mutex m_mutex = null;
+            private bool m_mutexClosed = false;
+            private Stream m_stream = null;
+
+            /// <summary>
+            /// Open the file specified and prepare for logging.
+            /// </summary>
+            /// <param name="filename">The filename to use</param>
+            /// <param name="append">Whether to append to the file, or overwrite</param>
+            /// <param name="encoding">The encoding to use</param>
+            /// <remarks>
+            /// <para>
+            /// Open the file specified and prepare for logging. 
+            /// No writes will be made until <see cref="AcquireLock"/> is called.
+            /// Must be called before any calls to <see cref="AcquireLock"/>,
+            /// -<see cref="ReleaseLock"/> and <see cref="CloseFile"/>.
+            /// </para>
+            /// </remarks>
+            public override void OpenFile(string filename, bool append, Encoding encoding)
+            {
+                try
+                {
+                    m_stream = CreateStream(filename, append, FileShare.ReadWrite);
+
+                    string mutextFriendlyFilename = filename
+                            .Replace("\\", "_")
+                            .Replace(":", "_")
+                            .Replace("/", "_");
+
+                    m_mutex = new Mutex(false, mutextFriendlyFilename); 
+                }
+                catch (Exception e1)
+                {
+                    CurrentAppender.ErrorHandler.Error("Unable to acquire lock on file " + filename + ". " + e1.Message);
+                }
+            }
+
+            /// <summary>
+            /// Close the file
+            /// </summary>
+            /// <remarks>
+            /// <para>
+            /// Close the file. No further writes will be made.
+            /// </para>
+            /// </remarks>
+            public override void CloseFile()
+            {
+                CloseStream(m_stream);
+                m_stream = null;
+
+                m_mutex.ReleaseMutex();
+                m_mutex.Close();
+                m_mutexClosed = true;
+            }
+
+            /// <summary>
+            /// Acquire the lock on the file
+            /// </summary>
+            /// <returns>A stream that is ready to be written to.</returns>
+            /// <remarks>
+            /// <para>
+            /// Does nothing. The lock is already taken
+            /// </para>
+            /// </remarks>
+            public override Stream AcquireLock()
+            {
+                // TODO: add timeout?
+                m_mutex.WaitOne();
+
+                // should always be true (and fast) for FileStream
+                if (m_stream.CanSeek)
+                {
+                    m_stream.Seek(0, SeekOrigin.End);
+                }
+
+                return m_stream;
+            }
+
+            /// <summary>
+            /// 
+            /// </summary>
+            public override void ReleaseLock()
+            {
+                if (m_mutexClosed == false)
+                {
+                    m_mutex.ReleaseMutex();
+                }
+            }
+        }
+
 		#endregion Locking Models
 
 		#region Public Instance Constructors