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

View: New views
1 Messages — Rating Filter:   Alert me  

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

by rgrabowski :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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