0
点赞
收藏
分享

微信扫一扫

在 ASP.NET Core 中 使用 Serilog

代码小姐 2022-07-27 阅读 96

Serilog.AspNetCore

​​https://github.com/serilog/serilog-aspnetcore#two-stage-initialization​​

这是 Serilog 日志系统的 ASP.NET Core 支持包。该包将 ASP.NET Core 的日志信息路由到 Serilog,所以你可以得到关于 ASP.NET 的内部处理信息,与你的应用程序事件一起写入到同样的输出中。

当安装 ​​Serilog.AspNetCore​​​ 并配置之后,你可以直接通过 Serilog 写出日志消息,或者使用任何通过 ASP.NET 所注入的 ​​ILogger​​ 接口。所有的日志器将使用相同的底层实现、日志级别以及输出目标。

.NET Framework 和 .NET Core 2.x 由本包的 v3.4.0 版本所支持。最新版本的 ​​Serilog.AspNetCore​​ 需要 .NET 3.x、.NET 5 或者更新版本的支持。

使用说明

首先,安装 ​​Serilog.AspNetCore​​ NuGet 包到你的应用中

dotnet add package Serilog.AspNetCore

然后,在你的应用程序的 ​​Program.cs​​​ 文件中,先配置 Serilog,然后使用 ​​try/catch​​ 语句块来确保任何配置问题都会被正确记录下来。

using Serilog;
using Serilog.Events;

public class Program
{
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();

try
{
Log.Information("Starting web host");
CreateHostBuilder(args).Build().Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}

然后,使用 ​​UseSerilog()​​ 到 Host 上来。

var builder = WebApplication.CreateBuilder(args);

// Add this line to add Serilog
builder.Host.UseSerilog ();

最后,通过删除对于默认日志器完成清理工作。

  • 删除 appsetting.json 配置文件中的​​Logging​​ 配置节

完成!使用配置的日志级别,你将会看到类似如下输出

[22:14:44.646 DBG] RouteCollection.RouteAsync
Routes:
Microsoft.AspNet.Mvc.Routing.AttributeRoute
{controller=Home}/{action=Index}/{id?}
Handled? True
[22:14:44.647 DBG] RouterMiddleware.Invoke
Handled? True
[22:14:45.706 DBG] /lib/jquery/jquery.js not modified
[22:14:45.706 DBG] /css/site.css not modified
[22:14:45.741 DBG] Handled. Status code: 304 File: /css/site.css

提示:为了在运行在 IIS 下的时候,通过 Visual Studio 中的 Output 窗口中看到 Serilog 输出,或者从输出窗口的 ​​Show output​​​ 的下拉列表中选择 ​​ASP.NET Core Web Server​​​,或者将 ​​WriteTo.Console()​​​ 替换为 ​​WriteTo.Debug()​​。

更多完整示例,包括使用 ​​appsetting.json​​​ 配置,可以​​参考示例项目​​。

请求日志

本包中包括了一个用于智能处理 HTTP 请求日志的中间件。在默认的 ASP.NET Core 的请求日志实现中包含杂音。对于单个请求生成多个事件。本包所提供的中间件将它们合并为单个事件,包含了 Method、Path、Status Code 和计时信息。

以纯文本方式,示例格式如下

[16:05:54 INF] HTTP GET / responded 200 in 227.3253 ms

JSON 格式:

{
"@t": "2019-06-26T06:05:54.6881162Z",
"@mt": "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms",
"@r": ["224.5185"],
"RequestMethod": "GET",
"RequestPath": "/",
"StatusCode": 200,
"Elapsed": 224.5185,
"RequestId": "0HLNPVG1HI42T:00000001",
"CorrelationId": null,
"ConnectionId": "0HLNPVG1HI42T"
}

为了启用该中间件,首先在你的日志配置或者 ​​appsetting.json​​​ 中修改 ​​Microsoft.AspNetCore​​​ 的最小日志级别到 ​​Warning​

.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)

然后,在应用程序的 ​​Startup.cs​​​ 中,使用 ​​UseSerilogRequestLogging()​​ 启用该中间件:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}

app.UseSerilogRequestLogging(); // <-- Add this line

// Other app configuration

特别重要的是,​​UseSerilogRequestLogging()​​​ 调用要出现在像 MVC 这样的处理器之前。中间件并不能处理流水线中出现在它之前的组件 ( 这样可以排除一些杂音,比如通过将 ​​UseSerilogRequestLogging()​​​ 放在 ​​UseStaticFiles()​​ 之后 )

在请求处理过程中,附加的属性可以通过 ​​IDiagnosticContext.Set()​​ 来追加到完整的事件上。

public class HomeController : Controller
{
readonly IDiagnosticContext _diagnosticContext;

public HomeController(IDiagnosticContext diagnosticContext)
{
_diagnosticContext = diagnosticContext ??
throw new ArgumentNullException(nameof(diagnosticContext));
}

public IActionResult Index()
{
// The request completion event will carry this property
_diagnosticContext.Set("CatalogLoadTime", 1423);

return View();
}

通过这种模式可以压缩日志事件的数量,涉及到针对每个 HTTP 请求中构造、传输和存储。在同一事件中设置多个属性也可以使得关联请求的细节内容,以及其它数据更简单。

下列请求信息将被默认添加到属性上:

  • RequestMethod
  • RequestPath
  • StatusCode
  • Elapsed

你可以修改默认的请求事件的模板,添加附加的属性,或者变更事件级别,在 ​​UseSerilogRequestLogging()​​​ 回调方法中使用 ​​options​​ 参数设置:

app.UseSerilogRequestLogging(options =>
{
// Customize the message template
options.MessageTemplate = "Handled {RequestPath}";

// Emit debug-level events instead of the defaults
options.GetLevel = (httpContext, elapsed, ex) => LogEventLevel.Debug;

// Attach additional properties to the request completion event
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme);
};
});

两段式初始化

前面的示例展示了如何在应用启动的时候,立即配置 Serilog。这样可以得到通过异常捕获在 ASP.NET Core 宿主初始化阶段的信息。

首先初始化 Serilog 的缺点是在 ASP.NET Core 主机的服务,包括了 ​​appsetting.json​​ 的配置和依赖注入,将不能使用到。

为了处理这个问题,Serilog 支持两段式初始化,第一步在应用启动的时候,立即配置并初始化日志器,然后再主机加载之后,它被重新配置的日志器替换掉。

为了使用这个技术,首先使用 ​​CreateBootstrapLogger()​​​ 替换原来的 ​​CreateLogger()​​。

using Serilog;
using Serilog.Events;

public class Program
{
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateBootstrapLogger(); // <-- Change this line!

然后,在调用 ​​UseSerilog()​​ 的时候通过回调函数来创建最终的日志器。

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((context, services, configuration) => configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
.WriteTo.Console())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});

特别需要注意的是,最终的日志器完全替换了原来的启动日志器。比如说,如果你希望它们都输出到控制台 Console,那么你需要在两个地方都指定 ​​WriteTo.Console()​​,如上面示例。

支持 ​​appsetting.json​​ 配置

使用两段式初始化,插入 ​​ReadFrom.Configuration(context.Configuration)​​​ 调用,如上面示例所示,JSON 配置文件的语法见 ​​Serilog.Settings.Configuratio README 文档​​

将服务注入到 Enricher 和 Sink 中

在使用两段式初始化的时候,插入如上面示例所示的 ​​ReadFrom.Services(services)​​ 调用。该调用将使用任何注册的如下服务的实现来配置日志处理管线:

  • IDestructuringPolicy
  • ILogEventEnricher
  • ILogEventFilter
  • ILogEventSink
  • LoggingLevelSwitch

启用 Microsoft.Extensions.Logging.ILoggerProviders

Serilog 将事件发送到被称为 sink 的输出中,Serilog 的 ​​ILogEventSink​​​ 接口实现,并使用 ​​WriteTo​​​ 添加到日志处理管线中。​​Microsoft.Extensions.Logging​​​ 有类似的概念,不过被称为 provider。它实现 ​​ILoggerProvider​​ 接口。这些 provider 通过类似 AddConsole() 这样的方法在底层被创建出来。

默认情况下,Serilog 忽略这些 provider,因为它们等价于 Serilog 中存在的 sink。它们在 Serilog 的处理管线中更为高效。如果需要支持 provider,它也可以被可选地启用。

为了使 Serilog 将事件传递给 provider,在使用两段式初始化的示例中,提供 ​​writeToProviders: true​​​ 作为参数传递给 ​​UseSerilog()​

.UseSerilog(
(hostingContext, services, loggerConfiguration) => /* snip! */,
writeToProviders: true)

JSON 输出

在 ​​Console()、Debug()、File()​​​ 输出中,都原生支持 JSON 格式,通过包含 ​​Serilog.Formatting.Compact​​ NuGet 包。

输出新行分隔的 JSON,传递 ​​CompactJsonFormatter​​​ 或者 ​​RenderedCompactJsonFormatter​​ 到 sink 配置方法中。

.WriteTo.Console(new RenderedCompactJsonFormatter())

如果你希望在你的代码的特定部分对日志都增加额外的属性,你可以将它们添加到 ​​ILogger<>​​​ 中,在 Microsoft.Extensions.Logging 中使用如下代码。为了使得这些代码生效,确保你在 ​​.UseSErilog(...)​​​ 的内部添加了 ​​.Enrich.FromLogContext()​​ 语句,如上面示例所示。

// Microsoft.Extensions.Logging ILogger<T>
// Yes, it's required to use a dictionary. See https://nblumhardt.com/2016/11/ilogger-beginscope/
using (logger.BeginScope(new Dictionary<string, object>
{
["UserId"] = "svrooij",
["OperationType"] = "update",
}))
{
// UserId and OperationType are set for all logging events in these brackets
}

在 Serilog 中,使用如下代码:

// Serilog ILogger
using (logger.PushProperty("UserId", "svrooij"))
using (logger.PushProperty("OperationType", "update"))
{
// UserId and OperationType are set for all logging events in these brackets
}

.NET 6 示例

Program

// step #1 serilog boostrap
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateBootstrapLogger(); // <-- Change this line!

try
{
Log.Information("Starting web host");

var builder = WebApplication.CreateBuilder(args);

// step #2 build final serilog logger
builder.Host.UseSerilog((context, services, configuration) => configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
);

配置文件 appsetting.json

{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.AspNetCore": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{ "Name": "Console" },
{
"Name": "File",
"Args": {
"path": "./Logs/log-.txt",
"rollingInterval": "Day",
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],

"Properties": {
"Application": "Sample"
}
},
"AllowedHosts": "*"
}

Reference

  • ​​.NET 6.0 如何使用 Serilog 對應用程式事件進行結構化紀錄​​
举报

相关推荐

0 条评论