既然ILogger<T>是单例,那么不同线程如何使用BeginScope()而不影响其他线程呢?

Gui*_*ndi 12 dependency-injection .net-core

这与这个问题有关。上下文:.Net Core 3.1,使用Microsoft.Extensions.Logging

记录器是应用程序的IHost. 如果我将 (DI) an 注入ILogger<T>到我的类或方法中,则注入的对象与其他类或方法在请求ILogger<T>. 这就提出了当我在一个线程中使用时会发生什么的问题logger.BeginScope($"Processing {transactionId}")。其他线程会发生什么情况?他们也会改变日志记录范围吗?日志记录范围是否会混淆?如果他们不这样做:那是如何工作的,因为他们的记录器是同一个对象?如果它们混合作用域,我怎样才能使两个线程对给定ILogger<T>类型使用不同的日志记录作用域?

Mat*_*hew 17

这取决于记录器实现,但通常它们是使用AsyncLocal中保存的堆栈类型来实现的。

调用BeginScope会将一个新项目放入该堆栈中,并且相邻的项目Dispose会将其从该堆栈中弹出。

当通过或以其他方式调用记录器时LogInformation,当前堆栈对象的数据将被复制以将其写入控制台或记录器实例配置为执行的任何输出。

AsyncLocal使得框架能够跨线程和任务存储信息。

作为参考,请查看Microsoft.Extensions.Logging.Console源代码:

  • @GuillermoPrandi如果您想启动后台任务,那么我通常建议您不要简单地启动它们,而是考虑对它们进行排队,以便您可以控制并行执行的数量(以检查线程池)。例如,您可以使用 [`BackgroundTaskQueue` 配方](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&amp;tabs=visual-studio#排队后台任务)。由于执行是从托管服务运行的,因此您还可以从那里的干净日志记录范围开始。 (3认同)
  • 这种行为是否有官方记录?因为它有一些非常讨厌的陷阱。例如,如果您在构造函数中启动一个日志记录作用域并在“Dispose()”中对其进行处理,那么很自然地会假设该作用域将附加到类的生命周期中。然而,情况并非如此,我观察到实例化多个类时日志记录范围“堆叠”,考虑到实现,这现在是有意义的。我想知道为什么选择这种设计而不是返回子记录器的“BeginScope()”,类似于“IServiceProvider”的工作方式。 (2认同)