2015-01-06 1 views
2

Я использую NLog согласно требованию Я обрабатываю несколько тысяч записей в параллельном циклеNlog не писать все строки в файл в параллельном цикле

Для каждого элемента После нескольких операций в, специфических последовательностях (Pipe Fliter шаблон) Я хочу, чтобы записывайте записи с особыми значениями (которые не загружаются в хранилище данных) в папку «Вывод».

Эта папка вывода не является {basedir}, но специфична для каждого клиента, поэтому я использую nlog и nlog Variable и assoicated configuration, как показано ниже, для записи значений в файл для многопоточного приложения.

В файле конфигурации

<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 записей записываются в файл. Любая идея Почему?

ответ

2

MappedDiagnosticsContext использует локальное хранилище потоков. Parallel.ForEach будет использовать другие потоки/задачи, которые не будут иметь доступ к значению, которое вы помещаете в MappedDiagnosticsContext (так как это локально для определенного потока). Если вы устанавливаете это значение только для приложения, возможно, вы можете использовать GlobalDiagnosticsContext.

Я подозреваю, что 17 элементов, которые записываются, выполняются из выполнения, которые происходят в основном потоке (то есть в том же потоке, где вы устанавливаете значение MDC). Остальные, вероятно, происходят в другой теме. Поскольку в MDC нет значения для MDC, имя файла равно null (или пустому).

Кроме того, я не понимаю, что вы надеетесь достичь с помощью этой строки в конфигурации:

<variable name="outPutFileFullName" value=""/> 

Объявление переменной в файле nlog.config позволяет использовать это имя переменной позже на конфигурации. Переменная не имеет ничего общего, не в любом случае, со ссылкой на элемент из MDC/GDC. Смотрите этот ответ на некоторые примеры того, как использовать переменные в nlog.config:

Most useful NLog configurations

Для полноты я упомяну, что log4net имеет LogicalThreadContext (в дополнение к контекстам, которые соответствуют MDC и GDC NLog в). LogicalThreadContext хранит значения в CallContext, используя LogicalSetData. В этом случае все значения, хранящиеся в LogicalThreadContext, будут «течь» на любые дочерние потоки или задачи. Таким образом, если в потоке A вы сохраняете значение под названием «Имя», а затем поток A запускает некоторые подпрограммы (или задачи), значение «Имя» будет доступно в LogicalThreadContext в каждом из этих субтитров (или задач).

В вашем примере, если вы используете log4net и установите значение в основном потоке, как это:

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

, то значение будет доступно во всех резьбовых/задач, которые стартовали на параллельная обработка.

Вот статья Джеффри Рихтером, который описывает, как работает CallContext.LogicalSetData:

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