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