diff --git a/src/NLog/Internal/FileAppenders/BaseFileAppender.cs b/src/NLog/Internal/FileAppenders/BaseFileAppender.cs index 8ce1f2e4..bd0a9b1f 100644 --- a/src/NLog/Internal/FileAppenders/BaseFileAppender.cs +++ b/src/NLog/Internal/FileAppenders/BaseFileAppender.cs @@ -1,263 +1,263 @@ -// -// Copyright (c) 2004-2011 Jaroslaw Kowalski -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.Internal.FileAppenders -{ - using System; - using System.IO; - using System.Runtime.InteropServices; - using NLog.Common; - using NLog.Config; - using NLog.Internal; - - /// - /// Base class for optimized file appenders. - /// - internal abstract class BaseFileAppender : IDisposable - { - private readonly Random random = new Random(); - - /// - /// Initializes a new instance of the class. - /// - /// Name of the file. - /// The create parameters. - public BaseFileAppender(string fileName, ICreateFileParameters createParameters) - { - this.CreateFileParameters = createParameters; - this.FileName = fileName; - this.OpenTime = CurrentTimeGetter.Now; - this.LastWriteTime = DateTime.MinValue; - } - - /// - /// Gets the name of the file. - /// - /// The name of the file. - public string FileName { get; private set; } - - /// - /// Gets the last write time. - /// - /// The last write time. - public DateTime LastWriteTime { get; private set; } - - /// - /// Gets the open time of the file. - /// - /// The open time. - public DateTime OpenTime { get; private set; } - - /// - /// Gets the file creation parameters. - /// - /// The file creation parameters. - public ICreateFileParameters CreateFileParameters { get; private set; } - - /// - /// Writes the specified bytes. - /// - /// The bytes. - public abstract void Write(byte[] bytes); - - /// - /// Flushes this instance. - /// - public abstract void Flush(); - - /// - /// Closes this instance. - /// - public abstract void Close(); - - /// - /// Gets the file info. - /// - /// The last write time. - /// Length of the file. - /// True if the operation succeeded, false otherwise. - public abstract bool GetFileInfo(out DateTime lastWriteTime, out long fileLength); - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// True to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - this.Close(); - } - } - - /// - /// Records the last write time for a file. - /// - protected void FileTouched() - { - this.LastWriteTime = CurrentTimeGetter.Now; - } - - /// - /// Records the last write time for a file to be specific date. - /// - /// Date and time when the last write occurred. - protected void FileTouched(DateTime dateTime) - { - this.LastWriteTime = dateTime; - } - - /// - /// Creates the file stream. - /// - /// If set to true allow concurrent writes. - /// A object which can be used to write to the file. - protected FileStream CreateFileStream(bool allowConcurrentWrite) - { - int currentDelay = this.CreateFileParameters.ConcurrentWriteAttemptDelay; - - InternalLogger.Trace("Opening {0} with concurrentWrite={1}", this.FileName, allowConcurrentWrite); - for (int i = 0; i < this.CreateFileParameters.ConcurrentWriteAttempts; ++i) - { - try - { - try - { - return this.TryCreateFileStream(allowConcurrentWrite); - } - catch (DirectoryNotFoundException) - { - if (!this.CreateFileParameters.CreateDirs) - { - throw; - } - - Directory.CreateDirectory(Path.GetDirectoryName(this.FileName)); - return this.TryCreateFileStream(allowConcurrentWrite); - } - } - catch (IOException) - { - if (!this.CreateFileParameters.ConcurrentWrites || !allowConcurrentWrite || i + 1 == this.CreateFileParameters.ConcurrentWriteAttempts) - { - throw; // rethrow - } - - int actualDelay = this.random.Next(currentDelay); - InternalLogger.Warn("Attempt #{0} to open {1} failed. Sleeping for {2}ms", i, this.FileName, actualDelay); - currentDelay *= 2; - System.Threading.Thread.Sleep(actualDelay); - } - } - - throw new InvalidOperationException("Should not be reached."); - } - -#if !NET_CF && !SILVERLIGHT - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Objects are disposed elsewhere")] - private FileStream WindowsCreateFile(string fileName, bool allowConcurrentWrite) - { - int fileShare = Win32FileNativeMethods.FILE_SHARE_READ; - - if (allowConcurrentWrite) - { - fileShare |= Win32FileNativeMethods.FILE_SHARE_WRITE; - } - - if (this.CreateFileParameters.EnableFileDelete && PlatformDetector.CurrentOS != RuntimeOS.Windows) - { - fileShare |= Win32FileNativeMethods.FILE_SHARE_DELETE; - } - - IntPtr handle = Win32FileNativeMethods.CreateFile( - fileName, - Win32FileNativeMethods.FileAccess.GenericWrite, - fileShare, - IntPtr.Zero, - Win32FileNativeMethods.CreationDisposition.OpenAlways, - this.CreateFileParameters.FileAttributes, - IntPtr.Zero); - - if (handle.ToInt32() == -1) - { - Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); - } - - var safeHandle = new Microsoft.Win32.SafeHandles.SafeFileHandle(handle, true); - var returnValue = new FileStream(safeHandle, FileAccess.Write, this.CreateFileParameters.BufferSize); - returnValue.Seek(0, SeekOrigin.End); - return returnValue; - } -#endif - - private FileStream TryCreateFileStream(bool allowConcurrentWrite) - { - FileShare fileShare = FileShare.Read; - - if (allowConcurrentWrite) - { - fileShare = FileShare.ReadWrite; - } - -#if !NET_CF - if (this.CreateFileParameters.EnableFileDelete && PlatformDetector.CurrentOS != RuntimeOS.Windows) - { - fileShare |= FileShare.Delete; - } -#endif - -#if !NET_CF && !SILVERLIGHT - if (PlatformDetector.IsDesktopWin32) - { - return this.WindowsCreateFile(this.FileName, allowConcurrentWrite); - } -#endif - - return new FileStream( - this.FileName, - FileMode.Append, - FileAccess.Write, - fileShare, - this.CreateFileParameters.BufferSize); - } - } -} +// +// Copyright (c) 2004-2011 Jaroslaw Kowalski +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Internal.FileAppenders +{ + using System; + using System.IO; + using System.Runtime.InteropServices; + using NLog.Common; + using NLog.Config; + using NLog.Internal; + + /// + /// Base class for optimized file appenders. + /// + internal abstract class BaseFileAppender : IDisposable + { + private readonly Random random = new Random(); + + /// + /// Initializes a new instance of the class. + /// + /// Name of the file. + /// The create parameters. + public BaseFileAppender(string fileName, ICreateFileParameters createParameters) + { + this.CreateFileParameters = createParameters; + this.FileName = fileName; + this.OpenTime = CurrentTimeGetter.Now; + this.LastWriteTime = DateTime.MinValue; + } + + /// + /// Gets the name of the file. + /// + /// The name of the file. + public string FileName { get; private set; } + + /// + /// Gets the last write time. + /// + /// The last write time. + public DateTime LastWriteTime { get; private set; } + + /// + /// Gets the open time of the file. + /// + /// The open time. + public DateTime OpenTime { get; private set; } + + /// + /// Gets the file creation parameters. + /// + /// The file creation parameters. + public ICreateFileParameters CreateFileParameters { get; private set; } + + /// + /// Writes the specified bytes. + /// + /// The bytes. + public abstract void Write(byte[] bytes); + + /// + /// Flushes this instance. + /// + public abstract void Flush(); + + /// + /// Closes this instance. + /// + public abstract void Close(); + + /// + /// Gets the file info. + /// + /// The last write time. + /// Length of the file. + /// True if the operation succeeded, false otherwise. + public abstract bool GetFileInfo(out DateTime lastWriteTime, out long fileLength); + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// True to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + this.Close(); + } + } + + /// + /// Records the last write time for a file. + /// + protected void FileTouched() + { + this.LastWriteTime = CurrentTimeGetter.Now; + } + + /// + /// Records the last write time for a file to be specific date. + /// + /// Date and time when the last write occurred. + protected void FileTouched(DateTime dateTime) + { + this.LastWriteTime = dateTime; + } + + /// + /// Creates the file stream. + /// + /// If set to true allow concurrent writes. + /// A object which can be used to write to the file. + protected FileStream CreateFileStream(bool allowConcurrentWrite) + { + int currentDelay = this.CreateFileParameters.ConcurrentWriteAttemptDelay; + + InternalLogger.Trace("Opening {0} with concurrentWrite={1}", this.FileName, allowConcurrentWrite); + for (int i = 0; i < this.CreateFileParameters.ConcurrentWriteAttempts; ++i) + { + try + { + try + { + return this.TryCreateFileStream(allowConcurrentWrite); + } + catch (DirectoryNotFoundException) + { + if (!this.CreateFileParameters.CreateDirs) + { + throw; + } + + Directory.CreateDirectory(Path.GetDirectoryName(this.FileName)); + return this.TryCreateFileStream(allowConcurrentWrite); + } + } + catch (IOException) + { + if (!this.CreateFileParameters.ConcurrentWrites || !allowConcurrentWrite || i + 1 == this.CreateFileParameters.ConcurrentWriteAttempts) + { + throw; // rethrow + } + + int actualDelay = this.random.Next(currentDelay); + InternalLogger.Warn("Attempt #{0} to open {1} failed. Sleeping for {2}ms", i, this.FileName, actualDelay); + currentDelay *= 2; + System.Threading.Thread.Sleep(actualDelay); + } + } + + throw new InvalidOperationException("Should not be reached."); + } + +#if !NET_CF && !SILVERLIGHT + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Objects are disposed elsewhere")] + private FileStream WindowsCreateFile(string fileName, bool allowConcurrentWrite) + { + int fileShare = Win32FileNativeMethods.FILE_SHARE_READ; + + if (allowConcurrentWrite) + { + fileShare |= Win32FileNativeMethods.FILE_SHARE_WRITE; + } + + if (this.CreateFileParameters.EnableFileDelete && PlatformDetector.CurrentOS != RuntimeOS.Windows) + { + fileShare |= Win32FileNativeMethods.FILE_SHARE_DELETE; + } + + IntPtr handle = Win32FileNativeMethods.CreateFile( + fileName, + Win32FileNativeMethods.FileAccess.GenericWrite, + fileShare, + IntPtr.Zero, + Win32FileNativeMethods.CreationDisposition.OpenAlways, + this.CreateFileParameters.FileAttributes, + IntPtr.Zero); + + if (handle.ToInt32() == -1) + { + Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); + } + + var safeHandle = new Microsoft.Win32.SafeHandles.SafeFileHandle(handle, true); + var returnValue = new FileStream(safeHandle, FileAccess.Write, this.CreateFileParameters.BufferSize); + returnValue.Seek(0, SeekOrigin.End); + return returnValue; + } +#endif + + private FileStream TryCreateFileStream(bool allowConcurrentWrite) + { + FileShare fileShare = FileShare.Read; + + if (allowConcurrentWrite) + { + fileShare = FileShare.ReadWrite; + } + +#if !NET_CF + if (this.CreateFileParameters.EnableFileDelete && PlatformDetector.CurrentOS != RuntimeOS.Windows) + { + fileShare |= FileShare.Delete; + } +#endif + +#if !NET_CF && !SILVERLIGHT + if (PlatformDetector.IsDesktopWin32 && AppDomain.CurrentDomain.IsFullyTrusted) + { + return this.WindowsCreateFile(this.FileName, allowConcurrentWrite); + } +#endif + + return new FileStream( + this.FileName, + FileMode.Append, + FileAccess.Write, + fileShare, + this.CreateFileParameters.BufferSize); + } + } +}