У меня нет ответа на вашу проблему, но у меня есть некоторые замечания и вопросы:
В соответствии с вашим кодом, похоже, что вы хотите создать регистратор для потока и хотитечтобы этот логгер записывал в файл имя некоторого переданного значения id.Таким образом, регистратор с идентификатором «abc» будет записывать в «x: \ abc.log», «def» будет записывать в «x: \ def.log» и так далее.Я подозреваю, что вы можете сделать это с помощью конфигурации NLog, а не программно.Я не знаю, будет ли это работать лучше, или если у NLog будет та же проблема, что и у вас.
Мое первое впечатление - вы выполняете большую работу: создаете файловую цель для потокасоздание нового правила для каждого потока, получение нового экземпляра регистратора и т. д., которое может не потребоваться для выполнения того, что, по-видимому, вы хотите выполнить.
Я знаю, что NLog позволяет выходному файлу бытьименуется динамически, основываясь, по крайней мере, на некоторых из NLog LayoutRenderers.Например, я знаю, что это работает:
fileName="${level}.log"
и даст вам такие имена файлов:
Trace.log
Debug.log
Info.log
Warn.log
Error.log
Fatal.log
Так, например, кажется, что вы могли бы использовать шаблон, подобный этомудля создания выходных файлов на основе идентификатора потока:
fileName="${threadid}.log"
И если у вас будут потоки 101 и 102, у вас будет два файла журнала: 101.log и 102.log.
В вашем случае вы хотите назвать файл на основе вашего собственного идентификатора.Вы можете сохранить идентификатор в MappedDiagnosticContext (это словарь, который позволяет хранить пары имя-значение потока-локально), а затем ссылаться на него в своем шаблоне.
Ваш шаблон для вашего имени файла будет выглядеть примерно так:
fileName="${mdc:myid}.log"
Итак, в вашем коде вы можете сделать это:
public class ThreadManager
{
//Get one logger per type.
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
protected override void OnDoWork(DoWorkEventArgs e)
{
// Set the desired id into the thread context
NLog.MappedDiagnosticsContext.Set("myid", myRandomID);
logger.Info("Hello from thread {0}, myid {1}", Thread.CurrentThread.ManagedThreadId, myRandomID);
base.OnDoWork(e);
//Clear out the random id when the thread work is finished.
NLog.MappedDiagnosticsContext.Remove("myid");
}
}
Что-то вроде этого должно позволить вашему классу ThreadManager иметь единственный регистратор с именем "ThreadManager".Каждый раз, когда он регистрирует сообщение, он регистрирует отформатированную строку в вызове Info.Если регистратор настроен для входа в цель File (в файле конфигурации создайте правило, которое отправляет «* .ThreadManager» в цель File, расположение файла которой выглядит примерно так:
fileName="${basedir}/${mdc:myid}.log"
В то времясообщение заносится в журнал, NLog определит, каким должно быть имя файла, на основе значения макета fileName (т.е. он применяет маркеры форматирования во время регистрации). Если файл существует, то сообщение записывается в него. Если файлеще не существует, файл создан, и сообщение заносится в него.
Если у каждого потока есть случайный идентификатор, такой как "aaaaaaaaaaaa", "aaaaaaaaaaab", "aaaaaaaaaaac", то вы должны получить файлы журнала, такие какэто:
aaaaaaaaaaaa.log
aaaaaaaaaaab.log
aaaaaaaaaaac.log
и т. д.
Если вы можете сделать это таким образом, то ваша жизнь должна быть проще, поскольку вам не нужно все эти программные конфигурации NLog (созданиеправила и цели для файлов). И вы можете позволить NLog беспокоиться о создании имен выходных файлов.
Я точно не знаю, будет ли это работать лучше, чем выпрежде чем делать.Или, даже если это произойдет, вам действительно может понадобиться то, что вы делаете в своей общей картине.Это должно быть достаточно легко проверить, чтобы увидеть, что он даже работает (то есть, что вы можете назвать свой выходной файл на основе значения в MappedDiagnosticContext).Если это работает для вас, то вы можете попробовать это для вашего случая, когда вы создаете тысячи потоков.
ОБНОВЛЕНИЕ:
Вот пример кода:
Используя этоПрограмма:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NLog;
using System.Threading;
using System.Threading.Tasks;
namespace NLogMultiFileTest
{
class Program
{
public static Logger logger = LogManager.GetCurrentClassLogger();
static void Main(string[] args)
{
int totalThreads = 50;
TaskCreationOptions tco = TaskCreationOptions.None;
Task task = null;
logger.Info("Enter Main");
Task[] allTasks = new Task[totalThreads];
for (int i = 0; i < totalThreads; i++)
{
int ii = i;
task = Task.Factory.StartNew(() =>
{
MDC.Set("id", "_" + ii.ToString() + "_");
logger.Info("Enter delegate. i = {0}", ii);
logger.Info("Hello! from delegate. i = {0}", ii);
logger.Info("Exit delegate. i = {0}", ii);
MDC.Remove("id");
});
allTasks[i] = task;
}
logger.Info("Wait on tasks");
Task.WaitAll(allTasks);
logger.Info("Tasks finished");
logger.Info("Exit Main");
}
}
}
И этот файл NLog.config:
<?xml version="1.0" encoding="utf-8" ?>
<!--
This file needs to be put in the application directory. Make sure to set
'Copy to Output Directory' option in Visual Studio.
-->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="file" xsi:type="File" layout="${longdate} | ${processid} | ${threadid} | ${logger} | ${level} | id=${mdc:id} | ${message}" fileName="${basedir}/log_${mdc:item=id}.txt" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="file" />
</rules>
</nlog>
Я могу получить один файл журнала для каждого выполнения делегата.Файл журнала назван по имени «id», хранящемуся в MDC (MappedDiagnosticContext).
Итак, когда я запускаю пример программы, я получаю 50 файлов журнала, в каждом из которых есть три строки «Enter... "," Привет ... "," Выход ... ".Каждый файл называется log__X_.txt
, где X - значение захваченного счетчика (ii), поэтому у меня есть log_ 0 .txt, log_ 1 .txt, log_ 1 .txt и т. Д., Log_ 49 .txt.Каждый файл журнала содержит только те сообщения журнала, которые относятся к одному выполнению делегата.
Это похоже на то, что вы хотите сделать? В моем примере программы используются задачи, а не потоки, потому что я уже писал это некоторое время назад. Я думаю, что техника должна адаптироваться к тому, что вы делаете достаточно легко.
Вы также можете сделать это таким образом (получить новый регистратор для каждого исключения делегата), используя тот же файл NLog.config:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NLog;
using System.Threading;
using System.Threading.Tasks;
namespace NLogMultiFileTest
{
class Program
{
public static Logger logger = LogManager.GetCurrentClassLogger();
static void Main(string[] args)
{
int totalThreads = 50;
TaskCreationOptions tco = TaskCreationOptions.None;
Task task = null;
logger.Info("Enter Main");
Task[] allTasks = new Task[totalThreads];
for (int i = 0; i < totalThreads; i++)
{
int ii = i;
task = Task.Factory.StartNew(() =>
{
Logger innerLogger = LogManager.GetLogger(ii.ToString());
MDC.Set("id", "_" + ii.ToString() + "_");
innerLogger.Info("Enter delegate. i = {0}", ii);
innerLogger.Info("Hello! from delegate. i = {0}", ii);
innerLogger.Info("Exit delegate. i = {0}", ii);
MDC.Remove("id");
});
allTasks[i] = task;
}
logger.Info("Wait on tasks");
Task.WaitAll(allTasks);
logger.Info("Tasks finished");
logger.Info("Exit Main");
}
}
}