如何利用Serilog的RequestLogging来精简ASP.NET Core的日志输出
這是該系列的第一篇文章:在ASP.NET Core 3.0中使用Serilog.AspNetCore。
第1部分-使用Serilog RequestLogging來簡化ASP.NET Core的日志輸出(本篇文章)
第2部分-使用Serilog記錄所選的端點(diǎn)名稱[敬請(qǐng)期待]
第3部分-使用Serilog.AspNetCore記錄MVC屬性[敬請(qǐng)期待]
作者:依樂祝
譯文地址:https://www.cnblogs.com/yilezhu/p/12215934.html
原文地址:https://andrewlock.net/using-serilog-aspnetcore-in-asp-net-core-3-reducing-log-verbosity/
眾所周知,ASP.NET Core的重要改變之一是把日志記錄內(nèi)置于框架中。這意味著您可以(如果需要)從自己的標(biāo)準(zhǔn)日志基礎(chǔ)設(shè)施訪問所有深層基礎(chǔ)設(shè)施日志。缺點(diǎn)是有時(shí)您會(huì)收到太多的日志。
在這個(gè)簡短的系列文章中,我將介紹如何使用Serilog的ASP.NET Core請(qǐng)求日志記錄功能。在第一篇文章中,我將講述如何將Serilog的RequestLoggingMiddleware添加到您的應(yīng)用程序,以及它提供的好處。在后續(xù)文章中,我將描述如何進(jìn)一步自定義行為。
我已經(jīng)將這些帖子草擬了一段時(shí)間。從那時(shí)起,Serilog的創(chuàng)建者Nicholas Blumhardt就在ASP.NET Core 3.0中使用Serilog撰寫了一篇詳盡的博客文章。這是一篇非常詳細(xì)(至少我認(rèn)為是這樣)的文章,我強(qiáng)烈建議您閱讀。您可以在他的文章中找到我在本系列文章中談?wù)摰拇蟛糠謨?nèi)容,所以請(qǐng)查看!
原生請(qǐng)求日志
在本節(jié)中,首先讓我們創(chuàng)建一個(gè)標(biāo)準(zhǔn)的ASP.NET Core 3.0的Razor pages應(yīng)用,當(dāng)然你也可以直接使用dotnet new webapp命令來進(jìn)行創(chuàng)建。這將創(chuàng)建一個(gè)標(biāo)準(zhǔn)Program.cs,如下所示:
public class Program{public static void Main(string[] args){CreateHostBuilder(args).Build().Run();}public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>{webBuilder.UseStartup<Startup>();});}還有一個(gè)Startup.cs,用于配置中間件管道,Configure如下所示:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}else{app.UseExceptionHandler("/Error");// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.app.UseHsts();}app.UseHttpsRedirection();app.UseStaticFiles();app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapRazorPages();});}如果您運(yùn)行該應(yīng)用程序并導(dǎo)航至主頁,則默認(rèn)情況下,您會(huì)在控制臺(tái)中看到每個(gè)請(qǐng)求都會(huì)產(chǎn)生許多的日志。以下日志是針對(duì)對(duì)主頁的單個(gè)請(qǐng)求生成的(此后我還沒有包括對(duì)CSS和JS文件的其他請(qǐng)求)(這是是開發(fā)環(huán)境請(qǐng)求出現(xiàn)的日志):
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]Request starting HTTP/2 GET https://localhost:5001/ info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]Executing endpoint '/Index' info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[3]Route matched with {page = "/Index"}. Executing page /Index info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[101]Executing handler method SerilogRequestLogging.Pages.IndexModel.OnGet - ModelState is Valid info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[102]Executed handler method OnGet, returned result . info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[103]Executing an implicit handler method - ModelState is Valid info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[104]Executed an implicit handler method, returned result Microsoft.AspNetCore.Mvc.RazorPages.PageResult. info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[4]Executed page /Index in 221.07510000000002ms info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]Executed endpoint '/Index' info: Microsoft.AspNetCore.Hosting.Diagnostics[2]Request finished in 430.9383ms 200 text/html; charset=utf-8單個(gè)請(qǐng)求就是10條日志。現(xiàn)在,很清楚,它正在Development環(huán)境中運(yùn)行,該環(huán)境默認(rèn)情況下將Microsoft名稱空間中的所有信息記錄在“Information”或更高的級(jí)別。如果我們切換到Production環(huán)境,則默認(rèn)模板會(huì)將Microsoft命名空間的日志過濾到“Warning” 。現(xiàn)在導(dǎo)航到默認(rèn)主頁會(huì)生成以下日志(這里注意,如果你現(xiàn)在使用ASP.NET Core3.1貌似Microsoft命名空間默認(rèn)日志級(jí)別已經(jīng)改為Warning):
是的,根本沒有日志!上一次運(yùn)行中生成的所有日志都位于Microsoft命名空間中,并且屬于“Information”級(jí)別,因此將它們?nèi)窟^濾掉。就個(gè)人而言,我覺得這有點(diǎn)麻煩。如果生產(chǎn)版本僅僅只是想記錄一部分內(nèi)容,而其他相關(guān)聯(lián)的內(nèi)容則不進(jìn)行記錄,這將會(huì)更有用的。
一種可能的解決方案是自定義應(yīng)用于每個(gè)命名空間的過濾器。例如,您可以將Microsoft.AspNetCore.Mvc.RazorPages命名空間限制為“Warning”級(jí)別,而將更通用的Microsoft命名空間保留為“Information”級(jí)別。現(xiàn)在,您將獲得精簡后的日志集:
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]Request starting HTTP/2 GET https://localhost:5001/ info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]Executing endpoint '/Index' info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]Executed endpoint '/Index' info: Microsoft.AspNetCore.Hosting.Diagnostics[2]Request finished in 184.788ms 200 text/html; charset=utf-8這些日志中包含一些有用的信息-URL,HTTP方法,時(shí)間信息,端點(diǎn)等-并且沒有太多冗余。但是,仍然令人討厭的是它們是四個(gè)單獨(dú)的日志消息。(還是很多,如果能精簡成一條日志記錄是不是會(huì)好很多)
這是Serilog?RequestLoggingMiddleware旨在解決的問題-為請(qǐng)求中的每個(gè)步驟創(chuàng)建單獨(dú)的日志相反,它是創(chuàng)建一個(gè)包含所有相關(guān)信息的“摘要”日志消息。
將Serilog添加到應(yīng)用程序
使用Serilog?RequestLoggingMiddleware?的一個(gè)前提條件就是您正在使用Serilog!在本節(jié)中,我將介紹將Serilog添加到ASP.NET Core應(yīng)用程序中。如果您已經(jīng)安裝了Serilog,請(qǐng)?zhí)料乱徊糠帧?/p>
首先安裝Serilog.AspNetCore?NuGet軟件包,再加上控制臺(tái)和Seq接收器【這是一個(gè)漂亮的可視化日志UI】,以便我們可以查看日志。您可以通過運(yùn)行以下命令從命令行執(zhí)行此操作:
dotnet add package Serilog.AspNetCore dotnet add package Serilog.Sinks.Seq現(xiàn)在該用Serilog替換默認(rèn)日志了。您可以通過多種方式執(zhí)行此操作,但是建議的方法是在Program.Main?執(zhí)行其他任何操作之前先配置記錄器。這與ASP.NET Core通常使用的方法背道而馳,但建議用于Serilog。當(dāng)然這會(huì)導(dǎo)致您的的Program.cs文件變得更長:
// Additional required namespaces using Serilog; using Serilog.Events;namespace SerilogDemo {public class Program{public static void Main(string[] args){// Create the Serilog logger, and configure the sinksLog.Logger = new LoggerConfiguration().MinimumLevel.Debug().MinimumLevel.Override("Microsoft", LogEventLevel.Information).Enrich.FromLogContext().WriteTo.Console().WriteTo.Seq("http://localhost:5341").CreateLogger();// Wrap creating and running the host in a try-catch blocktry{Log.Information("Starting host");CreateHostBuilder(args).Build().Run();}catch (Exception ex){Log.Fatal(ex, "Host terminated unexpectedly");}finally{Log.CloseAndFlush();}}public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).UseSerilog().ConfigureWebHostDefaults(webBuilder =>{webBuilder.UseStartup<Startup>();});} }盡管這樣設(shè)置可能顯得更為復(fù)雜,但是此設(shè)置可確保例如在appsettings.json文件格式錯(cuò)誤或缺少配置文件的情況下仍會(huì)獲取日志。如果現(xiàn)在運(yùn)行您的應(yīng)用程序,您將看到與我們最初相同的10條日志,只是格式略有不同:
[13:30:27 INF] Request starting HTTP/2 GET https://localhost:5001/ [13:30:27 INF] Executing endpoint '/Index' [13:30:27 INF] Route matched with {page = "/Index"}. Executing page /Index [13:30:27 INF] Executing handler method SerilogRequestLogging.Pages.IndexModel.OnGet - ModelState is Valid [13:30:27 INF] Executed handler method OnGet, returned result . [13:30:27 INF] Executing an implicit handler method - ModelState is Valid [13:30:27 INF] Executed an implicit handler method, returned result Microsoft.AspNetCore.Mvc.RazorPages.PageResult. [13:30:27 INF] Executed page /Index in 168.28470000000002ms [13:30:27 INF] Executed endpoint '/Index' [13:30:27 INF] Request finished in 297.0663ms 200 text/html; charset=utf-8現(xiàn)在,通過在應(yīng)用程序生命周期的早期進(jìn)行配置,我們的日志記錄配置的更加健壯,但實(shí)際上尚未解決我們提出的問題。為此,我們將添加RequestLoggingMiddleware。
切換到Serilog的?RequestLoggingMiddleware
RequestLoggingMiddleware被包含在Serilog.AspNetCore中,可以被用于為每個(gè)請(qǐng)求添加一個(gè)單一的“摘要”日志消息。如果您已經(jīng)完成了上一節(jié)中的步驟,則添加這個(gè)中間件將變得很簡單。在您的Startup類中,在您想要記錄日志的位置使用UseSerilogRequestLogging()進(jìn)行調(diào)用:
// Additional required namespace using Serilog;public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {// ... Error handling/HTTPS middlewareapp.UseStaticFiles();app.UseSerilogRequestLogging(); // <-- Add this lineapp.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapRazorPages();}); }與ASP.NET Core的中間件管道一樣,順序很重要。當(dāng)請(qǐng)求到達(dá)RequestLoggingMiddleware中間件時(shí),它將啟動(dòng)計(jì)時(shí)器,并將請(qǐng)求傳遞給后續(xù)中間件進(jìn)行處理。當(dāng)后面的中間件最終生成響應(yīng)(或拋出異常),則響應(yīng)通過中間件管道傳遞回到請(qǐng)求記錄器,并在其中記錄了結(jié)果并寫入概要日志信息。
Serilog只能記錄到達(dá)中間件的請(qǐng)求。在上面的例子中,我已經(jīng)在StaticFilesMiddleware之后添加了RequestLoggingMiddleware?。因此如果請(qǐng)求被UseStaticFiles處理并使管道短路的話,日志將不會(huì)被記錄。鑒于靜態(tài)文件中間件非常嘈雜,而且通常這是人們期望的行為(靜態(tài)文件進(jìn)行短路,不需要進(jìn)行記錄),但是如果您也希望記錄對(duì)靜態(tài)文件的請(qǐng)求,則可以在管道中serilog中間件移動(dòng)到更早的位置。
如果我們?cè)僖淮芜\(yùn)行該應(yīng)用程序,你還是會(huì)看到原來的10個(gè)日志消息,但你會(huì)看到一個(gè)額外的通過SerilogRequestLoggingMiddleware匯總的日志消息,倒數(shù)第二的消息:
# Standard logging from ASP.NET Core infrastructure [14:15:44 INF] Request starting HTTP/2 GET https://localhost:5001/ [14:15:44 INF] Executing endpoint '/Index' [14:15:45 INF] Route matched with {page = "/Index"}. Executing page /Index [14:15:45 INF] Executing handler method SerilogRequestLogging.Pages.IndexModel.OnGet - ModelState is Valid [14:15:45 INF] Executed handler method OnGet, returned result . [14:15:45 INF] Executing an implicit handler method - ModelState is Valid [14:15:45 INF] Executed an implicit handler method, returned result Microsoft.AspNetCore.Mvc.RazorPages.PageResult. [14:15:45 INF] Executed page /Index in 124.7462ms [14:15:45 INF] Executed endpoint '/Index'# Additional Log from Serilog [14:15:45 INF] HTTP GET / responded 200 in 249.6985 ms# Standard logging from ASP.NET Core infrastructure [14:15:45 INF] Request finished in 274.283ms 200 text/html; charset=utf-8關(guān)于此日志,有幾點(diǎn)需要說明下:
它在一條消息中包含您想要的大多數(shù)相關(guān)信息-HTTP方法,URL路徑,狀態(tài)代碼,持續(xù)時(shí)間。
顯示的持續(xù)時(shí)間略短于Kestrel在后續(xù)消息中記錄的值。這是可以預(yù)期的,因?yàn)镾erilog僅在請(qǐng)求到達(dá)其中間件時(shí)才開始計(jì)時(shí),而在返回時(shí)停止計(jì)時(shí)(在生成響應(yīng)之后)。
在這兩種情況下,使用結(jié)構(gòu)日志記錄時(shí)都會(huì)記錄其他值。例如,記錄了RequestId和SpanId(用于跟蹤功能),因?yàn)樗鼈兪侨罩居涗浄秶囊徊糠帧D梢栽诘卿浀絪eq的請(qǐng)求的以下圖像中看到這一點(diǎn)。
默認(rèn)情況下,我們確實(shí)會(huì)丟失一些信息。例如,不再記錄終結(jié)點(diǎn)名稱和Razor頁面處理程序。在后續(xù)文章中,我將展示如何將它們添加到摘要日志中。
如果想要通過``http://localhost:5341 訪問UI,你可能需要下載seq進(jìn)行安裝。由于某種不知名的原因,可能下載會(huì)很慢。所以當(dāng)然你也可以關(guān)注公眾號(hào)“DotNetCore實(shí)戰(zhàn)”然后回復(fù)關(guān)鍵字“seq”獲取下載地址。
完成整理工作所剩下的就是過濾掉我們當(dāng)前正在記錄的信息級(jí)日志消息。在Program.cs中更新Serilog配置以添加額外的過濾器:
Log.Logger = new LoggerConfiguration().MinimumLevel.Debug().MinimumLevel.Override("Microsoft", LogEventLevel.Information)// Filter out ASP.NET Core infrastructre logs that are Information and below.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning).Enrich.FromLogContext().WriteTo.Console().WriteTo.Seq("http://localhost:5341").CreateLogger();通過最后的更改,您現(xiàn)在將獲得一組干凈的請(qǐng)求日志,其中包含每個(gè)請(qǐng)求的摘要數(shù)據(jù):
[14:29:53 INF] HTTP GET / responded 200 in 129.9509 ms [14:29:56 INF] HTTP GET /Privacy responded 200 in 10.0724 ms [14:29:57 INF] HTTP GET / responded 200 in 3.3341 ms [14:30:54 INF] HTTP GET /Missing responded 404 in 16.7119 ms在下一篇文章中,我將介紹如何通過記錄其他數(shù)據(jù)來增強(qiáng)此日志。
摘要
在本文中,我描述了如何使用Serilog.AspNetCore的請(qǐng)求日志記錄中間件來減少為每個(gè)ASP.NET Core請(qǐng)求生成的日志數(shù),同時(shí)仍記錄摘要數(shù)據(jù)。如果您已經(jīng)在使用Serilog,則非常容易啟用。只需在您的Startup.cs文件中調(diào)用UseSerilogRequestLogging()。
當(dāng)請(qǐng)求到達(dá)此中間件時(shí),它將啟動(dòng)計(jì)時(shí)器。當(dāng)后續(xù)的中間件生成響應(yīng)(或引發(fā)異常)時(shí),響應(yīng)將通過中間件管道返回到請(qǐng)求記錄器,記錄器記錄結(jié)果并編寫摘要日志消息。
添加請(qǐng)求日志記錄中間件之后,您可以過濾掉默認(rèn)情況下在ASP.NET Core 3.0中生成的更多基礎(chǔ)結(jié)構(gòu)日志,而不會(huì)丟失有用的信息。
好看你就點(diǎn)點(diǎn)我總結(jié)
以上是生活随笔為你收集整理的如何利用Serilog的RequestLogging来精简ASP.NET Core的日志输出的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么?原来C#还有这两个关键字
- 下一篇: 临近年关,修复ASP.NET Core因