Nlog不在并行循环中将所有行写入文件
Nlog not writing all lines into File in Parallel Loop
我正在使用 Nlog 根据要求,我正在并行循环中处理几千条记录
对于每个项目在按特定顺序(管道过滤器模式)进行多次操作后,我想将特定记录(无法加载到数据存储区)写入输出文件夹。
此输出文件夹不是 {basedir}
,而是特定于每个客户端的,因此我使用 nlog 和 nlog 变量以及如下所示的相关配置将值写入多线程应用程序的文件。
在配置文件中
<variable name="outPutFileFullName" value=""/>
<target xsi:type="File" name="OutPutFile" fileName="${mdc:item=outPutFileFullName}"
layout="${message}"/>
<logger name ="ABC"
level="Info" writeTo="OutPutFile"></logger>
在代码中
_loggingService.SetMappedDiagnosticsContext("outPutFileFullName", outPutFolderFile);
Parallel.ForEach(allItems
itemLine =>
{
itemLine.OutPutFileFullName = outPutFolderFile;
var pipeLine = new PipeLine<TEST>();
pipeLine.Register(new Operation1<TEST>(_loggingService))
.Register(new Operation2<TEST>(_loggingService))
.Register(new Operation3<TEST>(_loggingService))
.Execute(itemLine);
});
操作3我有一个简单的方法
private void WriteToFileFromObject(Test obj)
{
LoggingService.Info(obj.FileLineNumber.ToString());
}
我希望这个过程写入 100 条记录,但它总是只写入 17 条记录,但并不相同,也不按特定顺序。
如果我改变fileName="${mdc:item=outPutFileFullName}"
在配置文件中设置为常量值,如 fileName="${basedir}/logs/Test.log"
然后所有 100 条记录都写入文件。知道为什么吗?
MappedDiagnosticsContext 使用线程本地存储。 Parallel.ForEach 将使用其他 threads/tasks,它将无法访问您放入 MappedDiagnosticsContext 中的值(因为它是特定线程的本地值)。如果您只为应用程序设置此值一次,那么也许您可以使用 GlobalDiagnosticsContext。
我怀疑正在写入的 17 个项目来自主线程上发生的执行(即您设置 MDC 值的同一线程)。其余的可能发生在不同的线程上。由于这些线程的 MDC 中没有值,因此文件名为 null(或空)。
另外,我不明白你希望在你的配置中用这一行实现什么:
<variable name="outPutFileFullName" value=""/>
在 nlog.config 文件中声明变量允许您稍后在配置中使用该变量名。变量与引用 MDC/GDC 中的项目没有任何关系,无论如何也不直接。有关如何在 nlog.config:
中使用变量的一些示例,请参阅此答案
Most useful NLog configurations
为了完整起见,我会提到 log4net 有一个 LogicalThreadContext(除了对应于 NLog 的 MDC 和 GDC 的上下文)。 LogicalThreadContext 使用 LogicalSetData 将值存储在 CallContext 中。在这种情况下,存储在 LogicalThreadContext 中的所有值对于任何子线程或任务都将是 "flowed"。因此,如果在线程 A 中存储一个名为 "Name" 的值,然后线程 A 启动一些子线程(或任务),则值 "Name" 将在每个子线程(或任务)的 LogicalThreadContext 中可用).
在您的示例中,如果您使用的是 log4net 并像这样在主线程中设置值:
log4net.LogicalThreadContext.Properties["outputFilename"] = outputFolderFile;
那么该值将在并行处理启动的所有 thread/tasks 中可用。
这是 Jeffrey Richter 的一篇文章,描述了 CallContext.LogicalSetData 的工作原理:
我正在使用 Nlog 根据要求,我正在并行循环中处理几千条记录
对于每个项目在按特定顺序(管道过滤器模式)进行多次操作后,我想将特定记录(无法加载到数据存储区)写入输出文件夹。
此输出文件夹不是 {basedir}
,而是特定于每个客户端的,因此我使用 nlog 和 nlog 变量以及如下所示的相关配置将值写入多线程应用程序的文件。
在配置文件中
<variable name="outPutFileFullName" value=""/>
<target xsi:type="File" name="OutPutFile" fileName="${mdc:item=outPutFileFullName}"
layout="${message}"/>
<logger name ="ABC"
level="Info" writeTo="OutPutFile"></logger>
在代码中
_loggingService.SetMappedDiagnosticsContext("outPutFileFullName", outPutFolderFile);
Parallel.ForEach(allItems
itemLine =>
{
itemLine.OutPutFileFullName = outPutFolderFile;
var pipeLine = new PipeLine<TEST>();
pipeLine.Register(new Operation1<TEST>(_loggingService))
.Register(new Operation2<TEST>(_loggingService))
.Register(new Operation3<TEST>(_loggingService))
.Execute(itemLine);
});
操作3我有一个简单的方法
private void WriteToFileFromObject(Test obj)
{
LoggingService.Info(obj.FileLineNumber.ToString());
}
我希望这个过程写入 100 条记录,但它总是只写入 17 条记录,但并不相同,也不按特定顺序。
如果我改变fileName="${mdc:item=outPutFileFullName}"
在配置文件中设置为常量值,如 fileName="${basedir}/logs/Test.log"
然后所有 100 条记录都写入文件。知道为什么吗?
MappedDiagnosticsContext 使用线程本地存储。 Parallel.ForEach 将使用其他 threads/tasks,它将无法访问您放入 MappedDiagnosticsContext 中的值(因为它是特定线程的本地值)。如果您只为应用程序设置此值一次,那么也许您可以使用 GlobalDiagnosticsContext。
我怀疑正在写入的 17 个项目来自主线程上发生的执行(即您设置 MDC 值的同一线程)。其余的可能发生在不同的线程上。由于这些线程的 MDC 中没有值,因此文件名为 null(或空)。
另外,我不明白你希望在你的配置中用这一行实现什么:
<variable name="outPutFileFullName" value=""/>
在 nlog.config 文件中声明变量允许您稍后在配置中使用该变量名。变量与引用 MDC/GDC 中的项目没有任何关系,无论如何也不直接。有关如何在 nlog.config:
中使用变量的一些示例,请参阅此答案Most useful NLog configurations
为了完整起见,我会提到 log4net 有一个 LogicalThreadContext(除了对应于 NLog 的 MDC 和 GDC 的上下文)。 LogicalThreadContext 使用 LogicalSetData 将值存储在 CallContext 中。在这种情况下,存储在 LogicalThreadContext 中的所有值对于任何子线程或任务都将是 "flowed"。因此,如果在线程 A 中存储一个名为 "Name" 的值,然后线程 A 启动一些子线程(或任务),则值 "Name" 将在每个子线程(或任务)的 LogicalThreadContext 中可用).
在您的示例中,如果您使用的是 log4net 并像这样在主线程中设置值:
log4net.LogicalThreadContext.Properties["outputFilename"] = outputFolderFile;
那么该值将在并行处理启动的所有 thread/tasks 中可用。
这是 Jeffrey Richter 的一篇文章,描述了 CallContext.LogicalSetData 的工作原理: