Asp.net Core使用 Serilog以及 配置 appsettings.json

最近使用Asp.net Core进行项目开发,日志功能自然是必不可少的。现在使用Asp.net Core 2.2 版,它自带的Logger虽然好用,但是不支持保存到日志文件,所以一番搜索后决定使用Serilog这个第三方Log Provider。它的好处不多说,这里简单记录一下我自己的操作。

为什么要从配置文件里设置Serilog

因为这个项目最终是发布给客户的,C#源代码会隐藏,而项目的Log功能又要求能灵活配置,例如客户想指定不同的输出目标(Console,File)以及不同的Log 级别。此时Serilog官方默认的代码生成方式就不合适,因为它无法实现灵活配置。下面代码就固定了将Log只输出到Console而无法再更改:

我的解决办法

综合了Serilog的官方站点以及几个网页,最终总结如下。

1. 安装Package

使用Nuget package manger或者命令行方式添加如下Package,有些是可选的,看你自己是否需要用到。

  • Serilog.AspNetCore 必须
  • Serilog.Settings.Configuration 必须, 提供从配置文件读取Serilog的支持
  • Serilog.Sinks.Console 提供输出到Console的支持
  • Serilog.Sinks.File 提供输出到文件的支持
  • Serilog.Sinks.Async 提供异步输出log的支持
  • Serilog.Enrichers.Environment 提供Enricher里的MachiName支持
  • Serilog.Enrichers.Thread 提供Enricher里的ThreadId支持

2. 修改Program.cs生成Logger对象

我们按照习惯,会把Serilog相关的日志配置放置在appsetting.jso这个文件,当然,也可以放在根据开发环境不同而区分开的子配置文件里,比如appsettings.Development.json文件,因此Main()函数里添加如下语句:

var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var config = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
    .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
    .Build();

Log.Logger = new LoggerConfiguration()
    .ReadFrom.Configuration(config)
    .CreateLogger();

上面第一行的environment变量是为了支持读取从子配置文件而设置,可以看到上面的代码块实现了一个通用的对象生成,并没有进行任何Serilog相关的配置,符合我们要求。

然后在Build的时候添加一句useSerilog():

到此为止,Logger对象生成完毕。

3. 使用Logger对象

它的后续使用就和AspNet Core 自带的logger用法一样了。可以在Controller里通过依赖注入的方式生成1个Logger对象。采用Serilog的好处就是这段代码和采用自带Logger的代码完全一样,不需要任何修改,具体见下图:

上面6个函数对应自带Logger的6个等级,而Serilog 也相应地有对应的6个等级,只是有两个的名字略有差别,Trace -> Verbose, Critical -> Fatal,上述代码执行后,输出到Console的Log截图可以看出它们名字差别:

4. 详解配置文件

下面是配置文件的详细解释,首先把整个文件关于Serilog的配置放置在此:

{
  "Serilog": {
    "Using": [ "Serilog.Sinks.File", "Serilog.Sinks.Async", "Serilog.Sinks.Console" ],
    "MinimumLevel": {
      "Default": "Verbose",
      "Override": {
        "Microsoft": "Warning"
      }
    },
    "WriteTo": [
      {
        "Name": "Async",
        "Args": {
          "configure": [
            {
              "Name": "File",
              "Args": {
                "path": "log.txt",
                "rollingInterval": "Day",
                "restrictedToMinimumLevel": "Verbose"
              }

            }
          ]
        }

      },
      {
        "Name": "Console",
        "Args": {
          "restrictedToMinimumLevel": "Verbose",
          "outputTemplate": "{Timestamp:HH:mm:ss.fff zzz} [{Level}] {Message}  {NewLine}{Exception}"

        }

      }
    ],
    "Enrich": [ "WithMachineName", "WithThreadId" ],
    "Properties": {
      "Application": "MyApplicationName"
    }
  }
}

这里我综合了如下几个网页:
https://github.com/serilog/serilog-settings-configuration
https://github.com/serilog/serilog/wiki/Configuration-Basics
重点解释一下下面几行:

  • 第2行列出要使用的Sink,这里我用了3个,Console, File, Async
  • 第3行开始的MinimumLevel是Global设置,会影响所有Sink的允许输出的Log level,因此它要设置为各Sink Level的最小值。
  • 第10行开始的WriteTo 列出所有Sink的具体配置,它是一个Json Array。
  • 第18行开始的File文件属性,path指定文件路径及扩展名,rollingInterval 是自动生成log文件的格式,此配置下,每天会在当前路径下创建1个log文件,名字为logyyyymmdd.txt. restrictMinimumLevel 是Log输出等级,注意最低是Verbose,而不是用默认的Trace
  • Console里面示例了指定输出字符模板样式,可以任意添加修改各种字符。
  • 第38行的Enrich只出了Enricher需要的几个域,如果想添加这些域到每条Log message,则可以如下操作:
    "outputTemplate": "ZZ==> {Timestamp:HH:mm:ss.fff zzz} [{Level}] {Message} [TheadId={ThreadId}] \"{MachineName}\" {NewLine}{Exception}"
    上面代码就是访问了TrheadId和MachiName,输出的Log格式如下:
    ZZ==> 14:38:55.426 -06:00 [Warning] _logger.LogWarning [TheadId=10] "DESKTOP-TEST"

5. 默认输出模板

另外官方文档对默认的输出模板里的几个参数含义也做了解释:
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"

  • {Level:u3} or {Level:w3} for three-character upper- or lowercase level names, respectively.
    2019-07-22 14:38:24.556 -06:00 [DBG] _logger.LogDebug
    2019-07-22 14:38:24.562 -06:00 [INF] _logger.LogInformation
    2019-07-22 14:38:24.610 -06:00 [WRN] _logger.LogWarning
  • {Message:lj} format options cause data embedded in the message to be output in JSON (j