代码之家  ›  专栏  ›  技术社区  ›  nawfal Donny V.

基于大小限制和时间戳备份的滚动文件附加器

  •  8
  • nawfal Donny V.  · 技术社区  · 6 年前

    有没有一种方法可以设置log4net配置,如果日志文件的大小超过某个限制,那么应该用时间戳重命名现有的日志文件?

    要求:

    例如,当大小超过1MB时,我的日志应该是

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

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

    注:

    1. 当滚动在同一天发生两次时,log4net应该将其附加到已经有当天时间戳的文件中。例如,如果连续两次 2018年7月12日 文件大小限制为 1MB 可以备份到 日志.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 回复  |  直到 6 年前
        1
  •  2
  •   pfx    6 年前

    它可以实现,但不只是通过配置。
    习惯 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>
    

    要求中最具挑战性的部分是排除文件名中的索引/计数器后缀;如Log-2018-07-16。 TXT 由于log4net内部跟踪并使用此计数器号来处理备份文件删除等问题。 因此,我们必须自己管理清理工作。

    log4net的实现 滚动文件 不是很开放;要重写的方法很少 并且无法访问当前的计数器编号跟踪。
    如果您不能或不想包括和修改原始代码的完整源代码 滚动文件 在你自己的项目中, 您可以从原始文件继承,以便尽可能少地进行更改,如下所示。

    如果发生基于文件大小的汇总,我们将检查当前日志文件(与当前日期匹配;请参见要求)是否已存在。 如果是,则不会发生汇总;只有来自 日志.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);
                }
            }        
        }
    }
    
        2
  •  0
  •   D Ie    6 年前

    也许我不应该把它作为答案发布(唯一的区别是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文件的配置对我有效