如何从ASP.NET Core中的控制器以外的其他类进行日志记录?

时间:2016-08-19 05:17:12

标签: asp.net logging asp.net-core

ASP.NET Core内置了对日志记录的支持,但文档指出应该通过请求ILogger依赖注入来完成日志记录,即将其作为参数添加到Controller构造

我的代码中使用ILogger参数的污染方法签名或构造函数对于这种跨领域的关注感觉是错误的解决方案。在Android中,Log类是静态的,使得从代码的任何部分进行日志记录变得微不足道。

从控制器以外的其他地方进行日志记录的好方法是什么?

3 个答案:

答案 0 :(得分:15)

关于在任何组件中使用日志记录:

  

通过依赖注入请求ILoggerFactoryILogger<T>来完成向应用程序中的组件添加日志记录。如果请求ILoggerFactory,则必须使用其CreateLogger方法创建记录器。

如果您的CustomClass是数据容器(DTO类),它不应该知道日志记录,而只是包含数据。

对于其他名称类似&#34; Service&#34; ,&#34;提供商&#34;,&#34;处理程序&#34;依此类推,最佳实践是使用依赖注入来解析实例。通常,您应该在任何可能的地方使用DI,因为这是一种在对象及其协作者或依赖关系之间实现松散耦合的技术。有关更多信息,以下问题可能很有趣:Should I use Dependency Injection or static factories?

因此,只需将ILogger<CustomClass>添加到其构造函数中(实际上与控制器相同),因为.NET Core默认只支持构造函数注入:

public class CustomClass
{
    private readonly ILogger _logger;

    public CustomClass(ILogger<CustomClass> logger)
    {
        _logger = logger;
    }
}

ASP.NET Core的内置依赖注入容器将自动解决瞬态依赖项。因此,如果您的控制器将类A作为依赖项使用您要记录内容的类B,那么您的代码可能是这样的:

public class MyController
{
    public MyController(ClassA classA)
    { ... }
}

public class ClassA
{
    public ClassA(ClassB classB)
    { ... }
}

public class ClassB
{
    private readonly ILogger _logger;

    public ClassB(ILogger<ClassB> logger)
    {
        _logger = logger;
    }

    public void DoSomethingWithLogging()
    {
        // use _logger
    }
}

请注意,您还需要使用Startup方法在IServiceCollection.Add…中注册依赖项:

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddTransient<ClassB>();
    services.AddTransient<ClassA>();
}

内置日志记录支持意味着开箱即用的.NET Core知道并使用内置抽象进行日志记录。这主要由ILoggerFactoryILoggerProvider接口完成。

/// <summary>
/// Represents a type used to configure the logging system and create instances of <see cref="ILogger"/> from
/// the registered <see cref="ILoggerProvider"/>s.
/// </summary>
public interface ILoggerFactory : IDisposable

// <summary>
/// Represents a type that can create instances of <see cref="ILogger"/>.
/// </summary>
public interface ILoggerProvider : IDisposable

使用您自己的ILoggerProvider实现,您可以添加自己的记录器,可以执行任何操作。您可以将NLog记录器实现检查为工作示例。

感谢ILoggerFactory,您可以根据项目特定目的配置Logger:

  

要在ASP.NET Core应用程序中配置日志记录,您应该在ILoggerFactory类的Configure方法中解析Startup。当您向ILoggerFactory方法添加此类型的参数时,ASP.NET Core将使用依赖注入自动提供Configure的实例。

答案 1 :(得分:5)

本文指出您应该使用DI在所有需要它的类中注入您的记录器: https://docs.asp.net/en/latest/fundamentals/logging.html

  

“此技术不仅限于控制器,也可以由任何使用依赖注入的应用程序服务使用。”

另一方面,这是一篇文章(微软赞助的),它提到了一种全局静态参考方法: https://msdn.microsoft.com/en-us/magazine/mt694089.aspx?f=255&MSPPError=-2147217396

  

“您可能会注意到,这需要访问先前配置了提供程序的同一个记录器工厂实例。虽然可以想象您可以将记录器工厂实例传递到您要执行日志记录的每个类中,但它会很快就会成为一个需要重构的麻烦。

     

解决方案是将单个静态ILoggerFactory保存为静态属性,在实例化其对象的特定ILoggger实例时可用于所有类。例如,考虑添加一个包含静态ILoggerFactory实例的ApplicationLogging静态类:“

没有试图引发静态与DI对比服务定位器:模式或反模式争论,我个人发现常见的跨领域问题,如日志和配置,以适应后一种方法,但两者都使用过去。

答案 2 :(得分:5)

共享Bjorn Reppens对已接受答案的担忧,并考虑Cyprien Autexiers的评论,我最终得到了这种“简化”的“内置DI”解决方案,而没有“污染控制器之外的其他类”中的“构造函数”。 .NET Core 3.1

工厂:

public class Runtime
{
    public static ILogger<T> GetLogger<T>()
    {
        using var serviceScope = host.Services.CreateScope();
        var services = serviceScope.ServiceProvider;
        return services.GetRequiredService<ILogger<T>>();
    }

    static IHost host;

    CancellationTokenSource CancellationSource { get; } = new CancellationTokenSource();

    Task hostTask;

    public void Start()
    {
        var hostBuilder = Host.CreateDefaultBuilder();
        hostBuilder.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
        host = hostBuilder.Build();
        hostTask = host.RunAsync(CancellationSource.Token);
    }
}

客户:

public class OtherClass
{
    ILogger Logger { get; }

    public OtherClass() => Logger = Runtime.GetLogger<OtherClass>();
}

进一步的抽象:

public abstract class LoggerBase<T>
{   
    protected ILogger Logger { get; }

    protected LoggerBase() => Logger = RunTime.GetLogger<T>();
}

例如

public class OtherClass : LoggerBase<OtherClass>
{
    public OtherClass() => Logger.LogInformation("Instantiated");
}