存档时NLog日期不正确

时间:2013-06-12 11:57:23

标签: date nlog archiving

我想使用归档来限制日志文件的数量,我希望每个归档日志的文件名都是日志的日期。很简单。

这是我的目标之一:

<target xsi:type="File" name="info" fileName="${basedir}/logs/info.log"
        layout="${date:format=HH\:mm\:ss}&#009;|&#009;${uppercase:${level}}&#009;|&#009;${message}"
        archiveEvery="Day"
        archiveFileName="${basedir}/logs/archive/info/${shortdate}.{#}.log"
        archiveNumbering="Rolling"
        maxArchiveFiles="30"/>

我已经读过你必须在archiveFileName中拥有{#},否则当你在文件名中有日期时归档将根本不起作用,这有点令人讨厌,但我想我可以忍受。

但是,由于日期更改后执行归档,$ {shortdate}将始终成为日的日期,即一天(或更多)在期望的日期之后。如果今天记录了一条消息,2013-06-12,那么当它明天存档时,它将被放置在一个名为2013-06-13.log的文件中。

是否有任何方式来获取正确的日期?我看到有人建议变量,但我不知道它会如何工作......这看起来就像是一个显而易见的特征。它肯定是archiveNumbering模式之一!与日期相比,现在可用的编号模式似乎是不切实际的。

这个问题与Delete log files after x days有关。如果可以在不使用归档功能的情况下设置最大日志文件数(因为这是我真正想要的归档功能),那实际上会更好,因为我也不必使用{#},但我怀疑是不可能的。

2 个答案:

答案 0 :(得分:1)

NLog自此发布此问题后添加了编号格式“日期”,这正是我所寻找的。

有关详细信息,请参阅https://github.com/nlog/NLog/wiki/File-target#archival-options

答案 1 :(得分:0)

这可能被认为是一种Rube Goldberg方法,但它可能有用......您可以编写一个自定义LayoutRenderer来计算“上一个”日期。此LayoutRenderer的参数将是Target配置中的“archiveEvery”设置。

使用ShortDateLayoutRenderer作为基础...

(取自NLog的git存储库......)

[LayoutRenderer("shortdate")]
[ThreadAgnostic]
public class ShortDateLayoutRenderer : LayoutRenderer
{
    /// <summary>
    /// Gets or sets a value indicating whether to output UTC time instead of local time.
    /// </summary>
    /// <docgen category='Rendering Options' order='10' />
    [DefaultValue(false)]
    public bool UniversalTime { get; set; }

    /// <summary>
    /// Renders the current short date string (yyyy-MM-dd) and appends it to the specified <see cref="StringBuilder" />.
    /// </summary>
    /// <param name="builder">The <see cref="StringBuilder"/> to append the rendered data to.</param>
    /// <param name="logEvent">Logging event.</param>
    protected override void Append(StringBuilder builder, LogEventInfo logEvent)
    {
        var ts = logEvent.TimeStamp;
        if (this.UniversalTime)
        {
            ts = ts.ToUniversalTime();
        }

        builder.Append(ts.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture));
    }
}

PreviousDateLayoutRenderer可能如下所示: (请注意,我既没有编译也没有测试过这段代码,但我之前编写过LayoutRenderers。)

[LayoutRenderer("previousdate")]
[ThreadAgnostic]
public class PreviousDateLayoutRenderer : LayoutRenderer
{
    /// <summary>
    /// Gets or sets a value indicating whether to output UTC time instead of local time.
    /// </summary>
    /// <docgen category='Rendering Options' order='10' />
    [DefaultValue(false)]
    public bool UniversalTime { get; set; }

    /// <summary>
    /// Gets or sets the value indicating the unit of time to subtract to get previous date.
    /// </summary>
    [DefaultValue("Day")]
    public string TimeUnit { get; set; }

    /// <summary>
    /// Gets the current date, subtracts one TimeUnit, renders the resulting short date string,
    /// then appends it to the specified <see cref="StringBuilder" />.
    /// </summary>
    /// <param name="builder">The <see cref="StringBuilder"/> to append the rendered data to.</param>
    /// <param name="logEvent">Logging event.</param>
    protected override void Append(StringBuilder builder, LogEventInfo logEvent)
    {
        var ts = logEvent.TimeStamp;
        if (this.UniversalTime)
        {
            ts = ts.ToUniversalTime();
        }

        // This could certainly be better.  Probably smarter to put code in the setter of the
        // TimeUnit property to compute a TimeSpan member variable that could then be subtracted
        // in this method rather than check the TimeUnit and compute the TimeSpan every time.

        TimeSpan span;

        switch (TimeUnit)
        {
          case "Day":
            span = TimeSpan.FromDays(1);
            break;
          case "Hour":
            span = TimeSpan.FromHours(1);
            break;
        }

        ts -= span;

        builder.Append(ts.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture));
    }
}

或者,您可以编写一个LayoutRendererWrapper,它将应用非常相似的逻辑,但它会传递一个字符串(包装器会尝试将其解释为日期)并从中减去所需的TimeSpan。

如上所述,给定一个包装器,它可能配置如下:

<target xsi:type="File" name="info" fileName="${basedir}/logs/info.log"
        layout="${date:format=HH\:mm\:ss}&#009;|&#009;${uppercase:${level}}&#009;|&#009;${message}"
        archiveEvery="Day"
        archiveFileName="${basedir}/logs/archive/info/${previousdate{shortdate,"Day"}}.{#}.log"
        archiveNumbering="Rolling"
        maxArchiveFiles="30"/>

我的建议假设应根据当前日期的“上一个日期”命名归档文件。它还取决于您在上面提到的文件在日期更改时滚动的事实,因此将“当前”日期分配给文件名而不是“上一个”日期。当然,这种方法可能无法提供您可能想要的结果。如果您的应用程序仅在工作日运行,该怎么办?它在星期一全天运行,然后在星期二的第一个日志中滚动日志文件并根据前一天(星期一)的日期命名。那样就好。一周的剩余时间,也就是周末,这是好的。由于程序在星期五运行,因此会捕获日志。该计划不会在周末运行。星期一,第一次记录消息时,将滚动日志文件。在这种情况下,上一个日期将是星期日,您可能希望上一个日期是星期五。也可能是因为某种原因,某一天没有日志。第二天有一个日志,导致翻转。同样,我描述的方法将确定前一个日期是实际的“实际”前一天,当您可能更喜欢“之前”表示前一天“当有任何日志写入时”。

这已经有点长了,但也许你会发现它很有用。