如何在 .NET Core 3.0 Worker 服务中使用 Microsoft.Extensions.Logging

How to use Microsoft.Extensions.Logging in a .NET Core 3.0 Worker Service

我开始了一个新的 Windows 服务项目,并决定使用 .NET Core 3.0 Worker Service 模板。此服务永远不会在 cloud/web 项目中使用,但我可能希望在某个时候将其适应 Linux 上的 运行。

我非常熟悉使用 NLog 进行日志记录。在每个 class 中创建一个新的静态记录器对象的模型非常直观。我看到 Worker Service 内置了 Microsoft.Extensions.Logging 框架,以至于默认的 worker 有一个 ILogger<Worker> 直接传入。这对 "my first service" 来说很好,但是真正的 long 呢? -运行ning 服务有很多 classes,每个都需要日志记录?

我为该模板找到的所有教程都涉及完成所有工作并登录 Worker class 的服务。由于历史原因,大多数日志记录文章只考虑 ASP.NET 核心,其中 DI 是优先考虑的。我没有找到任何关于如何使用多个长期存在的 class 树构建应用程序的描述,每个树都有自己的 ILogger 对象要写入。

尽管我在这个项目中永远不需要 DI,但它似乎是一个很好的标准,可以在我的代码库中采用,最初将它连接到 NLog 提供程序。我可以在我的 Main 中获取 IHostBuilder,然后添加一个静态函数,以类似于 NLog 的方式从它的 ILoggingBuilder 创建记录器,但我不相信这是最好的方法.

这里有既定的最佳实践吗?它是否涉及从 Worker Service 模板中解开一些魔法?

也许 Serilog https://serilog.net/ 值得一看? Serilog 非常灵活,您可以添加各种接收器,例如:console、file、elasticsearch、seq 等。而且您不必到处都使用 DI。

这就是我使用它打印到控制台 (std::out) 的方式。

using System;
using System.IO;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Serilog;
namespace YourNamespace
{
public class Program
{
    private static readonly string _applicationName = System.Reflection.Assembly.GetEntryAssembly().GetName().Name;
    public static IConfiguration Configuration = new ConfigurationBuilder()
              .SetBasePath(Directory.GetCurrentDirectory())
              .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
              .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", reloadOnChange: true, optional: true)
              .AddEnvironmentVariables()
              .Build();

    public static int Main(string[] args)
    {
        // serilog
        Log.Logger = new LoggerConfiguration()
                        .ReadFrom.Configuration(Configuration)
                        .Enrich.FromLogContext()
                        .CreateLogger();

        try
        {
            if (string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")))
                throw new Exception("Hosting environment variable not set");

            Log.Information($"Bootstrapping application: {_applicationName}");
            CreateWebHostBuilder(args).Build().Run();
            Log.Information($"Gracefully closing application: {_applicationName}");
            return 0; // returning a zero, indicates a successful shutdown.
        }
        catch (Exception e)
        {
            Log.Information($"Host {_applicationName} - terminated unexpectedly.", e);
            return 1; // returning a none-zero, indicates failure.
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .UseConfiguration(Configuration)
            .UseSerilog(); // overrides default logger
}
}

您现在可以在程序中的任何地方使用它,例如控制器

try 
{
   Log.Information("Testing the logs..");
}
catch (Exception e)
{      
   Log.Error("{@e}", e); // stack trace or other objects where {@e} is the object template.
   return BadRequest($"Rule violation: {e.Message}");
}

以及对应的配置(只写入控制台):

"Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": { // overrides default logger from MS
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },  
    "WriteTo": [
      {
        "Name": "Console",
        "Args": {
          "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {SourceContext}>{NewLine}{Exception}"
        }
      }
    ]
  }

还有 nugets:

    <PackageReference Include="Serilog" Version="2.9.0" />
    <PackageReference Include="Serilog.AspNetCore" Version="3.1.0" />
    <PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />

如果您确实想要 DI 记录器,也许您应该看看 Autofac DI 框架? 希望对您有所帮助。

NLog 在 .Net Core 中运行良好,无需依赖注入的任何帮助。你就像往常一样:

private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();

private static void Main()
{
    Logger.Info("Hello World");
}

但 NLog 还可以使用 Microsoft 依赖注入与 Microsoft 扩展日志记录 (MEL) 集成。您只需注册 NLog LoggingProvider,然后 MEL-ILogger 输入将转发到 NLog。

IHostBuilder.ConfigureLogging 中,然后可以调用 AddNLog()(就像可以为内置 MEL-LoggingProvider 调用 AddDebug()AddConsole() 一样)

您可以在维基页面上找到一个简单的示例(如果需要,还有 link 到 ASP.NET 核心):

https://github.com/NLog/NLog/wiki/Getting-started-with-.NET-Core-2---Console-application

有些人不喜欢 MEL-ILogger 必须通过 class-构造函数注入,即使是简单的 classes(太多混乱)。相反,他们只是直接使用 NLog.LogManager.GetCurrentClassLogger()