У меня есть ошибка в log4net или недоразумение с моей стороны.
Я пытаюсь использовать LogicalThreadContext
, чтобы связать некоторые данные с контекстом вызова и распространить их на любой журналзаявления, сделанные любым потоком в этом контексте.Это предполагаемое преимущество LogicalThreadContext
над ThreadContext
.
. Я не смог заставить распространение работать, поэтому я собрал простой модульный тест, чтобы посмотреть, будет ли он работать, и это не«т.Вот оно:
[Fact]
public void log4net_logical_thread_context_test()
{
XmlConfigurator.Configure();
var log = LogManager.GetLogger(GetType());
var waitHandle = new ManualResetEvent(false);
using (LogicalThreadContext.Stacks["foo"].Push("Some contextual info"))
{
log.Debug("START");
ThreadPool.QueueUserWorkItem(delegate
{
log.Debug("A DIFFERENT THREAD");
waitHandle.Set();
});
waitHandle.WaitOne();
log.Debug("STOP");
}
}
Моя конфигурация log4net выглядит следующим образом:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<file value="log.txt" />
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="[%thread]|[%property{foo}]|%message%newline"/>
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="FileAppender" />
</root>
</log4net>
</configuration>
И мой вывод выглядит так:
[xUnit.net STA Test Execution Thread]|[Some contextual info]|START
[32]|[(null)]|A DIFFERENT THREAD
[xUnit.net STA Test Execution Thread]|[Some contextual info]|STOP
Как видите,данные, которые я помещаю в стек LTC, присутствуют только в операторах регистрации, сделанных в том же потоке .В операторе журнала, выполненном фоновым потоком, отсутствуют контекстные данные.Отладывая тест, я увидел, что на самом деле LogicalThreadContext.Stacks.Count
равен нулю в фоновом потоке.
Копаясь в источник log4net, я обнаружил, что он использует класс CallContext
.Этот класс делает то, что говорит на жестяной коробке - он позволяет контексту для текущего «вызова» быть сохраненным и извлеченным.Как это происходит на низком уровне, я понятия не имею.
CallContext
имеет два набора методов, с помощью которых можно сохранять и извлекать контекстную информацию: GetData
/ SetData
и LogicalGetData
/LogicalSetData
.В документации очень мало информации о разнице между этими двумя наборами методов, но в примерах используется GetData
/ SetData
.Так же, как и в log4net LogicalThreadContext
.
. Быстрый тест показал, что GetData
/ SetData
демонстрирует ту же проблему - данные не распространяются по потокам.Я подумал, что вместо этого пошлю LogicalGetData
/ LogicalSetData
:
[Fact]
public void call_context_test()
{
XmlConfigurator.Configure();
var log = LogManager.GetLogger(GetType());
var count = 5;
var waitHandles = new ManualResetEvent[count];
for (var i = 0; i < count; ++i)
{
waitHandles[i] = new ManualResetEvent(false);
var localI = i;
// on a bg thread, set some call context data
ThreadPool.QueueUserWorkItem(delegate
{
CallContext.LogicalSetData("name", "value " + localI);
log.DebugFormat("Set call context data to '{0}'", CallContext.LogicalGetData("name"));
var localWaitHandle = new ManualResetEvent(false);
// then on another bg thread, make sure the logical call context value is correct with respect to the "owning" bg thread
ThreadPool.QueueUserWorkItem(delegate
{
var value = CallContext.LogicalGetData("name");
log.DebugFormat("Retrieved call context data '{0}'", value);
Assert.Equal("value " + localI, value);
localWaitHandle.Set();
});
localWaitHandle.WaitOne();
waitHandles[localI].Set();
});
}
foreach (var waitHandle in waitHandles)
{
waitHandle.WaitOne();
}
}
Этот тест пройден - контекстная информация успешно распространяется по потокам при использовании LogicalGetData
/ LogicalSetData
.
Итак, мой вопрос таков: не так ли в log4net?Или я что-то упускаю?
ОБНОВЛЕНИЕ: Я также попытался выполнить пользовательскую сборку log4net с ее классом LogicalThreadContextProperties
, измененным согласно моим выводам выше.Я перезапустил свой первоначальный тест, и он сработал.Это кажется мне слишком очевидной проблемой для продукта, который используют многие люди, поэтому я должен предположить, что что-то упустил.