如何将所有 nlog 文件目标指向一个额外的临时路径?

how to point all nlog file targets to an additional interim path?

假设我有两个记录器A, B,它们写入文件目标A, B, E,如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <targets async="true">
        <target name="A" xsi:type="File" fileName="${basepath}\A.log" archiveFileName="${basepath}\A.{##}.log" />
        <target name="B" xsi:type="File" fileName="${basepath}\B.log" archiveFileName="${basepath}\B{##}.log" />
        <target name="E" xsi:type="File" fileName="${basepath}\E.log" archiveFileName="${basepath}\E{##}.log" />
    </targets>
    <rules>
        <logger name="A" minlevel="Debug" writeTo="A"/>
        <logger name="B" minlevel="Debug" writeTo="B"/>
        <logger name="*" minlevel="Error" writeTo="E"/>
    </rules>
</nlog>

现在我有这样的要求,在程序运行期间 window 的一段时间内,不仅要使用 ${basepath},还要使用 ${InterimPath}。 在时间 window 过去后,它应该只继续写入 ${basepath}。 如何做到这一点?

目前我能想到的唯一复杂的解决方案是以编程方式

  1. 遍历所有文件目标并根据这些文件目标添加新目标,更改名称、FileName 和 ArchiveFileName 属性指向新路径。
  2. 遍历所有规则并添加新规则,并将 writeTo 更新为新的目标名称。
  3. 稍后删除这些目标和规则。

更新: 两个路径($basepath$interimpath)都应该在 window 期间用于日志记录。在时间 window 之后,只应使用 $basepath。

有多种方法可以让它发挥作用。我认为您的解决方案会奏效。我个人会选择以下解决方案之一:

GDC 方法

这是一个非常简单的解决方案。使用全局诊断上下文并在需要时更改值。因此无需在配置中进行迭代。

设置项目初始值:

GlobalDiagnosticsContext.Set("myPath", basePath1);

如果需要,例如在计时器上或单击按钮时,更改基本路径

GlobalDiagnosticsContext.Set("myPath", basePath2);

在配置中:fileName="${gdc:myPath}\E.log"

GDC

自定义布局渲染器方法

更自动化一点,创建一个自定义布局渲染器,根据时间 returns 不同的路径。

注册(在mainapp_start

using NLog.LayoutRenderers;

LayoutRenderer.Register("myPath", () =>
{
    if (DateTime.Now.Hour > 16 && DateTime.Now.Hour < 18)
    {
        return myTempPath;
    }

    return myRegularPath;
});

在配置中:fileName="${myPath}\E.log"

How to write a custom layout renderer

没有代码方法

您也可以在没有任何代码的情况下执行此操作,但配置会变得复杂,并且在以非常高的负载进行日志记录时可能会对性能产生影响。

  • 使用 basepath 和 interimPath 复制目标
  • 在您的配置中使用条件,每个目标有 2 个规则,如下所示:
     <logger name="A" writeTo="A">
            <filters defaultAction="Ignore">
                <when condition="${date:format=h} > 16" action="Log" />
            </filters>
     </logger>
     <logger name="A" writeTo="A-interim">
            <filters defaultAction="Log">
                <when condition="${date:format=h} >= 16" action="Ignore" />
            </filters>
     </logger>
    

conditions

更新答案

单个 FileTarget 无法将单个 LogEvent 转换为不同位置的两个文件写入。我建议你加倍:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <variable name="InterimPath" value="${gdc:InterimPath}" />
    <variable name="InterimPathOff" value="${gdc:InterimPath:whenEmpty=Off}" />
    <targets async="true">
        <target name="A1" xsi:type="File" fileName="${basepath}\A.log" archiveFileName="${basepath}\A.{##}.log" />
        <target name="A2" xsi:type="File" fileName="${InterimPath}\A.log" archiveFileName="${interimpath}\A.{##}.log" />
        <target name="B1" xsi:type="File" fileName="${basepath}\B.log" archiveFileName="${basepath}\B{##}.log" />
        <target name="B2" xsi:type="File" fileName="${InterimPath}\B.log" archiveFileName="${interimpath}\B{##}.log" />
        <target name="E1" xsi:type="File" fileName="${basepath}\E.log" archiveFileName="${basepath}\E{##}.log" />
        <target name="E2" xsi:type="File" fileName="${InterimPath}\E.log" archiveFileName="${interimpath}\E{##}.log" />
    </targets>
    <rules>
        <logger name="A" minlevel="Debug" writeTo="A1"/>
        <logger name="A" minlevel="${whenEmpty:whenEmpty=${InterimPathOff}:inner=Debug" writeTo="A2"/>
        <logger name="B" minlevel="Debug" writeTo="B1"/>
        <logger name="B" minlevel="${whenEmpty:whenEmpty=${InterimPathOff}:inner=Debug" writeTo="B2"/>
        <logger name="*" minlevel="Error" writeTo="E1"/>
        <logger name="*" minlevel="${whenEmpty:whenEmpty=${InterimPathOff}:inner=Error" writeTo="E2"/>
    </rules>
</nlog>

那你就可以启用号码了。 2 个目标:

GlobalDiagnosticsContext.Set("InterimPath", mySpecialPath);
LogManager.ReconfigExistingLoggers();

并且您可以禁用号码。 2 个目标再次像这样(例如,当计时器触发时):

GlobalDiagnosticsContext.Set("InterimPath", "");
LogManager.ReconfigExistingLoggers();

旧答案

你可以这样做:

${gdc:InterimPath:whenEmpty=${basepath}}

然后用计时器在超时后从 GDC 中清除 InterimPath 变量

GlobalDiagnosticsContext.Set("InterimPath", mySpecialPath);

另请参阅:https://github.com/nlog/nlog/wiki/Gdc-Layout-Renderer