我有什麼是log4net中的錯誤,或者是我的錯誤理解。log4net LogicalThreadContext不起作用
我試圖使用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
類。我重新運行了我的初始測試,它工作。這對我這麼多人使用的產品來說太顯而易見了,所以我不得不假設我錯過了一些東西。
值得注意的是,這是[固定](https://issues.apache.org/jira/browse/LOG4NET-317)在log4net 1.2.12中,但似乎還沒有發佈日期呢; https://issues.apache.org/jira/secure/ReleaseNote.jspa?projectId=10690&version=12318546 –