38

ASP.NET Core 日志框架:Serilog

 4 years ago
source link: http://beckjin.com/2020/02/14/aspnet-log-serilog/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

ASP.NET Core 日志模型 中对日志整体实现方式进行了介绍,通过使用内置日志记录器来实现日志的输出路径。而在实际项目开发中,使用第三方日志框架来记录日志也是非常多的,首先一般基础的内置日志记录器在第三方日志框架中都有实现,然后很多第三方日志框架在功能上更强大和丰富,能满足我们更多的项目分析和诊断的需求。

本文主要介绍 Serilog 这个日志框架的使用,它是目前比较突出和受欢迎的一个日志框架。 Serilog 在日志记录上采用 json 的格式,方便日志的快速查询与过滤。

默认日志行为

在使用 Serilog 之前我们先看看 ASP.NET Core API 项目默认的日志输出行为。创建基于 ASP.NET Core 3.1 的 API 项目后启动,默认访问会 weatherforecast 接口,然后可以从控制台和调试窗口看到如下输出日志 ( ASP.NET Core API 项目模板默认集成了 ConsoleDebug 日志记录器 ):

yAFFbmF.png!web

日志输出等级在配置文件中有默认设置参数, appsettings.Development.json (开发环境)和 appsettings.json (生产环境)的 Logging 节点。下面以 appsettings.Development.json 为例:

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

开发环境下默认输出 Debug 及以上等级日志, SystemMicrosoft 开头的日志 Information 及以上等级会输出,所以从上面的截图看到大批 Info 日志。

Serilog 使用

和内置日志记录器一样, Serilog 也有自己的日志记录器组件,NuGet 包命名上基本都是 Serilog.Sinks.xxx 的形式,如: Serilog.Sinks.ConsoleSerilog.Sinks.FileSerilog.Sinks.SeqSerilog.Sinks.ElasticSearch 等,接下来先以 Serilog.Sinks.Console 为例。

  1. 安装 Serilog.Settings.Configuration (读取配置)、 Serilog.Sinks.Console (日志输出到控制台) NuGet 包 ;
  2. 配置文件添加 Serilog 配置, WriteTo 指定输出目标位置,它是一个数组类型,所以可以指定多个目标位置,这里暂时只指定输出到控制台:

    {
      "Serilog": {
        "MinimumLevel": {
          "Default": "Debug"
        },
        "WriteTo": [
          { "Name": "Console" }
        ]
      }
    }
    
  3. Program.cs 添加 UseSerilog ,设置配置信息:

    public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
      .ConfigureWebHostDefaults(webBuilder =>
      {
          webBuilder.UseSerilog((context, logger) =>
          {
            logger.ReadFrom.Configuration(context.Configuration);
          });
    
          webBuilder.UseStartup<Startup>();
      });
    
  4. 在 Startup.cs 的 Configure 请求管道中添加 UseSerilogRequestLogging

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment())
      {
        app.UseDeveloperExceptionPage();
      }
    
      app.UseSerilogRequestLogging();
    
      app.UseRouting();
    
      app.UseAuthorization();
    
      app.UseEndpoints(endpoints =>
      {
        endpoints.MapControllers();
      });
    }
    
  5. 日志输出效果如下:

    AVb6fyy.png!web

    由于日志总是输出一堆,并不能区分什么日志来自 Serilog ,其实 Serilog 输出的日志是非常简洁的,只有 HTTP GET ... 这一条,其他都是 AspNetCore 本身输出的。

日志输出简化

为了使日志输出更简洁,我们可以设置不输出 AspNetCore Info 日志,只需在 Serilog 配置节点中设置 AspNetCore 日志输出级别为 Warning

{
  "Serilog": {
    "MinimumLevel": {
      "Default": "Debug",
      "Override": {
        "Microsoft.AspNetCore": "Warning"
      }
    },
    "WriteTo": [
      { "Name": "Console" }
    ]
  }
}

ri2YZjN.png!web

日志属性扩展

这部分我们将增加使用 Serilog.Sinks.Seq 日志记录器,不过在使用前需要先安装 Seq ,和 ElasticSearch 类似,主要提供日志存储和查询,安装非常简单,这里不再赘述,参考 Windows or Docker

Seq 服务启动后修改配置文件,在 WriteTo 节点内增加 Seq 的配置参数:

"WriteTo": [
  { "Name": "Console" },
  {
    "Name": "Seq",
    "Args": {
      "serverUrl": "http://localhost:5341/"
    }
  }
]

Seq 中查看日志如下,基于 ParentIdRequestIdTraceId 可实现链路跟踪:

aiUvymv.png!web

手动记录的日志如下:

eYJfyqy.png!web

虽然通过之前的配置使一次请求中系统日志只保留了一条,但相比之下可能丢失了一些重要信息,如: endpointcontentType 等,所以我们需要在日志体上增加一些扩展属性,做到数量少但内容丰富。

前面在 Startup 的 Configure 方法中添加过 UseSerilogRequestLogging ,该方法还支持更多的参数配置,通过指定 EnrichDiagnosticContext 委托方法的实现向 IDiagnosticContext 对象中添加扩展属性,这些扩展属性将会作用于请求日志体中。( Serilog.AspNetCore 已将接口 IDiagnosticContext 作为单例添加到了 DI 容器中,可以在任何类中通过其 Set 方法添加扩展属性

public static class LogUtil
{
  public static void EnrichDiagnosticContext(IDiagnosticContext diagnosticContext, HttpContext httpContext)
  {
    var request = httpContext.Request;
    diagnosticContext.Set("Host", request.Host);
    diagnosticContext.Set("ContentType", httpContext.Response.ContentType);
    var endpoint = httpContext.GetEndpoint();
    if (endpoint is object)
    {
      diagnosticContext.Set("EndpointName", endpoint.DisplayName);
    }
  }
}
app.UseSerilogRequestLogging(opts =>
{
  opts.EnrichDiagnosticContext = LogUtil.EnrichDiagnosticContext;
});

Seq 中查看日志如下,已包含添加的扩展属性:

muiU7rY.png!web

参考链接:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK