Как зарегистрировать корректный контекст с потоками Пула потоков с помощью log4net?

Я пытаюсь найти способ зарегистрировать полезный контекст от набора потоков. Проблема состоит в том, что с большим количеством кода имеют дело на Событиях, которые прибывают через потоки пула потоков (насколько я могу сказать), таким образом, их имена не относительно никакого контекста. Проблема может быть продемонстрирована со следующим кодом:

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 потоков кроме ручного добавления имени к сообщению (которого я хочу избежать). Как я могу получить Имя/Контекст в журнал для потоков пула потоков, не добавляя его к сообщению в каждом вызове или бывший должный установить свойство снова в каждом обратном вызове.

11
задан annemartijn 7 April 2014 в 07:13
поделиться

3 ответа

Контекстная информация в log4net предназначена для каждого потока, поэтому каждый раз, когда вы запускаете новый поток, вы нужно добавить к нему вашу контекстную информацию. Вы можете использовать свойства, или вы можете использовать NDC. NDC также существует для каждого потока, поэтому вам все равно придется добавлять его в контекст каждого потока в какой-то момент, что может быть, а может и не быть тем, что вы ищете. Однако это избавит вас от добавления его в само сообщение. В вашем примере это будет примерно так:

ThreadPool.QueueUserWorkItem(x => NDC.Push("nameStr")); log.Debug("From threadpool Thread: " + nameStr));

Вот ссылка на документацию для NDC .

В целом эффект аналогичен использованию свойств, как и в вашем примере. Единственное отличие состоит в том, что NDC можно складывать так, чтобы каждый раз, когда вы помещаете значение в стек, оно присоединяется к сообщению. Он также поддерживает оператор using, что делает код более чистым.

2
ответ дан 3 December 2019 в 07:11
поделиться

Из моего pov единственной возможностью было бы изменить создание потока внутри модулей, так как в противном случае вы не можете добавить какой-либо релевантный контекст.
Если вы можете изменить код, вы создадите класс, который будет наследовать от используемого класса System.Threading (например, Thread в вашем примере), а также вызовет суперкласс и добавит соответствующий контекст ведения журнала.
Возможны и другие трюки, но это был бы чистый подход без каких-либо грязных трюков.

0
ответ дан 3 December 2019 в 07:11
поделиться

Один из вариантов заключается в том, что вместо одного экземпляра статического средства ведения журнала можно создать его для каждого потока, пометив его атрибутом 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, для внедрения желаемого поведения журнала извне.

0
ответ дан 3 December 2019 в 07:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: