nicko       2004/09/18 09:34:16

  Modified:    src/Appender FileAppender.cs RollingFileAppender.cs
                        TextWriterAppender.cs
  Log:
  Added Doug de la Torre's updates to make the FileAppender and 
RollingFileAppender more robust.
  The TextWriterAppender.PrepareWriter method is called for each message logged 
if the Writer has not been set.
  FileAppender uses PrepareWriter to attempt to open the file for each message 
rather than just once at configuration. Once the file has been opened a write 
lock is held.
  RollingFileAppender moves files before deleting them as this reduces the 
number of situations where a file cannot be rolled.
  
  Revision  Changes    Path
  1.6       +85 -33    logging-log4net/src/Appender/FileAppender.cs
  
  Index: FileAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/FileAppender.cs,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- FileAppender.cs   1 Jun 2004 18:34:40 -0000       1.5
  +++ FileAppender.cs   18 Sep 2004 16:34:16 -0000      1.6
  @@ -31,16 +31,35 @@
        /// </summary>
        /// <remarks>
        /// <para>
  -     /// Logging events are sent to the specified file.
  +     /// Logging events are sent to the file specified by
  +     /// the <see cref="File"/> property.
        /// </para>
        /// <para>
  -     /// The file can be opened in either append or
  -     /// overwrite mode.
  +     /// The file can be opened in either append or overwrite mode 
  +     /// by specifying the <see cref="AppendToFile"/> property.
  +     /// If the file path is relative it is taken as relative from 
  +     /// the application base directory. The file encoding can be
  +     /// specified by setting the <see cref="Encoding"/> property.
  +     /// </para>
  +     /// <para>
  +     /// The layout's <see cref="ILayout.Header"/> and <see 
cref="ILayout.Footer"/>
  +     /// values will be written each time the file is opened and closed
  +     /// respectively. If the <see cref="AppendToFile"/> property is <see 
langword="true"/>
  +     /// then the file may contain multiple copies of the header and footer.
  +     /// </para>
  +     /// <para>
  +     /// This appender will first try to open the file for writing when <see 
cref="ActivateOptions"/>
  +     /// is called. This will typically be during configuration.
  +     /// If the file cannot be opened for writing the appender will attempt
  +     /// to open the file again each time a message is logged to the 
appender.
  +     /// If the file cannot be opened for writing when a message is logged 
then
  +     /// the message will be discarded by this appender.
        /// </para>
        /// </remarks>
        /// <author>Nicko Cadell</author>
        /// <author>Gert Driesen</author>
        /// <author>Rodrigo B. de Oliveira</author>
  +     /// <author>Douglas de la Torre</author>
        public class FileAppender : TextWriterAppender
        {
                #region Public Instance Constructors
  @@ -62,7 +81,7 @@
                public FileAppender(ILayout layout, string filename, bool 
append) 
                {
                        Layout = layout;
  -                     OpenFile(filename, append);
  +                     SafeOpenFile(filename, append);
                }
   
                /// <summary>
  @@ -71,7 +90,7 @@
                /// </summary>
                /// <param name="layout">the layout to use with this 
appender</param>
                /// <param name="filename">the full path to the file to write 
to</param>
  -             [Obsolete("Instead use the default constructor and set the 
Layout & File propertes")]
  +             [Obsolete("Instead use the default constructor and set the 
Layout & File properties")]
                public FileAppender(ILayout layout, string filename) : 
this(layout, filename, true)
                {
                }
  @@ -124,6 +143,12 @@
                /// <value>
                /// The <see cref="Encoding"/> used to write to the file.
                /// </value>
  +             /// <remarks>
  +             /// <para>
  +             /// The default encoding set is <see 
cref="System.Text.Encoding.Default"/>
  +             /// which is the encoding for the system's current ANSI code 
page.
  +             /// </para>
  +             /// </remarks>
                public Encoding Encoding
                {
                        get { return m_encoding; }
  @@ -158,21 +183,7 @@
                        base.ActivateOptions();
                        if (m_fileName != null) 
                        {
  -                             // We must cache the params locally because 
OpenFile will call
  -                             // Reset which will clear the class fields. We 
need to remember the
  -                             // values in case of an error.
  -
  -                             string fileName = m_fileName;
  -                             bool appendToFile = m_appendToFile;
  -
  -                             try 
  -                             {
  -                                     OpenFile(fileName, appendToFile);
  -                             }
  -                             catch(Exception e) 
  -                             {
  -                                     
ErrorHandler.Error("OpenFile("+fileName+","+appendToFile+") call failed.", e, 
ErrorCode.FileOpenFailure);
  -                             }
  +                             SafeOpenFile(m_fileName, m_appendToFile);
                        } 
                        else 
                        {
  @@ -194,6 +205,20 @@
                        m_fileName = null;
                }
   
  +             /// <summary>
  +             /// Called to initialize the file writer
  +             /// </summary>
  +             /// <remarks>
  +             /// <para>
  +             /// Will be called for each logged message until the file is
  +             /// successfully opened.
  +             /// </para>
  +             /// </remarks>
  +             override protected void PrepareWriter()
  +             {
  +                     SafeOpenFile(m_fileName, m_appendToFile);
  +             }
  +
                #endregion Override implementation of TextWriterAppender
   
                #region Public Instance Methods
  @@ -211,17 +236,42 @@
                #region Protected Instance Methods
   
                /// <summary>
  -             /// Sets and <i>opens</i> the file where the log output will
  -             /// go. The specified file must be writable.
  +             /// Sets and <i>opens</i> the file where the log output will 
go. The specified file must be writable.
  +             /// </summary>
  +             /// <param name="fileName">The path to the log file</param>
  +             /// <param name="append">If true will append to fileName. 
Otherwise will truncate fileName</param>
  +             /// <remarks>
  +             /// <para>
  +             /// Calls <see cref="OpenFile"/> but guarantees not to throw an 
exception.
  +             /// Errors are passed to the <see 
cref="TextWriterAppender.ErrorHandler"/>.
  +             /// </para>
  +             /// </remarks>
  +             virtual protected void SafeOpenFile(string fileName, bool 
append)
  +             {
  +                     try 
  +                     {
  +                             OpenFile(fileName, append);
  +                     }
  +                     catch(Exception e) 
  +                     {
  +                             
ErrorHandler.Error("OpenFile("+fileName+","+append+") call failed.", e, 
ErrorCode.FileOpenFailure);
  +                     }
  +             }
  +
  +             /// <summary>
  +             /// Sets and <i>opens</i> the file where the log output will 
go. The specified file must be writable.
                /// </summary>
                /// <param name="fileName">The path to the log file</param>
                /// <param name="append">If true will append to fileName. 
Otherwise will truncate fileName</param>
                /// <remarks>
  -             /// <para>If there was already an opened file, then the 
previous file
  -             /// is closed first.</para>
  -             /// 
  -             /// <para>This method will ensure that the directory structure
  -             /// for the <paramref name="fileName"/> specified exists.</para>
  +             /// <para>
  +             /// If there was already an opened file, then the previous file
  +             /// is closed first.
  +             /// </para>
  +             /// <para>
  +             /// This method will ensure that the directory structure
  +             /// for the <paramref name="fileName"/> specified exists.
  +             /// </para>
                /// </remarks>
                virtual protected void OpenFile(string fileName, bool append)
                {
  @@ -231,8 +281,12 @@
   
                                LogLog.Debug("FileAppender: Opening file for 
writing ["+fileName+"] append ["+append+"]");
   
  +                             // Save these for later, allowing retries if 
file open fails
  +                             m_fileName = fileName;
  +                             m_appendToFile = append;
  +
                                // Ensure that the directory structure exists
  -                             string directoryFullName = (new 
FileInfo(fileName)).DirectoryName;
  +                             string directoryFullName = 
Path.GetDirectoryName(fileName);
   
                                // Only create the directory if it does not 
exist
                                // doing this check here resolves some 
permissions failures
  @@ -243,9 +297,6 @@
   
                                SetQWForFiles(new StreamWriter(fileName, 
append, m_encoding));
   
  -                             m_fileName = fileName;
  -                             m_appendToFile = append;
  -
                                WriteHeader();
                        }
                }
  @@ -287,10 +338,11 @@
                                throw new ArgumentNullException("path");
                        }
   
  -                     if (SystemInfo.ApplicationBaseDirectory != null)
  +                     string applicationBaseDirectory = 
SystemInfo.ApplicationBaseDirectory;
  +                     if (applicationBaseDirectory != null)
                        {
                                // Note that Path.Combine will return the 
second path if it is rooted
  -                             return 
Path.GetFullPath(Path.Combine(SystemInfo.ApplicationBaseDirectory, path));
  +                             return 
Path.GetFullPath(Path.Combine(applicationBaseDirectory, path));
                        }
                        return Path.GetFullPath(path);
                }
  
  
  
  1.8       +165 -96   logging-log4net/src/Appender/RollingFileAppender.cs
  
  Index: RollingFileAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/RollingFileAppender.cs,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- RollingFileAppender.cs    7 Jun 2004 01:09:38 -0000       1.7
  +++ RollingFileAppender.cs    18 Sep 2004 16:34:16 -0000      1.8
  @@ -33,31 +33,81 @@
        /// </summary>
        /// <remarks>
        /// <para>
  -     /// RollingFileAppender can function as either or and do both
  -     /// at the same time (making size based rolling files until a data/time 
  -     /// boundary is crossed at which time it rolls all of those files
  -     /// based on the setting for <see cref="RollingStyle"/>.
  +     /// RollingFileAppender can roll log files based on size or date or both
  +     /// depending on the setting of the <see cref="RollingStyle"/> property.
  +     /// When set to <see cref="RollingMode.Size"/> the log file will be 
rolled
  +     /// once its size exceeds the <see cref="MaximumFileSize"/>.
  +     /// When set to <see cref="RollingMode.Date"/> the log file will be 
rolled
  +     /// once the date boundary specified in the <see cref="DatePattern"/> 
property
  +     /// is crossed.
  +     /// When set to <see cref="RollingMode.Composite"/> the log file will be
  +     /// rolled once the date boundary specified in the <see 
cref="DatePattern"/> property
  +     /// is crossed, but within a date boundary the file will also be rolled
  +     /// once its size exceeds the <see cref="MaximumFileSize"/>.
        /// </para>
        /// <para>
  -     /// A of few additional optional features have been added:<br/>
  -     /// -- Attach date pattern for current log file <see 
cref="StaticLogFileName"/><br/>
  -     /// -- Backup number increments for newer files <see 
cref="CountDirection"/><br/>
  -     /// -- Infinite number of backups by file size <see 
cref="MaxSizeRollBackups"/>
  +     /// A of few additional optional features have been added:
  +     /// <list type="bullet">
  +     /// <item>Attach date pattern for current log file <see 
cref="StaticLogFileName"/></item>
  +     /// <item>Backup number increments for newer files <see 
cref="CountDirection"/></item>
  +     /// <item>Infinite number of backups by file size <see 
cref="MaxSizeRollBackups"/></item>
  +     /// </list>
        /// </para>
  +     /// 
  +     /// <note>
        /// <para>
  -     /// A few notes and warnings:  For large or infinite number of backups
  -     /// countDirection &gt; 0 is highly recommended, with staticLogFileName 
= false if
  -     /// time based rolling is also used -- this will reduce the number of 
file renamings
  -     /// to few or none.  Changing staticLogFileName or countDirection 
without clearing
  -     /// the directory could have nasty side effects.  If Date/Time based 
rolling
  -     /// is enabled, CompositeRollingAppender will attempt to roll existing 
files
  -     /// in the directory without a date/time tag based on the last modified 
date
  -     /// of the base log files last modification.
  +     /// For large or infinite numbers of backup files a <see 
cref="CountDirection"/> 
  +     /// greater than zero is highly recommended, otherwise all the backup 
files need
  +     /// to be renamed each time a new backup is created.
        /// </para>
        /// <para>
  -     /// A maximum number of backups based on date/time boundaries would be 
nice
  -     /// but is not yet implemented.
  +     /// When Date/Time based rolling is used setting <see 
cref="StaticLogFileName"/> 
  +     /// to <see langword="true"/> will reduce the number of file renamings 
to few or none.
        /// </para>
  +     /// </note>
  +     /// 
  +     /// <note type="caution">
  +     /// <para>
  +     /// Changing <see cref="StaticLogFileName"/> or <see 
cref="CountDirection"/> without clearing
  +     /// the log file directory of backup files will cause unexpected and 
unwanted side effects.  
  +     /// </para>
  +     /// </note>
  +     /// 
  +     /// <para>
  +     /// If Date/Time based rolling is enabled this appender will attempt to 
roll existing files
  +     /// in the directory without a Date/Time tag based on the last write 
date of the base log file.
  +     /// The appender only rolls the log file when a message is logged. If 
Date/Time based rolling 
  +     /// is enabled then the appender will not roll the log file at the 
Date/Time boundary but
  +     /// at the point when the next message is logged after the boundary has 
been crossed.
  +     /// </para>
  +     /// 
  +     /// <para>
  +     /// The <see cref="RollingFileAppender"/> extends the <see 
cref="FileAppender"/> and
  +     /// has the same behavior when opening the log file.
  +     /// The appender will first try to open the file for writing when <see 
cref="ActivateOptions"/>
  +     /// is called. This will typically be during configuration.
  +     /// If the file cannot be opened for writing the appender will attempt
  +     /// to open the file again each time a message is logged to the 
appender.
  +     /// If the file cannot be opened for writing when a message is logged 
then
  +     /// the message will be discarded by this appender.
  +     /// </para>
  +     /// <para>
  +     /// When rolling a backup file necessitates deleting an older backup 
file the
  +     /// file to be deleted is moved to a temporary name before being 
deleted. 
  +     /// On the Windows platform if another process has a write lock on the 
file 
  +     /// that is to be deleted, but allows shared read access to the file 
then the
  +     /// file can be moved, but cannot be deleted. If the other process also 
allows 
  +     /// shared delete access to the file then the file will be deleted once 
that 
  +     /// process closes the file. If it is necessary to open the log file or 
any
  +     /// of the backup files outside of this appender for either read or 
  +     /// write access please ensure that read and delete share modes are 
enabled.
  +     /// </para>
  +     /// 
  +     /// <note type="caution">
  +     /// <para>
  +     /// A maximum number of backup files when rolling on date/time 
boundaries is not supported.
  +     /// </para>
  +     /// </note>
        /// </remarks>
        /// <author>Nicko Cadell</author>
        /// <author>Gert Driesen</author>
  @@ -212,7 +262,7 @@
                /// before being rolled over to backup files.
                /// </summary>
                /// <value>
  -             /// The maximum size that the output file is allowed to reach 
before being 
  +             /// The maximum size in bytes that the output file is allowed 
to reach before being 
                /// rolled over to backup files.
                /// </value>
                /// <remarks>
  @@ -223,7 +273,7 @@
                /// argument.
                /// </para>
                /// <para>
  -             /// The default maximum file size is 10MB.
  +             /// The default maximum file size is 10MB (10*1024*1024).
                /// </para>
                /// </remarks>
                public long MaxFileSize
  @@ -247,11 +297,16 @@
                /// expressed respectively in kilobytes, megabytes or 
gigabytes. 
                /// </para>
                /// <para>
  -             /// For example, the value "10KB" will be interpreted as 10240.
  +             /// For example, the value "10KB" will be interpreted as 10240 
bytes.
                /// </para>
                /// <para>
                /// The default maximum file size is 10MB.
                /// </para>
  +             /// <para>
  +             /// If you have the option to set the maximum file size 
programmatically
  +             /// consider using the <see cref="MaxFileSize"/> property 
instead as this
  +             /// allows you to set the size in bytes as a <see 
cref="Int64"/>.
  +             /// </para>
                /// </remarks>
                public string MaximumFileSize
                {
  @@ -359,8 +414,7 @@
                /// </para>
                /// <para>
                /// This will make time based rollovers with a large number of 
backups 
  -             /// much faster -- it won't have to
  -             /// rename all the backups!
  +             /// much faster as the appender it won't have to rename all the 
backups!
                /// </para>
                /// </remarks>
                public bool StaticLogFileName
  @@ -450,10 +504,9 @@
                                long currentCount = 0;
                                if (append) 
                                {
  -                                     FileInfo fileInfo = new 
FileInfo(fileName);
  -                                     if (fileInfo.Exists)
  +                                     if (System.IO.File.Exists(fileName))
                                        {
  -                                             currentCount = fileInfo.Length;
  +                                             currentCount = (new 
FileInfo(fileName)).Length;
                                        }
                                }
   
  @@ -486,13 +539,11 @@
                                sName = m_scheduledFilename;
                        }
   
  -                     FileInfo fileInfo = new FileInfo(sName);
  -                     if (null != fileInfo)
  -                     {
  -                             ArrayList arrayFiles = 
GetExistingFiles(fileInfo.FullName);
  -                             InitializeRollBackups((new 
FileInfo(m_baseFileName)).Name, arrayFiles);
  +                     string fullPath = System.IO.Path.GetFullPath(sName);
  +                     string fileName = System.IO.Path.GetFileName(fullPath);
   
  -                     }
  +                     ArrayList arrayFiles = GetExistingFiles(fullPath);
  +                     InitializeRollBackups(fileName, arrayFiles);
   
                        LogLog.Debug("RollingFileAppender: curSizeRollBackups 
starts at ["+m_curSizeRollBackups+"]");
                }
  @@ -518,21 +569,20 @@
                {
                        ArrayList alFiles = new ArrayList();
   
  -                     FileInfo fileInfo = new FileInfo(baseFilePath);
  -                     DirectoryInfo dirInfo = fileInfo.Directory;
  -                     LogLog.Debug("RollingFileAppender: Searching for 
existing files in ["+dirInfo+"]");
  +                     string directory = Path.GetDirectoryName(baseFilePath);
  +                     LogLog.Debug("RollingFileAppender: Searching for 
existing files in ["+directory+"]");
   
  -                     if (dirInfo.Exists)
  +                     if (Directory.Exists(directory))
                        {
  -                             string baseFileName = fileInfo.Name;
  +                             string baseFileName = 
Path.GetFileName(baseFilePath);
   
  -                             FileInfo[] files = 
dirInfo.GetFiles(GetWildcardPatternForFile(baseFileName));
  +                             string[] files = Directory.GetFiles(directory, 
GetWildcardPatternForFile(baseFileName));
        
                                if (files != null)
                                {
                                        for (int i = 0; i < files.Length; i++) 
                                        {
  -                                             string curFileName = 
files[i].Name;
  +                                             string curFileName = 
Path.GetFileName(files[i]);
                                                if 
(curFileName.StartsWith(baseFileName))
                                                {
                                                        
alFiles.Add(curFileName);
  @@ -550,10 +600,9 @@
                {
                        if (m_staticLogFileName && m_rollDate) 
                        {
  -                             FileInfo old = new FileInfo(m_baseFileName);
  -                             if (old.Exists) 
  +                             if (System.IO.File.Exists(m_baseFileName)) 
                                {
  -                                     DateTime last = old.LastWriteTime;
  +                                     DateTime last = 
System.IO.File.GetLastWriteTime(m_baseFileName);
                                        LogLog.Debug("RollingFileAppender: 
["+last.ToString(m_datePattern,System.Globalization.DateTimeFormatInfo.InvariantInfo)+"]
 vs. 
["+m_now.ToString(m_datePattern,System.Globalization.DateTimeFormatInfo.InvariantInfo)+"]");
   
                                        if 
(!(last.ToString(m_datePattern,System.Globalization.DateTimeFormatInfo.InvariantInfo).Equals(m_now.ToString(m_datePattern,
 System.Globalization.DateTimeFormatInfo.InvariantInfo)))) 
  @@ -568,11 +617,18 @@
                }
   
                /// <summary>
  -             /// <para>Initializes based on existing conditions at time of 
<see cref="ActivateOptions"/>.
  -             /// The following is done:</para>
  -             ///             A) determine curSizeRollBackups (only within 
the current roll point)
  -             ///             B) initiates a roll over if needed for crossing 
a date boundary since the last run.
  +             /// Initializes based on existing conditions at time of <see 
cref="ActivateOptions"/>.
                /// </summary>
  +             /// <remarks>
  +             /// <para>
  +             /// Initializes based on existing conditions at time of <see 
cref="ActivateOptions"/>.
  +             /// The following is done
  +             /// <list type="bullet">
  +             ///     <item>determine curSizeRollBackups (only within the 
current roll point)</item>
  +             ///     <item>initiates a roll over if needed for crossing a 
date boundary since the last run.</item>
  +             ///     </list>
  +             ///     </para>
  +             /// </remarks>
                protected void ExistingInit() 
                {
                        DetermineCurSizeRollBackups();
  @@ -663,7 +719,7 @@
                                        LogLog.Debug("RollingFileAppender: File 
name ["+curFileName+"] moves current count to ["+m_curSizeRollBackups+"]");
                                }
                        } 
  -                     catch (FormatException /*e*/) 
  +                     catch(FormatException) 
                        {
                                //this happens when file.log -> 
file.log.yyyy-mm-dd which is normal
                                //when staticLogFileName == false
  @@ -694,7 +750,7 @@
                /// <summary>
                /// Calculates the RollPoint for the datePattern supplied.
                /// </summary>
  -             /// <param name="datePattern">the date pattern to caluculate 
the check period for</param>
  +             /// <param name="datePattern">the date pattern to calculate the 
check period for</param>
                /// <returns>The RollPoint that is most accurate for the date 
pattern supplied</returns>
                /// <remarks>
                /// Essentially the date pattern is examined to determine what 
the
  @@ -706,19 +762,18 @@
                /// </remarks>
                private RollPoint ComputeCheckPeriod(string datePattern) 
                {
  -                     // set date to 1970-01-01 00:00:00 this is 
UniversalSortableDateTimePattern 
  +                     // s_date1970 is 1970-01-01 00:00:00 this is 
UniversalSortableDateTimePattern 
                        // (based on ISO 8601) using universal time. This date 
is used for reference
                        // purposes to calculate the resolution of the date 
pattern.
  -                     DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);
   
                        // Get string representation of base line date
  -                     string r0 = epoch.ToString(datePattern, 
System.Globalization.DateTimeFormatInfo.InvariantInfo);
  +                     string r0 = s_date1970.ToString(datePattern, 
System.Globalization.DateTimeFormatInfo.InvariantInfo);
   
                        // Check each type of rolling mode starting with the 
smallest increment.
                        for(int i = (int)RollPoint.TopOfMinute; i <= 
(int)RollPoint.TopOfMonth; i++) 
                        {
                                // Get string representation of next pattern
  -                             string r1 = NextCheckDate(epoch, 
(RollPoint)i).ToString(datePattern, 
System.Globalization.DateTimeFormatInfo.InvariantInfo);
  +                             string r1 = NextCheckDate(s_date1970, 
(RollPoint)i).ToString(datePattern, 
System.Globalization.DateTimeFormatInfo.InvariantInfo);
   
                                LogLog.Debug("RollingFileAppender: Type = 
["+i+"], r0 = ["+r0+"], r1 = ["+r1+"]");
   
  @@ -799,7 +854,7 @@
                {
                        if (m_staticLogFileName) 
                        {
  -                             /* Compute filename, but only if datePattern is 
specified */
  +                             // Compute filename, but only if datePattern is 
specified
                                if (m_datePattern == null) 
                                {
                                        ErrorHandler.Error("Missing DatePattern 
option in rollOver().");
  @@ -831,22 +886,14 @@
                                RollFile(File, m_scheduledFilename);
                        }
        
  -                     try 
  -                     {
  -                             //We've cleared out the old date and are ready 
for the new
  -                             m_curSizeRollBackups = 0; 
  -       
  -                             //new scheduled name
  -                             m_scheduledFilename = File + 
m_now.ToString(m_datePattern, 
System.Globalization.DateTimeFormatInfo.InvariantInfo);
  +                     //We've cleared out the old date and are ready for the 
new
  +                     m_curSizeRollBackups = 0; 
  +     
  +                     //new scheduled name
  +                     m_scheduledFilename = File + 
m_now.ToString(m_datePattern, 
System.Globalization.DateTimeFormatInfo.InvariantInfo);
   
  -                             // This will also close the file. This is OK 
since multiple
  -                             // close operations are safe.
  -                             this.OpenFile(m_baseFileName, false);
  -                     }
  -                     catch(Exception e) 
  -                     {
  -                             ErrorHandler.Error("setFile(" + File + ", 
false) call failed.", e, ErrorCode.FileOpenFailure);
  -                     }
  +                     // This will also close the file. This is OK since 
multiple close operations are safe.
  +                     SafeOpenFile(m_baseFileName, false);
                }
     
                /// <summary>
  @@ -857,25 +904,20 @@
                /// <param name="toFile">New name for file.</param>
                protected void RollFile(string fromFile, string toFile) 
                {
  -                     FileInfo target = new FileInfo(toFile);
  -                     if (target.Exists) 
  -                     {
  -                             LogLog.Debug("RollingFileAppender: Deleting 
existing target file ["+target+"]");
  -                             target.Delete();
  -                     }
  -     
  -                     FileInfo file = new FileInfo(fromFile);
  -                     if (file.Exists)
  +                     if (System.IO.File.Exists(fromFile))
                        {
  +                             // Delete the toFile if it exists
  +                             DeleteFile(toFile);
  +
                                // We may not have permission to move the file, 
or the file may be locked
                                try
                                {
  -                                     file.MoveTo(toFile);
  -                                     LogLog.Debug("RollingFileAppender: 
Moved [" + fromFile + "] -> [" + toFile + "]");
  +                                     LogLog.Debug("RollingFileAppender: 
Moving [" + fromFile + "] -> [" + toFile + "]");
  +                                     System.IO.File.Move(fromFile, toFile);
                                }
  -                             catch(Exception ex)
  +                             catch(Exception moveEx)
                                {
  -                                     ErrorHandler.Error("Exception while 
rolling file [" + fromFile + "] -> [" + toFile + "]", ex, 
ErrorCode.GenericFailure);
  +                                     ErrorHandler.Error("Exception while 
rolling file [" + fromFile + "] -> [" + toFile + "]", moveEx, 
ErrorCode.GenericFailure);
                                }
                        }
                        else
  @@ -890,18 +932,44 @@
                /// <param name="fileName">The file to delete.</param>
                protected void DeleteFile(string fileName) 
                {
  -                     FileInfo file = new FileInfo(fileName);
  -                     if (file.Exists) 
  +                     if (System.IO.File.Exists(fileName)) 
                        {
                                // We may not have permission to delete the 
file, or the file may be locked
  +
  +                             string fileToDelete = fileName;
  +
  +                             // Try to move the file to temp name.
  +                             // If the file is locked we should still be 
able to move it
  +                             string tempFileName = fileName + "." + 
Environment.TickCount + ".DeletePending";
  +                             try
  +                             {
  +                                     System.IO.File.Move(fileName, 
tempFileName);
  +                                     fileToDelete = tempFileName;
  +                             }
  +                             catch(Exception moveEx)
  +                             {
  +                                     LogLog.Debug("RollingFileAppender: 
Exception while moving file to be deleted [" + fileName + "] -> [" + 
tempFileName + "]", moveEx);
  +                             }
  +
  +                             // Try to delete the file (either the original 
or the moved file)
                                try
                                {
  -                                     file.Delete();
  +                                     System.IO.File.Delete(fileToDelete);
                                        LogLog.Debug("RollingFileAppender: 
Deleted file [" + fileName + "]");
                                }
  -                             catch(Exception ex)
  +                             catch(Exception deleteEx)
                                {
  -                                     ErrorHandler.Error("Exception while 
deleting file [" + fileName + "]", ex, ErrorCode.GenericFailure);
  +                                     if (fileToDelete == fileName)
  +                                     {
  +                                             // Unable to move or delete the 
file
  +                                             ErrorHandler.Error("Exception 
while deleting file [" + fileToDelete + "]", deleteEx, 
ErrorCode.GenericFailure);
  +                                     }
  +                                     else
  +                                     {
  +                                             // Moved the file, but the 
delete failed. File is probably locked.
  +                                             // The file should 
automatically be deleted when the lock is released.
  +                                             
LogLog.Debug("RollingFileAppender: Exception while deleting temp file [" + 
fileToDelete + "]", deleteEx);
  +                                     }
                                }
                        }
                }
  @@ -976,16 +1044,8 @@
                                }
                        }
        
  -                     try 
  -                     {
  -                             // This will also close the file. This is OK 
since multiple
  -                             // close operations are safe.
  -                             this.OpenFile(m_baseFileName, false);
  -                     } 
  -                     catch(Exception e) 
  -                     {
  -                             ErrorHandler.Error("OpenFile 
["+m_baseFileName+"] call failed.", e);
  -                     }
  +                     // This will also close the file. This is OK since 
multiple close operations are safe.
  +                     SafeOpenFile(m_baseFileName, false);
                }
   
                #endregion
  @@ -1150,6 +1210,15 @@
                private string m_baseFileName;
     
                #endregion Private Instance Fields
  +
  +             #region Static Members
  +
  +             /// <summary>
  +             /// The 1st of January 1970 in UTC
  +             /// </summary>
  +             private static readonly DateTime s_date1970 = new 
DateTime(1970, 1, 1);
  +
  +             #endregion
   
                #region DateTime
   
  
  
  
  1.5       +37 -6     logging-log4net/src/Appender/TextWriterAppender.cs
  
  Index: TextWriterAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/TextWriterAppender.cs,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- TextWriterAppender.cs     1 Jun 2004 18:34:40 -0000       1.4
  +++ TextWriterAppender.cs     18 Sep 2004 16:34:16 -0000      1.5
  @@ -29,10 +29,18 @@
        /// Sends logging events to a <see cref="TextWriter"/>.
        /// </summary>
        /// <remarks>
  +     /// <para>
        /// An Appender that writes to a <see cref="TextWriter"/>.
  +     /// </para>
  +     /// <para>
  +     /// This appender may be used stand alone if initialized with an 
appropriate
  +     /// writer, however it is typically used as a base class for an 
appender that
  +     /// can open a <see cref="TextWriter"/> to write to.
  +     /// </para>
        /// </remarks>
        /// <author>Nicko Cadell</author>
        /// <author>Gert Driesen</author>
  +     /// <author>Douglas de la Torre</author>
        public class TextWriterAppender : AppenderSkeleton
        {
                #region Public Instance Constructors
  @@ -128,8 +136,11 @@
                                lock(this) 
                                {
                                        Reset();
  -                                     m_qtw = new QuietTextWriter(value, 
ErrorHandler);
  -                                     WriteHeader();
  +                                     if (value != null)
  +                                     {
  +                                             m_qtw = new 
QuietTextWriter(value, ErrorHandler);
  +                                             WriteHeader();
  +                                     }
                                }
                        }
                }
  @@ -157,15 +168,21 @@
   
                        if (m_qtw == null) 
                        {
  -                             ErrorHandler.Error("No output stream or file 
set for the appender named ["+ Name +"].");
  -                             return false;
  +                             // Allow subclass to lazily create the writer
  +                             PrepareWriter();
  +
  +                             if (m_qtw == null) 
  +                             {
  +                                     ErrorHandler.Error("No output stream or 
file set for the appender named ["+ Name +"].");
  +                                     return false;
  +                             }
                        }
                        if (m_qtw.Closed) 
                        {
                                ErrorHandler.Error("Output stream for appender 
named ["+ Name +"] has been closed.");
                                return false;
                        }
  -     
  +
                        return true;
                }
   
  @@ -320,7 +337,21 @@
                                        m_qtw.Write(h);
                                }
                        }
  -             }       
  +             }
  +
  +             /// <summary>
  +             /// Called to allow a subclass to lazily initialize the writer
  +             /// </summary>
  +             /// <remarks>
  +             /// <para>
  +             /// This method is called when an event is logged and the <see 
cref="Writer"/> or
  +             /// <see cref="QuietWriter"/> have not been set. This allows a 
subclass to
  +             /// attempt to initialize the writer multiple times.
  +             /// </para>
  +             /// </remarks>
  +             virtual protected void PrepareWriter()
  +             {
  +             }
   
                #endregion Protected Instance Methods
   
  
  
  

Reply via email to