Log4Net:在带有滚动日期的RollingFileAppender中设置最大备份文件数

80

我有以下配置,但是我没有找到如何在按日期滚动样式中设置最大备份文件的任何文档。我知道你可以使用maxSizeRollBackups来使用大小滚动样式来做到这一点。

<appender name="AppLogFileAppender" type="log4net.Appender.RollingFileAppender">
    <file value="mylog.log" />
    <appendToFile value="true" />
    <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
    <rollingStyle value="Date" />
    <datePattern value=".yyMMdd.'log'" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%d %-5p %c - %m%n"  />
    </layout>
</appender>
9个回答

55

3
更新:它支持maxSizeRollBackups选项:http://logging.apache.org/log4net/release/sdk/log4net.Appender.RollingFileAppender.MaxSizeRollBackups.html - schoetbi
@schoetbi,还是不行。我正在制作一些原型,虽然maxSizeRollBackups设置为3,但备份的文件超过了4个。 - manu
7
maxSizeRollBackups的作用并非如此,如果日期对应的文件被锁定或太大,则会为相同日期创建一个备份。它只限制同一天的多个文件数量,而不是保存多少“天”的文件。 - Eric Brown - Cal
1
已修复文档链接:http://logging.apache.org/log4net/release/sdk/html/P_log4net_Appender_RollingFileAppender_MaxSizeRollBackups.htm,该链接不再提到“注意事项”。 - Myster
1
@Myster但是它指的是:最大值适用于每个基于时间组的文件,而不是总数。 - Gregor Ažbe
2
而且在 RollingFileAppender 类的文档中仍然有警告:https://logging.apache.org/log4net/release/sdk/html/T_log4net_Appender_RollingFileAppender.htm - Gregor Ažbe

43

虽然它不受支持,但这是我处理这种情况的方法:

这是我的配置:

    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
        <file value="C:\logs\LoggingTest\logfile.txt" />
        <appendToFile value="true" />
        <rollingStyle value="Composite" />
        <datePattern value="yyyyMMdd" />
        <maxSizeRollBackups value="10" />
        <maximumFileSize value="1MB" />
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%date  - %message%newline" />
        </layout>
    </appender>

应用程序启动时,我执行以下操作:

 XmlConfigurator.Configure();
 var date = DateTime.Now.AddDays(-10);
 var task = new LogFileCleanupTask();
 task.CleanUp(date);

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

using log4net;
using log4net.Appender;
using log4net.Config;

    public class LogFileCleanupTask
    {
        #region - Constructor -
        public LogFileCleanupTask()
        {
        }
        #endregion

        #region - Methods -
        /// <summary>
        /// Cleans up. Auto configures the cleanup based on the log4net configuration
        /// </summary>
        /// <param name="date">Anything prior will not be kept.</param>
        public void CleanUp(DateTime date)
        {
            string directory = string.Empty;
            string filePrefix = string.Empty;

            var repo = LogManager.GetAllRepositories().FirstOrDefault(); ;
            if (repo == null)
                throw new NotSupportedException("Log4Net has not been configured yet.");

            var app = repo.GetAppenders().Where(x => x.GetType() == typeof(RollingFileAppender)).FirstOrDefault();
            if (app != null)
            {
                var appender = app as RollingFileAppender;

                directory = Path.GetDirectoryName(appender.File);
                filePrefix = Path.GetFileName(appender.File);

                CleanUp(directory, filePrefix, date);
            }
        }

        /// <summary>
        /// Cleans up.
        /// </summary>
        /// <param name="logDirectory">The log directory.</param>
        /// <param name="logPrefix">The log prefix. Example: logfile dont include the file extension.</param>
        /// <param name="date">Anything prior will not be kept.</param>
        public void CleanUp(string logDirectory, string logPrefix, DateTime date)
        {
            if (string.IsNullOrEmpty(logDirectory))
                throw new ArgumentException("logDirectory is missing");

            if (string.IsNullOrEmpty(logPrefix))
                throw new ArgumentException("logPrefix is missing");

            var dirInfo = new DirectoryInfo(logDirectory);
            if (!dirInfo.Exists)
                return;

            var fileInfos = dirInfo.GetFiles("{0}*.*".Sub(logPrefix));
            if (fileInfos.Length == 0)
                return;

            foreach (var info in fileInfos)
            {
                if (info.CreationTime < date)
                {
                    info.Delete();
                }
            }

        }
        #endregion
    }

Sub方法是一个扩展方法,它基本上像这样包装了string.format:

/// <summary>
/// Extension helper methods for strings
/// </summary>
[DebuggerStepThrough, DebuggerNonUserCode]
public static class StringExtensions
{
    /// <summary>
    /// Formats a string using the <paramref name="format"/> and <paramref name="args"/>.
    /// </summary>
    /// <param name="format">The format.</param>
    /// <param name="args">The args.</param>
    /// <returns>A string with the format placeholders replaced by the args.</returns>
    public static string Sub(this string format, params object[] args)
    {
        return string.Format(format, args);
    }
}

3
值得注意的是,“创建日期”可能不可靠,因为存在这种情况 - https://dev59.com/snI95IYBdhLWcg3w3yJU - Iain
1
值得注意的是,只有满足以下两个条件,这个类才能正常运行:1)StaticLogFileName设置为true。2)preserveLogFileNameExtension设置为false - Mr Jones

12
为了限制日志的数量,不要在日期模式中包括年份或月份,例如 datePattern value="_dd'.log'"。
这将每天创建一个新的日志,并且下个月会被覆盖。

1
这听起来是一个非常简单和有趣的想法,但我看到了以下问题 - 每次应用程序重新启动都会覆盖当天的现有文件。或者如果您将其配置为不覆盖文件,而是追加 - 文件将无限增长。您如何精确配置此项? - Lukas K
@LukasK 如果你将 rollingStyle 设置为 composite,然后设置 maximumFileSizemaxSizeRollbackups,那么你就可以限制每个文件的大小。然而,如果一个月里你有三个day6的滚动备份,而下个月只有两个,这可能会让人感到困惑。上个月的 '_06.3' 滚动备份将会与当前月份混合在一起。 - mdisibio

11

我几个月前花了一些时间研究这个问题。v1.2.10版本不支持按日期滚动删除旧的日志文件。这个功能已经被列入下一个版本的任务清单中。我拿到了源代码并自己添加了这个功能,并将其发布给其他需要的人。该问题和补丁可以在https://issues.apache.org/jira/browse/LOG4NET-27找到。


如何使用它?您需要下载log4net源代码,添加它,然后重新编译并获取新的程序集文件吗? - n00b
据我所知,是的。 - Mafu Josh

3

我不确定你需要什么。以下是我一个lo4net.config文件的摘录:

  <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
    <param name="File" value="App_Data\log"/>
    <param name="DatePattern" value=".yyyy-MM-dd-tt&quot;.log&quot;"/>
    <param name="AppendToFile" value="true"/>
    <param name="RollingStyle" value="Date"/>
    <param name="StaticLogFileName" value="false"/>
    <param name="maxSizeRollBackups" value="60" />
    <layout type="log4net.Layout.PatternLayout">
      <param name="ConversionPattern" value="%r %d [%t] %-5p %c - %m%n"/>
    </layout>
  </appender>

1
不确定为什么会被踩。解释一下为什么会被踩是好的习惯,这样我们都可以学到东西。 - wcm
5
似乎没有抓住问题的要点。maxSizeRollBackups不能让你删除超过10天的文件,我认为这正是提问者想要实现的目标。 - Graham Griffiths
1
我认为你是正确的,这就是 OP 想要的。当时我有点不清楚他想要实现什么。我的回答只是试图向他展示我的代码。我仍然会以同样的方式回答问题,以尝试提供帮助,但我可以理解你为什么会投反对票。 - wcm

2

最近我在清理日志记录时遇到了一个需求,需要根据传入服务的maxAgeInDays配置值进行清理...和许多人一样,我也遇到了NTFS“特性”隧道,这使得使用FileInfo.CreationDate存在问题(尽管我已经解决了这个问题)...

既然我有了一个模式可以依照,我决定自己编写清理方法...我的记录器是通过编程方式配置的,所以当我的记录器设置完成后,我只需要调用以下方法即可...

    //.........................
    //Log Config Stuff Above...

    log4net.Config.BasicConfigurator.Configure(fileAppender);
    if(logConfig.DaysToKeep > 0)
       CleanupLogs(logConfig.LogFilePath, logConfig.DaysToKeep);
}

static void CleanupLogs(string logPath, int maxAgeInDays)
{
    if (File.Exists(logPath))
    {
        var datePattern = "yyyy.MM.dd";
        List<string> logPatternsToKeep = new List<string>();
        for (var i = 0; i <= maxAgeInDays; i++)
        {
            logPatternsToKeep.Add(DateTime.Now.AddDays(-i).ToString(datePattern));
        }

        FileInfo fi = new FileInfo(logPath);

        var logFiles = fi.Directory.GetFiles(fi.Name + "*")
            .Where(x => logPatternsToKeep.All(y => !x.Name.Contains(y) && x.Name != fi.Name));

        foreach (var log in logFiles)
        {
            if (File.Exists(log.FullName)) File.Delete(log.FullName);
        }
    }
}

可能不是最美观的方法,但对于我们的目的来说很有效...


1

NLog几乎与Log4Net设置方式相同(并且正在积极维护 - 甚至支持.NET Core),支持基于日期滚动日志。


1
你能给个例子吗?我在它的WIKI中找不到如何配置它。 - yu yang Jian

0

不再担心更复杂的每日 x,只指定了任意文件计数,并将其组合在一起。请小心使用 [SecurityAction.Demand]

public string LogPath { get; set; }
public int MaxFileCount { get; set; } = 10;

private FileSystemWatcher _fileSystemWatcher;

[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public async Task StartAsync()
{
    await Task.Yield();

    if (!Directory.Exists(LogPath))
    { Directory.CreateDirectory(LogPath); }

    _fileSystemWatcher = new FileSystemWatcher
    {
        Filter = "*.*",
        Path = LogPath,
        EnableRaisingEvents = true,
        NotifyFilter = NotifyFilters.FileName
            | NotifyFilters.LastAccess
            | NotifyFilters.LastWrite
            | NotifyFilters.Security
            | NotifyFilters.Size
    };

    _fileSystemWatcher.Created += OnCreated;
}

public async Task StopAsync()
{
    await Task.Yield();

    _fileSystemWatcher.Created -= OnCreated; // prevents a resource / memory leak.
    _fileSystemWatcher = null; // not using dispose allows us to re-start if necessary.
}

private void OnCreated(object sender, FileSystemEventArgs e)
{
    var fileInfos = Directory
        .GetFiles(LogPath)
        .Select(filePath => new FileInfo(filePath))
        .OrderBy(fileInfo => fileInfo.LastWriteTime)
        .ToArray();

    if (fileInfos.Length <= MaxFileCount)
    { return; }

    // For every file (over MaxFileCount) delete, starting with the oldest file.
    for (var i = 0; i < fileInfos.Length - MaxFileCount; i++)
    {
        try
        {
            fileInfos[i].Delete();
        }
        catch (Exception ex)
        {
            /* Handle */
        }
    }
}

0

从log4net appender继承并添加自己的重写方法以执行文件清理非常容易。我重写了OpenFile来实现这一点。以下是一个自定义log4net appender的示例,可供参考:https://dev59.com/gXE95IYBdhLWcg3wQ7uc#2385874


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接