Я пытаюсь найти способ зарегистрировать полезный контекст от набора потоков. Проблема состоит в том, что с большим количеством кода имеют дело на Событиях, которые прибывают через потоки пула потоков (насколько я могу сказать), таким образом, их имена не относительно никакого контекста. Проблема может быть продемонстрирована со следующим кодом:
class Program
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
static void Main(string[] args)
{
new Thread(TestThis).Start("ThreadA");
new Thread(TestThis).Start("ThreadB");
Console.ReadLine();
}
private static void TestThis(object name)
{
var nameStr = (string)name;
Thread.CurrentThread.Name = nameStr;
log4net.ThreadContext.Properties["ThreadContext"] = nameStr;
log4net.LogicalThreadContext.Properties["LogicalThreadContext"] = nameStr;
log.Debug("From Thread itself");
ThreadPool.QueueUserWorkItem(x => log.Debug("From threadpool Thread: " + nameStr));
}
}
Шаблон Преобразования:
%date [%thread] %-5level %logger [%property] - %message%newline
Вывод похож так:
2010-05-21 15:08:02,357 [ThreadA] DEBUG LogicalContextTest.Program [{LogicalThreadContext=ThreadA, log4net:HostName=xxx, ThreadContext=ThreadA}] - From Thread itself
2010-05-21 15:08:02,357 [ThreadB] DEBUG LogicalContextTest.Program [{LogicalThreadContext=ThreadB, log4net:HostName=xxx, ThreadContext=ThreadB}] - From Thread itself
2010-05-21 15:08:02,404 [7] DEBUG LogicalContextTest.Program [{log4net:HostName=xxx}] - From threadpool Thread: ThreadA
2010-05-21 15:08:02,420 [16] DEBUG LogicalContextTest.Program [{log4net:HostName=xxx}] - From threadpool Thread: ThreadB
Поскольку Вы видите, что последние две строки не имеют никаких Названий полезной информации для различения 2 потоков кроме ручного добавления имени к сообщению (которого я хочу избежать). Как я могу получить Имя/Контекст в журнал для потоков пула потоков, не добавляя его к сообщению в каждом вызове или бывший должный установить свойство снова в каждом обратном вызове.
Контекстная информация в log4net предназначена для каждого потока, поэтому каждый раз, когда вы запускаете новый поток, вы нужно добавить к нему вашу контекстную информацию. Вы можете использовать свойства, или вы можете использовать NDC. NDC также существует для каждого потока, поэтому вам все равно придется добавлять его в контекст каждого потока в какой-то момент, что может быть, а может и не быть тем, что вы ищете. Однако это избавит вас от добавления его в само сообщение. В вашем примере это будет примерно так:
ThreadPool.QueueUserWorkItem(x => NDC.Push("nameStr")); log.Debug("From threadpool Thread: " + nameStr));
Вот ссылка на документацию для NDC .
В целом эффект аналогичен использованию свойств, как и в вашем примере. Единственное отличие состоит в том, что NDC можно складывать так, чтобы каждый раз, когда вы помещаете значение в стек, оно присоединяется к сообщению. Он также поддерживает оператор using, что делает код более чистым.
Из моего pov единственной возможностью было бы изменить создание потока внутри модулей, так как в противном случае вы не можете добавить какой-либо релевантный контекст.
Если вы можете изменить код, вы создадите класс, который будет наследовать от используемого класса System.Threading (например, Thread в вашем примере), а также вызовет суперкласс и добавит соответствующий контекст ведения журнала.
Возможны и другие трюки, но это был бы чистый подход без каких-либо грязных трюков.
Один из вариантов заключается в том, что вместо одного экземпляра статического средства ведения журнала можно создать его для каждого потока, пометив его атрибутом ThreadStatic и инициализировав в getter свойства. Затем вы можете добавить свой контекст в регистратор, и он будет применен к каждой записи журнала после установки контекста.
[ThreadStatic]
static readonly log4net.ILog _log;
static string log {
get {
if (null == _log) {
_log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
}
return _log;
}
}
Тем не менее, у вас все равно будет проблема с получением контекста, заданного в каждом потоке. Для этого я рекомендую абстрагироваться от создания ваших логгеров. Используйте фабричный метод и требуйте вызова метода CreateLogger() для извлечения экземпляра средств ведения журнала. На заводе-изготовителя используйте ThreadStatic и задайте свойство ThreadContext при инициализации средств ведения журнала.
Это требует небольшой модификации кода, но не тонны.
Более сложным вариантом является использование инфраструктуры AOP (аспектно-ориентированного программирования), такой как LinFu, для внедрения желаемого поведения журнала извне.