【问题标题】:Does injecting ILogger<T> create a new logger each time?每次注入 ILogger<T> 都会创建一个新的记录器吗?
【发布时间】:2018-06-02 18:08:24
【问题描述】:

the documentation 中的日志记录示例中,有一个如何将记录器注入控制器的示例:

public class TodoController : Controller
{
    private readonly ITodoRepository _todoRepository;
    private readonly ILogger _logger;

    public TodoController(ITodoRepository todoRepository,
        ILogger<TodoController> logger)
    {
        _todoRepository = todoRepository;
        _logger = logger;
    }
}

每当我将一个记录器注入到类似这里的东西时,DI 框架是否会创建一个新的记录器?有没有更好的办法?

【问题讨论】:

  • 取决于 ILogger 实现的注册方式。

标签: c# asp.net-core asp.net-core-2.0


【解决方案1】:

通过查看源代码很容易回答这个问题。当您执行services.AddLogging() 时,默认行为是ILogger&lt;T&gt; is registered as a singleton

public static IServiceCollection AddLogging(this IServiceCollection services, Action<ILoggingBuilder> configure)
{
    // …

    services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());
    services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));

    // …
}

所以不,只要应用程序正在运行,特定类型TILogger&lt;T&gt; 实例就会一直存在。因此,当将ILogger&lt;TodoController&gt; 注入控制器时,每次都会将相同的记录器实例传递给它。

当然,这仅适用于记录器,而不适用于您的控制器本身。默认情况下,控制器在 DI 之外激活,但有效地存在范围内的生命周期。所以每次请求都会有一个新的控制器实例;但是那个会从之前获取记录器实例。

要回答您的最后一个问题,有没有更好的方法?不。除了这种行为已经是一个很好的行为(因为不需要新的记录器实例),使用记录的正确方法确实是将ILogger&lt;T&gt;注入类型T,所以你得到一个正确分类记录器实例。当你可能永远看不到的背景中有很多更昂贵的事情发生时,真的没有必要担心这里非常薄的记录器;)


由于ILogger&lt;T&gt; 是一个单例,它的实例将在整个应用程序中被重用。请注意,这不会影响 日志记录范围。您在应用程序中使用的ILogger&lt;T&gt; 实现实际上只是一个瘦包装器,它将日志记录调用转发到内部记录器(实际上也是单例)。所以ILogger&lt;T&gt; 的生命周期实际上是不相关的,因为它们根本不保留任何状态。

日志范围本身使用AsyncLocal 进行持久化,这是一种在整个异步调用流程中保持状态的机制。这意味着日志范围只会“神奇地”工作,不会因为某些实例碰巧在多个线程(或异步流)之间共享而泄漏到调用流之外。

【讨论】:

  • “不需要新的记录器实例”:记录范围呢?如果并发请求共享一个记录器,它们似乎不希望共享通过ILoggerBeginScope() 添加的任何内容...
  • @KyleMcClellan 这不正确。您使用的记录器基本上只是实际(内部)记录器的包装器。如果您开始一个范围,那么如果您在该范围内登录,所有记录器(包括现有实例)都将应用该范围。
  • @poke 但是这些实例如何确定它们所在的BeginScope-scope?目前尚不清楚BeginScope 在属于使用单例ILogger(或使用注入ILoggerFactoryCreateLogger&lt;T&gt; 方法的类的多个实例)的单例类的方法中使用时如何工作。
  • @Dai 您使用的ILogger&lt;T&gt; 实现只是转发到底层内部记录器的shell,因此这些记录器的生命周期实际上并不相关。它们被尽可能地重复使用。日志范围使用异步本地持久化,以便它们正确地遵循您的调用流程(并且不会泄漏到外部)。
  • @GuillermoPrandi 正如我在您的第一个评论正上方的评论中所写:“日志范围使用异步本地人进行持久化,以便它们正确地遵循您的调用流程(并且不会泄漏到它之外) )。” – 所以不,这不是一种或另一种方式。
猜你喜欢
  • 2014-04-25
  • 1970-01-01
  • 1970-01-01
  • 2018-03-16
  • 1970-01-01
  • 2016-01-05
  • 1970-01-01
  • 2017-05-05
相关资源
最近更新 更多