.Net?Core日志記錄之第三方框架Serilog
一、前言
對內(nèi)置日志系統(tǒng)的整體實現(xiàn)進行了介紹之后,可以通過使用內(nèi)置記錄器來實現(xiàn)日志的輸出路徑。而在實際項目開發(fā)中,使用第三方日志框架(如: Log4Net、NLog、Loggr、Serilog、Sentry 等)來記錄也是非常多的。首先一般基礎(chǔ)的內(nèi)置日志記錄器在第三方日志框架中都有實現(xiàn),然后第三方日志框架在功能上更加強大和豐富,能滿足我們更多的項目分析和診斷的需求。
所以在這一篇中,我們將介紹第三方日志記錄提供程序——Serilog
二、回顧
系統(tǒng)內(nèi)置日志系列:
1. 基于.NetCore3.1系列 —— 日志記錄之日志配置揭秘
2. 基于.NetCore3.1系列 —— 日志記錄之日志核心要素揭秘
3. 基于.NetCore3.1系列 —— 日志記錄之自定義日志組件
從之前學(xué)習(xí)的內(nèi)置日志系統(tǒng)中,我們根據(jù)日志配置的方式了解到了通過配置的方式,可以有效的輸出日志記錄,方便我們查找發(fā)現(xiàn)問題。
而在進一步對內(nèi)部運行的主要核心機制進行深入探究后發(fā)現(xiàn)了內(nèi)置日志記錄的幾個核心要素,在日志工廠記錄器(ILoggerFactory)中實現(xiàn)將日志記錄提供器(ILoggerProvider)對象都可以集成到Logger對象組合中,這樣的話,我們就可以通過基于ILoggerProvider自定義日志記錄程序集成到Logger中,再創(chuàng)建寫日志定義Ilogger,自定義日志記錄器實現(xiàn)日志的輸出方式,這樣實現(xiàn)自定義日志記錄工具。
在最后我們通過自定義的方式簡單的實現(xiàn)了自定義日志組件,在這個基礎(chǔ)上,我們可以根據(jù)具體的需求進行完善修改。當(dāng)然了,我們也可以借用第三方日志框架組件程序進行使用。
三、說明
我們都知道日志記錄在項目開發(fā)中或者生產(chǎn)環(huán)境中,都起到舉足輕重的作用。因此,我們都會采用在項目加入第三方框架日志或自行封裝日志記錄來記錄日志。
所以在這一篇中,我們會采用在項目中使用Serilog,目的不僅僅在于希望在用戶使用之前發(fā)現(xiàn)代碼中的BUG和錯誤,更多的是方便我們可以快速的查詢生產(chǎn)環(huán)境的日志問題,深入的了解系統(tǒng)運行的表現(xiàn)。
從Serilog的官方介紹中,我們可以發(fā)現(xiàn) 其框架是.net中的診斷日志庫,可以在所有的.net平臺上運行。支持結(jié)構(gòu)化日志記錄,對復(fù)雜、分布式、異步應(yīng)用程序的支持非常出色。
Serilog是基于日志事件(log events),而不是日志消息(log message)??梢詫?strong>日志事件格式化為控制臺的可讀文本或者將事件化為JSON格式。應(yīng)用程序中的日志語句會創(chuàng)建LogEvent對象,而連接到管道的接收器(sinks)會知道如何記錄它們。(接收器 包括各種終端、控制臺、文本、SqlServer、ElasticSearch等等可用的列表)
結(jié)構(gòu)化與非結(jié)構(gòu)化之間的問題:
對于日志的處理,在大部分情況下,會權(quán)衡是否對開發(fā)者的友好型以及對程序解析的方便性。在很多情況下,開發(fā)者可能只是想記錄一段日志而已,所以可以會考慮簡單的加上一行代碼來以達到記錄日志的目的,如(
log.debug("Disk quota {0} exceeded by user {1}", quota, user);)當(dāng)然了,日志的執(zhí)行結(jié)構(gòu)可能被存于文本文件或者數(shù)據(jù)庫中。這樣的日志從開發(fā)者的角度來說,清晰易懂,十分友好。但是如果后續(xù)要使用程序取查找海量的的上述例子在某段時間內(nèi)的特定用戶,則很難高效率地完成這一要求,因為需要對每個日志進行字符串解析。因此,我們就需要尋求更快更方便的方式來查找記錄。
非結(jié)構(gòu)的日志:
對自由格式文本的解析往往依賴于正則表達式,并且依賴于不變的文本。這會使解析自由格式的文本變得非常脆弱(即解析與代碼中的確切文本緊密耦合)。
還考慮搜索/查找的情況,例如:
SELECT text FROM logs WHERE text LIKE "Disk quota";
LIKE條件需要與每個text行值進行比較;再次,這在計算上是相對浪費的,尤其是在使用通配符時:
SELECT text FROM logs WHERE text LIKE "Disk %";
結(jié)構(gòu)化的日志:
使用結(jié)構(gòu)化日志記錄,與磁盤錯誤相關(guān)的日志消息在JSON中可能如下所示:
{ "level": "DEBUG", "user": "username", "error_type": "disk", "text": "Disk quota ... exceeded by user ..." }這種結(jié)構(gòu)的字段可以很容易地映射到例如 SQL表列名,這意味著查找可以更具體/更細(xì)粒度:
SELECT user, text FROM logs WHERE error_type = "disk";
您可以在希望經(jīng)常搜索/查找其值的列上放置索引,只要您不對
LIKE這些列值使用子句即可。您可以將日志消息細(xì)分為特定類別的內(nèi)容越多,查找的對象就越有針對性。例如,除了error_type上面示例中的字段/列之外,您甚至可以設(shè)置為be"error_category": "disk", "error_type": "quota"或諸如此類。結(jié)構(gòu)越多,你的日志消息,通過解析/檢索系統(tǒng)(如
fluentd,elasticsearch,kibana),可以利用該結(jié)構(gòu),并以更快的速度和更低的CPU /內(nèi)存執(zhí)行任務(wù)。總之這不僅與速度和效率有關(guān),更重要的是使用結(jié)構(gòu)化日志記錄和“結(jié)構(gòu)化查詢”時,能以特定格式捕獲以及呈現(xiàn)結(jié)構(gòu)化日志,同時提供對開發(fā)者與程序友好的解析支持??梢愿奖愕匾云錇闂l件進行篩選,搜索結(jié)果的相關(guān)性將更高。如果沒有這種搜索,那么在不同上下文中出現(xiàn)的任何單詞都會給您帶來大量無關(guān)的點擊。
四、開始
為了更好的理解認(rèn)識Serilog,我們這簡單的創(chuàng)建一個新的項目來認(rèn)識一下Serilog的使用。這里我們就簡單的使用Console和Debug的方式來實現(xiàn),后續(xù)有機會我們可以實現(xiàn)更多方式的接收器寫入日志。
4.1 Serilog使用
4.1.1 安裝依賴包
Serilog.AspNetCore : 基于AspNetCore框架整合的Serilog日志記錄程序包,包含了Serilog基本庫和控制臺日志的實現(xiàn)。
當(dāng)然了,你也可以直接安裝Serilog 基本庫,然后根據(jù)需要安裝對應(yīng)的拓展包。
說明:
- Serilog.Extensions.Logging 包含了注入了Serilog的拓展方法。
- Serilog.Sinks.Async 實現(xiàn)了日志異步收集。
- Serilog.Sinks.Console 實現(xiàn)了控制臺輸出日志。
- Serilog.Sinks.Debug 實現(xiàn)了調(diào)試臺輸出日志。
- Serilog.Sinks.File 實現(xiàn)了文件輸出日志。
4.1.2 配置Serilog
在應(yīng)用程序中Program.cs文件中,配置Serilog記錄,確保正確記錄任何配置日志問題。
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
try
{
Log.Information("Starting web host");
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}然后,添加UseSerilog()到CreateHostBuilder()中的通用主機中。
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args) //從appsettings.json中讀取配置。
.UseSerilog() // <-- Add this line
.ConfigureLogging((hostingContext, logging) =>
{
logging.ClearProviders(); //去掉默認(rèn)添加的日志提供程序
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}最后,通過刪除默認(rèn)記錄器的其余配置進行清理,從appsettings.json文件中刪除Logging對應(yīng)的配置部分。可以再使用根據(jù)Serilog的配置規(guī)則進行相應(yīng)配置替換它。
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
}
}4.1.3 提示
當(dāng)在IIS下運行時候,要在Visual Studio輸出窗口中查看Serilog輸出日志的時候,需要將輸出方式選擇為 Web 服務(wù)器方式,輸出窗口查看日志,或者使用WriteTo.Debug()替換記錄器配置中的WriteTo.Console()。
4.2 輸出格式
4.2.1 文本格式
作為文本,它的格式如下:
[21:45:15 INF] HTTP GET / responded 200 in 227.3253 ms
測試在控制臺中輸出如下:

上述事件格式中,可以看出由以下幾個格式組成:
- 事件發(fā)生時的時間戳[timestamp]
- 描述何時應(yīng)該捕獲事件的級別[level]
- 記錄事件的消息[message]內(nèi)容]
- 描述事件的命名屬性[properties]
- 還可能有一個Exception對象
4.2.2 JSON格式
作為JSON格式,它的格式如下:
{
"@t": "2020-08-27T13:59:44.6410761Z",
"@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"
}在寫入日志文件中,根據(jù)Serilog的多種接收器的中(Console()、Debug()、File())等支持使用JSON寫入日志記錄,通過引用緊湊的JSON格式化類庫[Serilog.Formatting.Compact]接收所有JSON格式的輸出。
要編寫以換行符分隔的JSON,請將CompactJsonFormatter或RenderedCompactJsonFormatter傳遞到接收器配置方法,如下:
.WriteTo.Console(new RenderedCompactJsonFormatter()) 或 .WriteTo.Console(new CompactJsonFormatter())
運行這個程序?qū)a(chǎn)生使用Serilog的緊湊格式JSON,并在對應(yīng)的輸出路徑中生成換行符分隔的JSON流。

4.3 示例
4.3.1 安裝依賴包
安裝 Serilog.AspNetCore NuGet 包 ;
4.3.2 配置文件
在appsettings.json配置文件添加 Serilog 配置,WriteTo 指定輸出目標(biāo)位置,它是一個數(shù)組類型,所以可以指定多個目標(biāo)位置,這里暫時只指定輸出到控制臺:
{
"Serilog": {
"MinimumLevel": {
"Default": "Debug"
}
}
}4.3.3 設(shè)置配置信息
讀取配置文件信息,設(shè)置配置信息
public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
.AddEnvironmentVariables()
.Build();在main方法中,
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.Enrich.FromLogContext()
.WriteTo.Debug() //輸出路徑
.WriteTo.Console(
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}") //模板
.CreateLogger();
try
{
Log.Information("Starting web host");
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}在Program.cs 添加 UseSerilog()
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseSerilog(); //添加4.3.4 設(shè)置請求管道
在 Startup.cs 的 中的Configure 請求管道中添加 UseSerilogRequestLogging:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseSerilogRequestLogging();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}重要的是UseSerilogRequestLogging()調(diào)用應(yīng)出現(xiàn)在諸如MVC之類的處理程序之前。 中間件不會對管道中出現(xiàn)在它之前的組件進行時間或日志記錄。通過將UseSerilogRequestLogging() 放在它們之后,可以將其用于從日志中排除雜亂的處理程序,例如UseStaticFiles()。)
為了減少每個HTTP請求需要構(gòu)造,傳輸和存儲的日志事件的數(shù)量。 在同一事件上具有許多屬性還可以使請求詳細(xì)信息和其他數(shù)據(jù)的關(guān)聯(lián)更加容易。
默認(rèn)情況下,以下請求信息將作為屬性添加:
請求方法
請求路徑
狀態(tài)碼
響應(yīng)時間
您可以使用
UseSerilogRequestLogging()上的選項回調(diào)來修改用于請求完成事件的消息模板,添加其他屬性或更改事件級別:
app.UseSerilogRequestLogging(options =>
{
// 自定義消息模板
options.MessageTemplate = "Handled {RequestPath}";
// 發(fā)出調(diào)試級別的事件,而不是默認(rèn)事件
options.GetLevel = (httpContext, elapsed, ex) => LogEventLevel.Debug;
//將其他屬性附加到請求完成事件
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme);
};
});4.3.5 輸出效果

由于日志總是輸出一堆,我們不能快速的查找定位問題,其實 Serilog 輸出的日志是非常簡潔的,只有 HTTP GET ... 這一條,其他都是 AspNetCore 系統(tǒng)本身輸出的,所以我們可以對輸出的日志進行簡化操作。
4.3.6 輸出簡化
為了使日志輸出更簡潔,我們可以設(shè)置不輸出 AspNetCore Info 日志,只需在 Serilog配置節(jié)點中設(shè)置 AspNetCore 日志輸出級別為 Warning:
{
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
}
}
}
五、總結(jié)
- 本篇主要是對Serilog的說明,認(rèn)識到是一個基于日志事件的而非日志消息的結(jié)構(gòu)化日志類庫。
- 簡單的涉及對基礎(chǔ)知識的認(rèn)識以及使用,通過構(gòu)建一個新的項目來實現(xiàn)Serilog的日志記錄以及怎么使用這個框架。
- 在后續(xù)中如何結(jié)合這個日志類庫引入項目中使用,以及對日志怎么存儲和查詢進行說明(會考慮 ELK存儲采集分析 )。
- 如果有不對的或不理解的地方,希望大家可以多多指正,提出問題,一起討論,不斷學(xué)習(xí),共同進步。
- 本文中參考資料: 官方簡介 、Serilog文檔、serilog-aspnetcore
- 本文源碼下載地址
到此這篇關(guān)于.Net Core日志記錄之第三方框架Serilog的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Asp.net Core 如何設(shè)置黑白名單(路由限制)
本文主要介紹了Asp.net Core 如何設(shè)置黑白名單(路由限制),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-08-08
.net 中的 StringBuilder 和 TextWriter 區(qū)別詳解
這篇文章主要介紹了.net 中的 StringBuilder 和 TextWriter 區(qū)別詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
ASP.NET?Core中創(chuàng)建中間件的方式匯總
ASP.NET?Core中間件(Middleware)是用于處理HTTP請求和響應(yīng)的組件,它們被安排在請求處理管道中,并按順序執(zhí)行,這篇文章主要介紹了ASP.NET?Core中創(chuàng)建中間件的幾種方式,需要的朋友可以參考下2024-07-07
[Asp.Net Core]用Blazor Server Side實現(xiàn)圖片驗證碼
這篇文章主要介紹了如何用Blazor Server Side實現(xiàn)圖片驗證碼,文中講解非常詳細(xì),代碼幫助大家更好理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07

