根据大小限制滚动文件附加程序并备份时间戳

时间:2018-07-13 09:01:31

标签: c# log4net

是否可以通过以下方式设置log4net配置:如果日志文件的大小超过某个限制,则应使用时间戳将现有日志文件重命名?

要求:

例如当大小超过1MB时,我的日志应该看起来像

Log.txt
Log.12-07-2018.txt
Log.11-07-2018.txt

etc,其中Log.txt是当前/最新日志。

注意:

  1. 如果同一天两次滚动,则log4net应该将其附加到已经具有当天时间戳记的文件中。例如如果在 12-07-2018 上发生两次滚动并且文件大小限制为 1MB ,则可以备份到 Log.12-07-2018。 txt 文件的大小为 2MB

我尝试过的事情:

这是我正在使用的log4net配置:

<log4net>
  <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender" >
    <file value="${LOCALAPPDATA}\foobar\Log.txt" />
    <encoding value="utf-8" />
    <appendToFile value="true" />
    <rollingStyle value="Size" />
    <maximumFileSize value="1MB" />
    <maxSizeRollBackups value="1000" />
    <preserveLogFileNameExtension value="true" />
    <datePattern value=".dd-MM-yyyy" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date %level [%thread] %type.%method - %message%n" />
    </layout>
  </appender>
  <root>
    <level value="All" />
    <!-- If the following line is not included the log file will not be created even if log4net is configured with this file. -->
    <appender-ref ref="RollingFileAppender" />
  </root>
</log4net>

我得到的东西:

Log.txt
Log.1.txt
Log.2.txt

我希望重命名部分基于时间戳,而备份逻辑基于大小。当我尝试时:

    <rollingStyle value="Date" />
    <datePattern value=".dd-MM-yyyy" />

命名备份日志文件是可行的,但是滚动是根据日期而不是大小进行的。

2 个答案:

答案 0 :(得分:2)

可以实现,但不能仅通过配置来实现。
如下所示,必须创建自定义RollingFileAppender并在Log4net配置中引用它,其中PFX.RollingFileAppender是从程序集PFX.Lib加载的。

设置staticLogFileName可确保最新文件始终被命名为Log.txt(已配置)。 我们(重新)使用datePattern选项在日志文件名中配置日期部分。
(出于性能原因,countDirection设置为0以减少过渡次数;最新/最后一个备份文件将是次数最多的文件。)

<log4net>
    <appender name="RollingFileAppender" type="PFX.CustomRollingFileAppender, PFX.Lib" >
        <datePattern value="yyyy-MM-dd" />
        <staticLogFileName value="true" />
        <countDirection value="0" />
        <file value="${LOCALAPPDATA}\foobar\Log.txt" />
        <encoding value="utf-8" />
        <appendToFile value="true" />
        <rollingStyle value="Size" />
        <maximumFileSize value="1MB" />
        <maxSizeRollBackups value="1000" />
        <preserveLogFileNameExtension value="true" />            
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%date %level [%thread] %type.%method - %message%n" />
        </layout>
    </appender>
    <root>
        <level value="All" />    
        <appender-ref ref="RollingFileAppender" />
    </root>
</log4net>

要求中最具挑战性的部分是文件名中索引/计数器后缀的排他性;例如。日志-2018-07-16。 1 .txt 因为Log4net在内部跟踪并使用此计数器编号来处理例如。备份文件删除。 因此,我们必须自己管理清理。

Log4net对RollingFileAppender的实现不是很开放。有很少的方法可以覆盖 并且无法访问当前的计数器编号跟踪。
如果您不能或不想在自己的项目中包含和修改原始RollingFileAppender的完整源代码, 您可以继承原始版本,以进行尽可能少的更改,如下所示。

如果基于文件大小进行汇总,我们将检查是否存在今天的日志文件(与今天的日期匹配;请参见要求)。 如果是这样,则不会发生汇总。只有Log.txt中的内容被移至今天的日志文件。
如果今天的日志文件不存在,则会发生现成的过渡。这将创建一个名称后带有索引/计数器后缀的文件;之后,此文件将立即重命名以与配置的日期模式匹配。 由于创建了新文件,所有删除的日志文件(超过配置的阈值)都将被删除。

(为简便起见,下面的代码不包含任何异常处理。)

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using log4net.Appender;
using log4net.Util;

namespace PFX
{
    public class CustomRollingFileAppender : RollingFileAppender
    {
        private String _baseFileExtension;
        private String _baseFileNameWithoutExtension;        
        private String _fileDeletePattern;
        private String _folder;        
        private String _backupSearchPattern;

        public CustomRollingFileAppender()
        {}

        public override void ActivateOptions()
        {
            base.ActivateOptions();

            this._baseFileNameWithoutExtension = Path.GetFileNameWithoutExtension(this.File);
            this._baseFileExtension = Path.GetExtension(this.File);
            this._folder = Path.GetDirectoryName(this.File);            
            this._fileDeletePattern = $"{this._baseFileNameWithoutExtension}*{this._baseFileExtension}";
            this._backupSearchPattern = $"{this._baseFileNameWithoutExtension}.*{this._baseFileExtension}";            
        }               

        protected override void AdjustFileBeforeAppend()
        {   
            if ((RollingMode.Size == this.RollingStyle) 
                && (this.File != null)
                && (((CountingQuietTextWriter)base.QuietWriter).Count >= this.MaxFileSize)
                )
            {                   
                DateTime now = DateTime.Now;
                String todayFileSuffix = now.ToString(this.DatePattern, CultureInfo.InvariantCulture);
                String todayFileName = $"{this._baseFileNameWithoutExtension}{todayFileSuffix}{this._baseFileExtension}";
                String todayFile = Path.Combine(this._folder, todayFileName);

                if (base.FileExists(todayFile))
                {                    
                    /* Todays logfile already exist; append content to todays file. */
                    base.CloseFile(); // Close current file in order to allow reading todays file.
                    this.moveContentToTodaysFile(todayFile);
                    // Delete and open todays file for a fresh start.
                    base.DeleteFile(this.File);
                    base.OpenFile(this.File, false);
                }
                else
                {
                    /* Do a roll-over. */

                    base.RollOverSize();

                    using (base.SecurityContext.Impersonate(this))
                    {   
                        this.deleteDepricatedBackupFiles();
                        this.renameBackupFiles(todayFile);
                    }
                }
            }
            else
            {
                base.AdjustFileBeforeAppend();
            }
        }

        // Moves the content from the current log file to todays file.
        private void moveContentToTodaysFile(String todayFile)
        {
            using (FileStream logFile = new FileStream(this.File, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (StreamReader reader = new StreamReader(logFile))
            using (FileStream backupFile = new FileStream(todayFile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
            using (StreamWriter writer = new StreamWriter(backupFile))
            {
                const Int32 BUFFER_SIZE = 1024;
                Char[] buffer = new Char[BUFFER_SIZE];

                while (true)
                {
                    Int32 nrOfCharsRead = reader.Read(buffer, 0, BUFFER_SIZE);
                    if (nrOfCharsRead <= 0) { break; }

                    writer.Write(buffer, 0, nrOfCharsRead);
                }
            }
        }

        // Rename backup files according to the configured date pattern, removing the counter/index suffix.
        private void renameBackupFiles(String todayFile)
        {
            IEnumerable<String> backupFiles = Directory.EnumerateFiles(this._folder, this._backupSearchPattern, SearchOption.TopDirectoryOnly);
            foreach (String backupFile in backupFiles)
            {   
                base.RollFile(backupFile, todayFile);
            }
        }

        // Keep the number of allowed backup files and delete all others.
        private void deleteDepricatedBackupFiles()
        {
            DirectoryInfo folder = new DirectoryInfo(this._folder);    

            IEnumerable<FileInfo> filesToDelete = 
                folder
                    .EnumerateFiles(this._fileDeletePattern, SearchOption.TopDirectoryOnly)
                    .OrderByDescending(o => o.LastWriteTime)
                    .Skip(this.MaxSizeRollBackups + 1)
                    ;

            foreach (FileSystemInfo fileToDelete in filesToDelete)
            {
                base.DeleteFile(fileToDelete.FullName);
            }
        }        
    }
}

答案 1 :(得分:0)

也许我不应该将其发布为答案(唯一的区别是staticlogfilename属性和日志扩展名的file-datePattern值),但是我发现在注释中发布代码太丑了...

<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
        <file value="path\Log"/>            
        <datePattern value="'.'yyyy-MM-dd'.log'" />         
        <appendToFile value="true"/>
        <staticLogFileName value="false" />
        <maximumFileSize value="1MB"/>          
        <lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
    </appender>

.config文件的此配置对我有用