如何使用async/await方法管理类似NDC的log4net堆栈?(每个任务堆栈?)

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因为在该点没有任务实际执行).

线程亲和力也有其自身的问题.实际上,每个独立的异步操作最终都需要一个单独的线程.再见,可扩展性......