Jam*_*ing 14 c# logging log4net asynchronous async-await
在普通/同步/单线程控制台应用程序中,NDC.Push可以很好地管理"当前项目"(可能在多个嵌套级别,但对于此示例仅为1级).
例如:
private static ILog s_logger = LogManager.GetLogger("Program");
static void Main(string[] args)
{
BasicConfigurator.Configure();
DoSomeWork("chunk 1");
DoSomeWork("chunk 2");
DoSomeWork("chunk 3");
}
static void DoSomeWork(string chunkName)
{
using (NDC.Push(chunkName))
{
s_logger.Info("Starting to do work");
Thread.Sleep(5000);
s_logger.Info("Finishing work");
}
}
Run Code Online (Sandbox Code Playgroud)
这将导致期望日志输出,显示"程序"右侧的"块X"NDC条目(基本配置器的默认模式)
232 [9] INFO程序块1 - 开始工作
5279 [9] INFO计划块1 - 完成工作
5279 [9] INFO程序块2 - 开始工作
10292 [9] INFO计划块2 - 完成工作
10292 [9] INFO程序块3 - 开始工作
15299 [9] INFO计划块3 - 完成工作
但是,我无法弄清楚如何使用"普通"异步方法来维护它.
例如,尝试这样做:
private static ILog s_logger = LogManager.GetLogger("Program");
static void Main(string[] args)
{
BasicConfigurator.Configure();
var task1 = DoSomeWork("chunk 1");
var task2 = DoSomeWork("chunk 2");
var task3 = DoSomeWork("chunk 3");
Task.WaitAll(task1, task2, task3);
}
static async Task DoSomeWork(string chunkName)
{
using (log4net.LogicalThreadContext.Stacks["NDC"].Push(chunkName))
//using (log4net.ThreadContext.Stacks["NDC"].Push(chunkName))
{
s_logger.Info("Starting to do work");
await Task.Delay(5000);
s_logger.Info("Finishing work");
}
}
Run Code Online (Sandbox Code Playgroud)
显示它们都是"正常"启动,但是当任务在另一个线程上完成时,堆栈就会丢失(我希望log4net.LogicalThreadContext是TPL -'ware?').
234 [10] INFO程序块1 - 开始工作
265 [10] INFO程序块2 - 开始工作
265 [10] INFO程序块3 - 开始工作
5280 [7] INFO计划(null) - 完成工作
5280 [12] INFO计划(null) - 完成工作
5280 [12] INFO计划(null) - 完成工作
除了向log4net添加新的TaskContext(或类似内容)之外,有没有办法跟踪这种活动?
目标是使用async/await语法糖来实现这一目标 - 要么强制某种线程关联,要么像保持任务键入的并发字典一样可能是可行的选项,但我试图保持尽可能接近同步版本的代码尽可能.:)
Ste*_*ary 17
现在没有关于async
逻辑调用上下文的好故事.
CallContext
不能用于此.逻辑CallContext
不理解async
方法如何早期返回并稍后恢复,因此对于使用简单并行性的代码,它不会总是正常工作Task.WhenAll
.
更新: CallContext
已在.NET 4.5 RTW中更新,以正确使用async
方法.
我查看了log4net; LogicalThreadContext
记录为使用CallContext
,但有一个错误使它使用非逻辑上下文(在2012年2月2日的SVN中修复;当前的1.2.11版本不包括该修复).即使它被修复了,它仍然无法使用async
(因为逻辑CallContext
不起作用async
).
当我需要一个async
逻辑调用上下文时,我创建一个包含上下文数据的类,并将所有async
方法保持为函数样式,作为该类的实例成员.这肯定不是一个理想的解决方案,但它是一个有效的肮脏黑客.
与此同时,请提出微软为此提供一些机制的建议.
PS并发键入的并发字典Task
将不起作用,因为async
方法不一定是运行任务(即,在您的示例代码中,在using
语句中,Task.CurrentId
将是null
因为在该点没有任务实际执行).
线程亲和力也有其自身的问题.实际上,每个独立的异步操作最终都需要一个单独的线程.再见,可扩展性......