如何将所有 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}
。
如何做到这一点?
目前我能想到的唯一复杂的解决方案是以编程方式
- 遍历所有文件目标并根据这些文件目标添加新目标,更改名称、FileName 和 ArchiveFileName 属性指向新路径。
- 遍历所有规则并添加新规则,并将 writeTo 更新为新的目标名称。
- 稍后删除这些目标和规则。
更新: 两个路径($basepath
和 $interimpath
)都应该在 window 期间用于日志记录。在时间 window 之后,只应使用 $basepath。
有多种方法可以让它发挥作用。我认为您的解决方案会奏效。我个人会选择以下解决方案之一:
GDC 方法
这是一个非常简单的解决方案。使用全局诊断上下文并在需要时更改值。因此无需在配置中进行迭代。
设置项目初始值:
GlobalDiagnosticsContext.Set("myPath", basePath1);
如果需要,例如在计时器上或单击按钮时,更改基本路径
GlobalDiagnosticsContext.Set("myPath", basePath2);
在配置中:fileName="${gdc:myPath}\E.log"
见GDC
自定义布局渲染器方法
更自动化一点,创建一个自定义布局渲染器,根据时间 returns 不同的路径。
注册(在main
或app_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>
更新答案
单个 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);
假设我有两个记录器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}
。
如何做到这一点?
目前我能想到的唯一复杂的解决方案是以编程方式
- 遍历所有文件目标并根据这些文件目标添加新目标,更改名称、FileName 和 ArchiveFileName 属性指向新路径。
- 遍历所有规则并添加新规则,并将 writeTo 更新为新的目标名称。
- 稍后删除这些目标和规则。
更新: 两个路径($basepath
和 $interimpath
)都应该在 window 期间用于日志记录。在时间 window 之后,只应使用 $basepath。
有多种方法可以让它发挥作用。我认为您的解决方案会奏效。我个人会选择以下解决方案之一:
GDC 方法
这是一个非常简单的解决方案。使用全局诊断上下文并在需要时更改值。因此无需在配置中进行迭代。
设置项目初始值:
GlobalDiagnosticsContext.Set("myPath", basePath1);
如果需要,例如在计时器上或单击按钮时,更改基本路径
GlobalDiagnosticsContext.Set("myPath", basePath2);
在配置中:fileName="${gdc:myPath}\E.log"
见GDC
自定义布局渲染器方法
更自动化一点,创建一个自定义布局渲染器,根据时间 returns 不同的路径。
注册(在main
或app_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>
更新答案
单个 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);