这可能被认为是一种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}	|	${uppercase:${level}}	|	${message}"
archiveEvery="Day"
archiveFileName="${basedir}/logs/archive/info/${previousdate{shortdate,"Day"}}.{#}.log"
archiveNumbering="Rolling"
maxArchiveFiles="30"/>
我的建议假定归档文件应根据当前日期的“上一个日期”命名。这也有点取决于您上面提到的文件在日期更改时滚动的事实,因此将“当前”日期分配给文件名而不是“上一个”日期。在某些情况下,这种方法可能无法提供您可能想要的结果。如果您的应用程序只在工作日运行怎么办?它在星期一全天运行,然后在星期二的第一个日志中滚动日志文件并根据前一天(星期一)的日期命名。没事儿。本周剩下的时间,也就是直到周末,这都很好。当程序在星期五运行时,会捕获日志。该程序不会在周末运行。星期一,第一次记录消息时,日志文件滚动。在这种情况下,前一个日期将是星期日,而您可能希望前一个日期是星期五。也可能是由于某种原因,某一天没有日志。第二天有一个日志,导致翻转。同样,我描述的方法将确定前一天是实际的“物理”前一天,此时您可能更喜欢“前一天”来表示前一天“有任何日志写入时”。
这已经有点长了,但也许你会发现它很有用。