.Net?Core日志記錄之第三方框架Serilog
一、前言
對(duì)內(nèi)置日志系統(tǒng)的整體實(shí)現(xiàn)進(jìn)行了介紹之后,可以通過(guò)使用內(nèi)置記錄器來(lái)實(shí)現(xiàn)日志的輸出路徑。而在實(shí)際項(xiàng)目開(kāi)發(fā)中,使用第三方日志框架(如: Log4Net、NLog、Loggr、Serilog、Sentry 等)來(lái)記錄也是非常多的。首先一般基礎(chǔ)的內(nèi)置日志記錄器在第三方日志框架中都有實(shí)現(xiàn),然后第三方日志框架在功能上更加強(qiáng)大和豐富,能滿(mǎn)足我們更多的項(xiàng)目分析和診斷的需求。
所以在這一篇中,我們將介紹第三方日志記錄提供程序——Serilog
二、回顧
系統(tǒng)內(nèi)置日志系列:
1. 基于.NetCore3.1系列 —— 日志記錄之日志配置揭秘
2. 基于.NetCore3.1系列 —— 日志記錄之日志核心要素揭秘
3. 基于.NetCore3.1系列 —— 日志記錄之自定義日志組件
從之前學(xué)習(xí)的內(nèi)置日志系統(tǒng)中,我們根據(jù)日志配置的方式了解到了通過(guò)配置的方式,可以有效的輸出日志記錄,方便我們查找發(fā)現(xiàn)問(wèn)題。
而在進(jìn)一步對(duì)內(nèi)部運(yùn)行的主要核心機(jī)制進(jìn)行深入探究后發(fā)現(xiàn)了內(nèi)置日志記錄的幾個(gè)核心要素,在日志工廠記錄器(ILoggerFactory
)中實(shí)現(xiàn)將日志記錄提供器(ILoggerProvider
)對(duì)象都可以集成到Logger
對(duì)象組合中,這樣的話,我們就可以通過(guò)基于ILoggerProvider
自定義日志記錄程序集成到Logger
中,再創(chuàng)建寫(xiě)日志定義Ilogger
,自定義日志記錄器實(shí)現(xiàn)日志的輸出方式,這樣實(shí)現(xiàn)自定義日志記錄工具。
在最后我們通過(guò)自定義的方式簡(jiǎn)單的實(shí)現(xiàn)了自定義日志組件,在這個(gè)基礎(chǔ)上,我們可以根據(jù)具體的需求進(jìn)行完善修改。當(dāng)然了,我們也可以借用第三方日志框架組件程序進(jìn)行使用。
三、說(shuō)明
我們都知道日志記錄在項(xiàng)目開(kāi)發(fā)中或者生產(chǎn)環(huán)境中,都起到舉足輕重的作用。因此,我們都會(huì)采用在項(xiàng)目加入第三方框架日志或自行封裝日志記錄來(lái)記錄日志。
所以在這一篇中,我們會(huì)采用在項(xiàng)目中使用Serilog,目的不僅僅在于希望在用戶(hù)使用之前發(fā)現(xiàn)代碼中的BUG和錯(cuò)誤,更多的是方便我們可以快速的查詢(xún)生產(chǎn)環(huán)境的日志問(wèn)題,深入的了解系統(tǒng)運(yùn)行的表現(xiàn)。
從Serilog的官方介紹中,我們可以發(fā)現(xiàn) 其框架是.net中的診斷日志庫(kù),可以在所有的.net平臺(tái)上運(yùn)行。支持結(jié)構(gòu)化日志記錄,對(duì)復(fù)雜、分布式、異步應(yīng)用程序的支持非常出色。
Serilog是基于日志事件(log events),而不是日志消息(log message)。可以將日志事件格式化為控制臺(tái)的可讀文本或者將事件化為JSON格式。應(yīng)用程序中的日志語(yǔ)句會(huì)創(chuàng)建LogEvent
對(duì)象,而連接到管道的接收器(sinks)會(huì)知道如何記錄它們。(接收器 包括各種終端、控制臺(tái)、文本、SqlServer、ElasticSearch等等可用的列表)
結(jié)構(gòu)化與非結(jié)構(gòu)化之間的問(wèn)題:
對(duì)于日志的處理,在大部分情況下,會(huì)權(quán)衡是否對(duì)開(kāi)發(fā)者的友好型以及對(duì)程序解析的方便性。在很多情況下,開(kāi)發(fā)者可能只是想記錄一段日志而已,所以可以會(huì)考慮簡(jiǎn)單的加上一行代碼來(lái)以達(dá)到記錄日志的目的,如(
log.debug("Disk quota {0} exceeded by user {1}", quota, user);
)當(dāng)然了,日志的執(zhí)行結(jié)構(gòu)可能被存于文本文件或者數(shù)據(jù)庫(kù)中。這樣的日志從開(kāi)發(fā)者的角度來(lái)說(shuō),清晰易懂,十分友好。但是如果后續(xù)要使用程序取查找海量的的上述例子在某段時(shí)間內(nèi)的特定用戶(hù),則很難高效率地完成這一要求,因?yàn)樾枰獙?duì)每個(gè)日志進(jìn)行字符串解析。因此,我們就需要尋求更快更方便的方式來(lái)查找記錄。
非結(jié)構(gòu)的日志:
對(duì)自由格式文本的解析往往依賴(lài)于正則表達(dá)式,并且依賴(lài)于不變的文本。這會(huì)使解析自由格式的文本變得非常脆弱(即解析與代碼中的確切文本緊密耦合)。
還考慮搜索/查找的情況,例如:
SELECT text FROM logs WHERE text LIKE "Disk quota";
LIKE
條件需要與每個(gè)text
行值進(jìn)行比較;再次,這在計(jì)算上是相對(duì)浪費(fèi)的,尤其是在使用通配符時(shí):
SELECT text FROM logs WHERE text LIKE "Disk %";
結(jié)構(gòu)化的日志:
使用結(jié)構(gòu)化日志記錄,與磁盤(pán)錯(cuò)誤相關(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)常搜索/查找其值的列上放置索引,只要您不對(duì)
LIKE
這些列值使用子句即可。您可以將日志消息細(xì)分為特定類(lèi)別的內(nèi)容越多,查找的對(duì)象就越有針對(duì)性。例如,除了error_type
上面示例中的字段/列之外,您甚至可以設(shè)置為be"error_category": "disk", "error_type": "quota"
或諸如此類(lèi)。結(jié)構(gòu)越多,你的日志消息,通過(guò)解析/檢索系統(tǒng)(如
fluentd
,elasticsearch
,kibana
),可以利用該結(jié)構(gòu),并以更快的速度和更低的CPU /內(nèi)存執(zhí)行任務(wù)。總之這不僅與速度和效率有關(guān),更重要的是使用結(jié)構(gòu)化日志記錄和“結(jié)構(gòu)化查詢(xún)”時(shí),能以特定格式捕獲以及呈現(xiàn)結(jié)構(gòu)化日志,同時(shí)提供對(duì)開(kāi)發(fā)者與程序友好的解析支持??梢愿奖愕匾云錇闂l件進(jìn)行篩選,搜索結(jié)果的相關(guān)性將更高。如果沒(méi)有這種搜索,那么在不同上下文中出現(xiàn)的任何單詞都會(huì)給您帶來(lái)大量無(wú)關(guān)的點(diǎn)擊。
四、開(kāi)始
為了更好的理解認(rèn)識(shí)Serilog,我們這簡(jiǎn)單的創(chuàng)建一個(gè)新的項(xiàng)目來(lái)認(rèn)識(shí)一下Serilog的使用。這里我們就簡(jiǎn)單的使用Console
和Debug
的方式來(lái)實(shí)現(xiàn),后續(xù)有機(jī)會(huì)我們可以實(shí)現(xiàn)更多方式的接收器寫(xiě)入日志。
4.1 Serilog使用
4.1.1 安裝依賴(lài)包
Serilog.AspNetCore : 基于AspNetCore框架整合的Serilog日志記錄程序包,包含了Serilog基本庫(kù)和控制臺(tái)日志的實(shí)現(xiàn)。
當(dāng)然了,你也可以直接安裝Serilog 基本庫(kù),然后根據(jù)需要安裝對(duì)應(yīng)的拓展包。
說(shuō)明:
- Serilog.Extensions.Logging 包含了注入了Serilog的拓展方法。
- Serilog.Sinks.Async 實(shí)現(xiàn)了日志異步收集。
- Serilog.Sinks.Console 實(shí)現(xiàn)了控制臺(tái)輸出日志。
- Serilog.Sinks.Debug 實(shí)現(xiàn)了調(diào)試臺(tái)輸出日志。
- Serilog.Sinks.File 實(shí)現(xiàn)了文件輸出日志。
4.1.2 配置Serilog
在應(yīng)用程序中Program.cs
文件中,配置Serilog記錄,確保正確記錄任何配置日志問(wèn)題。
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()
中的通用主機(jī)中。
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>(); }); }
最后,通過(guò)刪除默認(rèn)記錄器的其余配置進(jìn)行清理,從appsettings.json
文件中刪除Logging
對(duì)應(yīng)的配置部分??梢栽偈褂酶鶕?jù)Serilog
的配置規(guī)則進(jìn)行相應(yīng)配置替換它。
"Serilog": { "MinimumLevel": { "Default": "Information", "Override": { "Microsoft": "Warning", "System": "Warning" } } }
4.1.3 提示
當(dāng)在IIS下運(yùn)行時(shí)候,要在Visual Studio輸出窗口中查看Serilog輸出日志的時(shí)候,需要將輸出方式選擇為 Web 服務(wù)器方式,輸出窗口查看日志,或者使用WriteTo.Debug()
替換記錄器配置中的WriteTo.Console()
。
4.2 輸出格式
4.2.1 文本格式
作為文本,它的格式如下:
[21:45:15 INF] HTTP GET / responded 200 in 227.3253 ms
測(cè)試在控制臺(tái)中輸出如下:
上述事件格式中,可以看出由以下幾個(gè)格式組成:
- 事件發(fā)生時(shí)的時(shí)間戳[timestamp]
- 描述何時(shí)應(yīng)該捕獲事件的級(jí)別[level]
- 記錄事件的消息[message]內(nèi)容]
- 描述事件的命名屬性[properties]
- 還可能有一個(gè)Exception對(duì)象
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" }
在寫(xiě)入日志文件中,根據(jù)Serilog的多種接收器的中(Console()、Debug()、File())等支持使用JSON寫(xiě)入日志記錄,通過(guò)引用緊湊的JSON格式化類(lèi)庫(kù)[Serilog.Formatting.Compact]接收所有JSON格式的輸出。
要編寫(xiě)以換行符分隔的JSON,請(qǐng)將CompactJsonFormatter
或RenderedCompactJsonFormatter
傳遞到接收器配置方法,如下:
.WriteTo.Console(new RenderedCompactJsonFormatter()) 或 .WriteTo.Console(new CompactJsonFormatter())
運(yùn)行這個(gè)程序?qū)a(chǎn)生使用Serilog的緊湊格式JSON,并在對(duì)應(yīng)的輸出路徑中生成換行符分隔的JSON流。
4.3 示例
4.3.1 安裝依賴(lài)包
安裝 Serilog.AspNetCore
NuGet 包 ;
4.3.2 配置文件
在appsettings.json
配置文件添加 Serilog
配置,WriteTo
指定輸出目標(biāo)位置,它是一個(gè)數(shù)組類(lèi)型,所以可以指定多個(gè)目標(biāo)位置,這里暫時(shí)只指定輸出到控制臺(tái):
{ "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è)置請(qǐng)求管道
在 Startup.cs 的 中的Configure
請(qǐng)求管道中添加 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之類(lèi)的處理程序之前。 中間件不會(huì)對(duì)管道中出現(xiàn)在它之前的組件進(jìn)行時(shí)間或日志記錄。通過(guò)將UseSerilogRequestLogging()
放在它們之后,可以將其用于從日志中排除雜亂的處理程序,例如UseStaticFiles()。)
為了減少每個(gè)HTTP請(qǐng)求需要構(gòu)造,傳輸和存儲(chǔ)的日志事件的數(shù)量。 在同一事件上具有許多屬性還可以使請(qǐng)求詳細(xì)信息和其他數(shù)據(jù)的關(guān)聯(lián)更加容易。
默認(rèn)情況下,以下請(qǐng)求信息將作為屬性添加:
請(qǐng)求方法
請(qǐng)求路徑
狀態(tài)碼
響應(yīng)時(shí)間
您可以使用
UseSerilogRequestLogging()
上的選項(xiàng)回調(diào)來(lái)修改用于請(qǐng)求完成事件的消息模板,添加其他屬性或更改事件級(jí)別:
app.UseSerilogRequestLogging(options => { // 自定義消息模板 options.MessageTemplate = "Handled {RequestPath}"; // 發(fā)出調(diào)試級(jí)別的事件,而不是默認(rèn)事件 options.GetLevel = (httpContext, elapsed, ex) => LogEventLevel.Debug; //將其他屬性附加到請(qǐng)求完成事件 options.EnrichDiagnosticContext = (diagnosticContext, httpContext) => { diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value); diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme); }; });
4.3.5 輸出效果
由于日志總是輸出一堆,我們不能快速的查找定位問(wèn)題,其實(shí) Serilog
輸出的日志是非常簡(jiǎn)潔的,只有 HTTP GET ...
這一條,其他都是 AspNetCore 系統(tǒng)本身輸出的,所以我們可以對(duì)輸出的日志進(jìn)行簡(jiǎn)化操作。
4.3.6 輸出簡(jiǎn)化
為了使日志輸出更簡(jiǎn)潔,我們可以設(shè)置不輸出 AspNetCore Info 日志,只需在 Serilog
配置節(jié)點(diǎn)中設(shè)置 AspNetCore 日志輸出級(jí)別為 Warning
:
{ "Serilog": { "MinimumLevel": { "Default": "Debug", "Override": { "Microsoft": "Warning", "System": "Warning" } } } }
五、總結(jié)
- 本篇主要是對(duì)Serilog的說(shuō)明,認(rèn)識(shí)到是一個(gè)基于日志事件的而非日志消息的結(jié)構(gòu)化日志類(lèi)庫(kù)。
- 簡(jiǎn)單的涉及對(duì)基礎(chǔ)知識(shí)的認(rèn)識(shí)以及使用,通過(guò)構(gòu)建一個(gè)新的項(xiàng)目來(lái)實(shí)現(xiàn)Serilog的日志記錄以及怎么使用這個(gè)框架。
- 在后續(xù)中如何結(jié)合這個(gè)日志類(lèi)庫(kù)引入項(xiàng)目中使用,以及對(duì)日志怎么存儲(chǔ)和查詢(xún)進(jìn)行說(shuō)明(會(huì)考慮 ELK存儲(chǔ)采集分析 )。
- 如果有不對(duì)的或不理解的地方,希望大家可以多多指正,提出問(wèn)題,一起討論,不斷學(xué)習(xí),共同進(jìn)步。
- 本文中參考資料: 官方簡(jiǎn)介 、Serilog文檔、serilog-aspnetcore
- 本文源碼下載地址
到此這篇關(guān)于.Net Core日志記錄之第三方框架Serilog的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
涉及網(wǎng)絡(luò)編程時(shí),需要用到的幾個(gè)常用方法
涉及網(wǎng)絡(luò)編程時(shí),需要用到的幾個(gè)常用方法...2006-09-09Entity?Framework?Core生成數(shù)據(jù)庫(kù)表
這篇文章介紹了Entity?Framework?Core生成數(shù)據(jù)庫(kù)表的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-03-03創(chuàng)建一個(gè)完整的ASP.NET Web API項(xiàng)目
ASP.NET Web API具有與ASP.NET MVC類(lèi)似的編程方式,ASP.NET Web API不僅僅具有一個(gè)完全獨(dú)立的消息處理管道,而且這個(gè)管道比為ASP.NET MVC設(shè)計(jì)的管道更為復(fù)雜,功能也更為強(qiáng)大。下面創(chuàng)建一個(gè)簡(jiǎn)單的Web API項(xiàng)目,需要的朋友可以參考下2015-10-10ASP.NET數(shù)據(jù)庫(kù)緩存依賴(lài)實(shí)例分析
這篇文章主要介紹了ASP.NET數(shù)據(jù)庫(kù)緩存依賴(lài),以實(shí)例的形式分析總結(jié)了數(shù)據(jù)庫(kù)緩存依賴(lài)的原理與用法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-10-10.Net Core WebApi部署到Windows服務(wù)器上的步驟
這篇文章主要介紹了.Net Core WebApi部署到Windows服務(wù)器上的步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03asp.net下Linq To Sql注意事項(xiàng)小結(jié)
對(duì)于Linq 連接數(shù)據(jù)庫(kù)進(jìn)行操作時(shí)需注意的問(wèn)題2008-10-10ASP.Net Core基于EF6、Unitwork、Autofac實(shí)現(xiàn)Repository模式
這篇文章介紹了ASP.Net Core基于EF6、Unitwork、Autofac實(shí)現(xiàn)Repository模式的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02