.NET Core中异步日志记录范围

3
在.NET Core中,我希望支持这样的场景,即将代码包装在某些上下文中,例如:
using(new LoggingContext("Method 1"))
{

}

那些上下文还可以相互嵌套。
using(new LoggingContext("Method 1"))
{
   Method2();
}

void Method2() 
{
   using(new LoggingContext("Method 2"))
   {
      logger.Log("ERROR")
   }
}

调用logger.Log("ERROR")后,我希望它能记录传递的消息以及堆栈上下文标识符,例如:

Method 1 > Method 2 > ERROR

在单线程场景下非常容易 - 它需要一个全局可访问的上下文堆栈。在.NET Core中,例如是一些public static AsyncLocal<Stack<LoggingContext>>
然而,我不知道在多线程场景下实现类似行为的最佳方法是什么。
using(LoggingContext("Method 1"))
{
    Task.WaitAll(
       new Task(() => Method("Method A", "A"), 
       new Task(() => Method("Method B", "B")))
}

void Method(string contextName, string message)
{
   using(new LoggingContext(contextName)
   {
       _logger.Log(message)
   }
}

期望的日志应该是这样的:
Method 1 > Method A > A Method 1 > Method B > B
当然,一个任务可以在另一个任务中启动,情况会更加复杂。
还有一件事需要注意: 由于某些遗留原因,我无法在创建LoggingContext时传递所有依赖项,必须采用静态解决方案。
我能想到唯一的想法是在创建新任务时使用ExecutionContext.SupressFlow,并将当前堆栈复制到新的AsyncLocal对象中,但这是一个好的解决方案吗?修改执行上下文似乎对我来说有点不安全。
1个回答

0

一种选择是在创建新任务时手动克隆或扩展父日志上下文:

using(var parentContext = LoggingContext("Method 1"))
{
    Task.WaitAll(
       new Task(() => {
          using(parentContext.Clone()) 
          {
              Method("Method A", "A");
          }
       }), 
       ...
}

虽然不太美观,但你可以编写一些辅助方法来处理混乱的部分。例如:

using(var parentContext = LoggingContext("Method 1"))
{
    Task.WaitAll(
       parentContext.StartTask(() => Method("Method A", "A")),
       parentContext.StartTask(() => Method("Method B", "B")));
}

谢谢您回答我的问题,但就我理解您的帖子而言,它并没有解决我的问题。问题不在于如何复制堆栈,而在于如何为每个任务/上下文单独存储它。 - Szymon Knop
我不知道是否有一种自动方式可以使您的上下文被克隆到您创建的每个新任务中。我的建议解决方案只是为每个创建的任务显式地存储一个新的上下文。如果这不可能,那么可能可以通过TaskScheduler做些什么,但我不知道会是什么样子。 - StriplingWarrior

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接