Nlog没有将所有行写入并行循环中的文件

时间:2015-01-06 11:03:00

标签: c#-4.0 logging visual-studio-2013 nlog parallel.foreach

我正在使用Nlog根据要求我在并行循环中处理几千条记录

对于每个项目按特定顺序执行多个操作(Pipe Fliter模式)后,我想将特定记录(无法加载到数据存储区中)写入Output文件夹。

此Output文件夹不是{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条记录都写入文件。任何想法为什么?

1 个答案:

答案 0 :(得分:2)

MappedDiagnosticsContext使用线程本地存储。 Parallel.ForEach将使用其他线程/任务,这些线程/任务将无法访问您在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中的所有值都将被&#34;流动&#34;任何子线程或任务。因此,如果在线程A中存储一个名为&#34; Name&#34;的值,然后线程A启动一些子线程(或任务),则值为&#34; Name&#34;将在每个子线程(或任务)的LogicalThreadContext中提供。

在您的示例中,如果您使用的是log4net并在主线程中设置值,请执行以下操作:

log4net.LogicalThreadContext.Properties["outputFilename"] = outputFolderFile;

然后该值将在并行处理启动的所有线程/任务中可用。

以下是Jeffrey Richter撰写的一篇文章,描述了CallContext.LogicalSetData的工作原理:

http://www.wintellect.com/blogs/jeffreyr/logical-call-context-flowing-data-across-threads-appdomains-and-processes