【问题标题】:How to implement auto archiving for log file using Log4Net如何使用 Log4Net 实现日志文件的自动归档
【发布时间】:2014-12-04 06:11:57
【问题描述】:

我想以这样一种方式配置 log4net,即所有前一天的日志都应该自动存档。是否有可能在 Log4Net 中自动存档以前的日志。我只想使用配置来执行此操作,而不使用任何第三方库(如Sharplibzip)编写任何代码来创建存档。

通过归档添加的另一件事是压缩 zip/rar 格式的文件以节省磁盘空间。

【问题讨论】:

    标签: c# logging log4net


    【解决方案1】:

    log4net 不包含任何压缩日志文件的功能。但是压缩是在 .Net 框架中(从 4.5 开始),或者您可以使用 the Windows Shell API 压缩文件,因此您将拥有定期获取所有不是当前日志文件的日志文件并将它们压缩的代码:

    -- assuming you don't have more that one appender
    appender = LogManager.GetRepository().GetAppenders()
                         .OfType<RollingFileAppender>().FirstOrDefault();
    
    if (appender == null) return; // no appenders found
    
    var currentFile = appender.File;
    
    -- dropping to pseudocode:
    var files = Get_all_files_in_log_directory_which_match_log_file_pattern 
             but_aren't_the_current_file(current file);
    
    if (ZipFiles(files...))
        (DeleteFiles(files); // presumably
    

    【讨论】:

    • 嗨@stuartd。请问我在gist.github.com/KamranShahid/cbb2d04691b0862938b72d83100e9e93做错了什么@
    • @KamranShahid 不是真的。如果它不起作用,您可能需要考虑发布一个问题,详细说明问题是什么?
    • 在我的情况下,日志在文件大小上旋转,文件未归档。我想在归档后删除旧文件
    • 也许你应该单步执行代码。顺便说一句,我似乎记得不建议在自定义附加程序中调用 ILog 上的方法,因为您可能会在循环中结束,您应该改用 LogLog
    • 我只想要当前使用的文件将保留为文本文件,而旧文件将被压缩并删除。如果一天中有多个文件由于大小而应添加到当天的 zip 文件中作为条目。 ILog 正在滚动工作一天。但是同一天有多个文件上面的逻辑不起作用
    【解决方案2】:

    不编写代码就无法归档文件,抱歉。不过代码不会很复杂。

    您可以创建一个继承自RollingFileAppender 的自定义附加程序,并覆盖AdjustFileBeforeAppend 方法以向文件滚动添加行为。这是为RollingFileAppender 滚动文件的现有方法,您可以覆盖该方法以添加归档。

    使用File属性查找文件名处理吧

    // log4net.Appender.RollingFileAppender
    protected virtual void AdjustFileBeforeAppend()
    {
        var fileToZip = File; // save the current file
        if (this.m_rollDate)
        {
            DateTime now = this.m_dateTime.Now;
            if (now >= this.m_nextCheck)
            {
                this.m_now = now;
                this.m_nextCheck = this.NextCheckDate(this.m_now, this.m_rollPoint);
                this.RollOverTime(true);
                // zip the file if roll occurs here
            }
        }
        if (this.m_rollSize)
        {
            if (this.File != null && ((CountingQuietTextWriter)base.QuietWriter).Count >= this.m_maxFileSize)
            {
                this.RollOverSize();
                // zip the file if roll occurs here
            }
        }
    }
    

    或者,您可以找到一个现有的附加程序来执行您想要的操作,但我不知道。


    我冒昧地将@stuartd 的评论提升到答案中,因为他的建议非常优雅。您可以通过这种方式简单地覆盖AdjustFileBeforeAppend

    protected override void AdjustFileBeforeAppend() {
        var previousFile = File;
        base.AdjustFileBeforeAppend();
        if (File != previousFile) { // zip the file }
    }
    

    这是一种非常巧妙的方法,但您可能希望能够区分两种卷(即日期和大小)。例如,仅在日期卷上压缩,以便将某个日期范围内的文件保存在一起。

    【讨论】:

    • 为了避免替换现有方法中的代码,从长远来看这不是最好的主意,可以这样做:protected override void AdjustFileBeforeAppend() { var previousFile = File; base.AdjustFileBeforeAppend(); if (File != previousFile) { // 压缩文件 } }
    • 这是个好主意!我认为区分大小滚动和日期滚动并没有什么坏处,因为您可能希望将同一天的文件压缩在一起。太糟糕了,我们不能在 cmets 上奖励代表
    • log4net 的灵感来自 log4j。 log4j 的继任者称为 logback,并在配置中内置了 zipping,因此它完全符合您(和我)的要求。现在我们只需要一些有灵感的团队来编写 logback.NET!
    • 我不明白,谁在乎这个逻辑?谁在乎只为旧文件做呢?
    • 为什么应该这样工作? ActivateOptions 是唯一更改 File 属性的地方。 File 是它当前应该记录到的文件的名称,即最新的文件。滚动后保持不变。实际上尝试这个也表明文件在滚动后永远不会改变
    【解决方案3】:

    这是我使用缩减版 DotNetZip 的解决方案。

    public class CustomRollingFileAppender : RollingFileAppender
        {
            protected override void AdjustFileBeforeAppend()
            {
                var currentFile = File;
    
                FileInfo fa = new System.IO.FileInfo(currentFile);
    
                if (fa.Length >= 10000000)
                {
                    using (ZipFile zip = new ZipFile(File + ".zip"))
                    {
                        string newFile =  DateTime.Now.ToString("HHmmss") + fa.Name;
                        zip.AddFile(File).FileName = newFile;
                        zip.Save(File + ".zip");
                    }
                }
    
                base.AdjustFileBeforeAppend();
            }
        }
    

    【讨论】:

    • 可行的解决方案,但在我看来,由于为每个 write.samys 解决方案调用 FileInfo,会产生噩梦般的性能开销;-)
    【解决方案4】:

    我想指出,正确实现这一点的唯一方法是完全复制RollingFileAppender 源代码并从中创建FileAppender 的子类。在此代码中,您可以在adjustFileBeforeAppend() 中添加压缩部分。

    上面samy的建议不起作用

    1. 因为第一个代码 sn-p 使用私有成员(不能在子类中访问)
    2. 因为第二个代码sn -p 假定当前日志文件名包含日期左右。但实际上,日期模式仅适用于所有旧文件,而不适用于当前文件。因此,您无法确定文件是否以 sn-p 中显示的方式滚动。

    【讨论】:

      【解决方案5】:

      这是一个建议。这是一个工作代码。 它还避免读取每个日志附加的文件信息。 相反,它将适用于第一个文件翻转。基于时间或基于大小。两个都。 一旦它处于压缩当前文件的过程中。它也会 gzip 退出旧文件。那是在应用程序停止后留下的。 它不需要重新编码所有 RollingFileAppender 类。

      public class CustomRollingFileAppender : RollingFileAppender
      {
         
          override protected void AdjustFileBeforeAppend()
          {
              var currentFile = this.File;
              base.AdjustFileBeforeAppend();
      
              if (!currentFile.Equals(this.File))
              {
                  CompressFileGzip(currentFile);
                  var dirName = Path.GetDirectoryName(this.File);
                  var files = Directory.GetFiles(dirName);
                  var fileExtension = Path.GetExtension(this.File);
                  foreach (var file in files)
                  {
                      if (!file.EndsWith(this.File) && file.EndsWith(fileExtension))
                      {
                          CompressFileGzip(file.ToString());
                      }
                  }
              }
          }
          private void CompressFileGzip(string fileToGzip)
          {
              if (!FileExists(fileToGzip))
              {
                  return;
              }
              FileInfo fileToBeGZipped = new FileInfo(fileToGzip);
              FileInfo gzipFileName = new FileInfo(string.Concat(fileToBeGZipped.FullName, ".gz"));
      
              using (FileStream fileToBeZippedAsStream = fileToBeGZipped.OpenRead())
              {
                  using (FileStream gzipTargetAsStream = gzipFileName.Create())
                  {
                      using (GZipStream gzipStream = new GZipStream(gzipTargetAsStream, CompressionMode.Compress))
                      {
                          fileToBeZippedAsStream.CopyTo(gzipStream);
                      }
                  }
              }
              System.IO.File.Delete(fileToGzip);
          }
      }
      

      【讨论】:

        【解决方案6】:

        我已经修改了 Masood 的实现,但是我没有使用 File 属性(似乎没有改变),而是阅读了“Move [from] -> [to]”事件的 LogLog 消息。

        为 gz 添加了文件时间以避免多个“myLog.xml.1.gz”冲突。 不想让文件名更整洁。

        public class CompressedRollingFileAppender : RollingFileAppender {
                private readonly Regex _moveRegex;
                private string _moveToFile = null;
        
                public CompressedRollingFileAppender() : base() {
                    LogLog.LogReceived += LogReceived;
                    _moveRegex = new Regex(@"^Moving \[(.+)\] -> \[(.+)\]$");
                }
        
                private void LogReceived(object source, LogReceivedEventArgs e) {
                    var match = _moveRegex.Match(e.LogLog.Message);
        
                    if (match.Groups[1].Value == this.File) {
                        _moveToFile = match.Groups[2].Value;
                    }
                }
        
                protected override void AdjustFileBeforeAppend() {
                    var currentFile = this.File;
                    base.AdjustFileBeforeAppend();
        
                    if (_moveToFile != null) {
                        CompressFileGzip(_moveToFile);
                        _moveToFile = null;
                    }
                }
        
                private void CompressFileGzip(string fileToGzip) {
                    if (!FileExists(fileToGzip)) {
                        return;
                    }
        
                    FileInfo fileToBeGZipped = new FileInfo(fileToGzip);
                    // filetime 
                    var filetime = DateTime.Now.ToFileTimeUtc().ToString(System.Globalization.CultureInfo.InvariantCulture);
                    FileInfo gzipFileName = new FileInfo(string.Concat(fileToBeGZipped.FullName, "." + filetime + ".gz"));
        
                    using (FileStream fileToBeZippedAsStream = fileToBeGZipped.OpenRead())
                    using (FileStream gzipTargetAsStream = gzipFileName.Create())
                    using (GZipStream gzipStream = new GZipStream(gzipTargetAsStream, CompressionMode.Compress))
                        fileToBeZippedAsStream.CopyTo(gzipStream);
        
                    System.IO.File.Delete(fileToGzip);
                }
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多