.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系列 —— 日志記錄之自定義日志組件
從之前學習的內(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ù)具體的需求進行完善修改。當然了,我們也可以借用第三方日志框架組件程序進行使用。
三、說明
我們都知道日志記錄在項目開發(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)化日志記錄,對復雜、分布式、異步應用程序的支持非常出色。
Serilog是基于日志事件(log events),而不是日志消息(log message)。可以將日志事件格式化為控制臺的可讀文本或者將事件化為JSON格式。應用程序中的日志語句會創(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);
)當然了,日志的執(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表列名,這意味著查找可以更具體/更細粒度:
SELECT user, text FROM logs WHERE error_type = "disk";
您可以在希望經(jīng)常搜索/查找其值的列上放置索引,只要您不對
LIKE
這些列值使用子句即可。您可以將日志消息細分為特定類別的內(nèi)容越多,查找的對象就越有針對性。例如,除了error_type
上面示例中的字段/列之外,您甚至可以設置為be"error_category": "disk", "error_type": "quota"
或諸如此類。結(jié)構(gòu)越多,你的日志消息,通過解析/檢索系統(tǒng)(如
fluentd
,elasticsearch
,kibana
),可以利用該結(jié)構(gòu),并以更快的速度和更低的CPU /內(nèi)存執(zhí)行任務。總之這不僅與速度和效率有關(guān),更重要的是使用結(jié)構(gòu)化日志記錄和“結(jié)構(gòu)化查詢”時,能以特定格式捕獲以及呈現(xiàn)結(jié)構(gòu)化日志,同時提供對開發(fā)者與程序友好的解析支持。可以更方便地以其為條件進行篩選,搜索結(jié)果的相關(guān)性將更高。如果沒有這種搜索,那么在不同上下文中出現(xiàn)的任何單詞都會給您帶來大量無關(guān)的點擊。
四、開始
為了更好的理解認識Serilog,我們這簡單的創(chuàng)建一個新的項目來認識一下Serilog的使用。這里我們就簡單的使用Console
和Debug
的方式來實現(xiàn),后續(xù)有機會我們可以實現(xiàn)更多方式的接收器寫入日志。
4.1 Serilog使用
4.1.1 安裝依賴包
Serilog.AspNetCore : 基于AspNetCore框架整合的Serilog日志記錄程序包,包含了Serilog基本庫和控制臺日志的實現(xiàn)。
當然了,你也可以直接安裝Serilog 基本庫,然后根據(jù)需要安裝對應的拓展包。
說明:
- 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
在應用程序中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(); //去掉默認添加的日志提供程序 }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); }
最后,通過刪除默認記錄器的其余配置進行清理,從appsettings.json
文件中刪除Logging
對應的配置部分??梢栽偈褂酶鶕?jù)Serilog
的配置規(guī)則進行相應配置替換它。
"Serilog": { "MinimumLevel": { "Default": "Information", "Override": { "Microsoft": "Warning", "System": "Warning" } } }
4.1.3 提示
當在IIS下運行時候,要在Visual Studio輸出窗口中查看Serilog輸出日志的時候,需要將輸出方式選擇為 Web 服務器方式,輸出窗口查看日志,或者使用WriteTo.Debug()
替換記錄器配置中的WriteTo.Console()
。
4.2 輸出格式
4.2.1 文本格式
作為文本,它的格式如下:
[21:45:15 INF] HTTP GET / responded 200 in 227.3253 ms
測試在控制臺中輸出如下:
上述事件格式中,可以看出由以下幾個格式組成:
- 事件發(fā)生時的時間戳[timestamp]
- 描述何時應該捕獲事件的級別[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,并在對應的輸出路徑中生成換行符分隔的JSON流。
4.3 示例
4.3.1 安裝依賴包
安裝 Serilog.AspNetCore
NuGet 包 ;
4.3.2 配置文件
在appsettings.json
配置文件添加 Serilog
配置,WriteTo
指定輸出目標位置,它是一個數(shù)組類型,所以可以指定多個目標位置,這里暫時只指定輸出到控制臺:
{ "Serilog": { "MinimumLevel": { "Default": "Debug" } } }
4.3.3 設置配置信息
讀取配置文件信息,設置配置信息
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 設置請求管道
在 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)用應出現(xiàn)在諸如MVC之類的處理程序之前。 中間件不會對管道中出現(xiàn)在它之前的組件進行時間或日志記錄。通過將UseSerilogRequestLogging()
放在它們之后,可以將其用于從日志中排除雜亂的處理程序,例如UseStaticFiles()。)
為了減少每個HTTP請求需要構(gòu)造,傳輸和存儲的日志事件的數(shù)量。 在同一事件上具有許多屬性還可以使請求詳細信息和其他數(shù)據(jù)的關(guān)聯(lián)更加容易。
默認情況下,以下請求信息將作為屬性添加:
請求方法
請求路徑
狀態(tài)碼
響應時間
您可以使用
UseSerilogRequestLogging()
上的選項回調(diào)來修改用于請求完成事件的消息模板,添加其他屬性或更改事件級別:
app.UseSerilogRequestLogging(options => { // 自定義消息模板 options.MessageTemplate = "Handled {RequestPath}"; // 發(fā)出調(diào)試級別的事件,而不是默認事件 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 輸出簡化
為了使日志輸出更簡潔,我們可以設置不輸出 AspNetCore Info 日志,只需在 Serilog
配置節(jié)點中設置 AspNetCore 日志輸出級別為 Warning
:
{ "Serilog": { "MinimumLevel": { "Default": "Debug", "Override": { "Microsoft": "Warning", "System": "Warning" } } } }
五、總結(jié)
- 本篇主要是對Serilog的說明,認識到是一個基于日志事件的而非日志消息的結(jié)構(gòu)化日志類庫。
- 簡單的涉及對基礎(chǔ)知識的認識以及使用,通過構(gòu)建一個新的項目來實現(xiàn)Serilog的日志記錄以及怎么使用這個框架。
- 在后續(xù)中如何結(jié)合這個日志類庫引入項目中使用,以及對日志怎么存儲和查詢進行說明(會考慮 ELK存儲采集分析 )。
- 如果有不對的或不理解的地方,希望大家可以多多指正,提出問題,一起討論,不斷學習,共同進步。
- 本文中參考資料: 官方簡介 、Serilog文檔、serilog-aspnetcore
- 本文源碼下載地址
到此這篇關(guān)于.Net Core日志記錄之第三方框架Serilog的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Entity?Framework?Core生成數(shù)據(jù)庫表
這篇文章介紹了Entity?Framework?Core生成數(shù)據(jù)庫表的方法,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-03-03創(chuàng)建一個完整的ASP.NET Web API項目
ASP.NET Web API具有與ASP.NET MVC類似的編程方式,ASP.NET Web API不僅僅具有一個完全獨立的消息處理管道,而且這個管道比為ASP.NET MVC設計的管道更為復雜,功能也更為強大。下面創(chuàng)建一個簡單的Web API項目,需要的朋友可以參考下2015-10-10.Net Core WebApi部署到Windows服務器上的步驟
這篇文章主要介紹了.Net Core WebApi部署到Windows服務器上的步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-03-03asp.net下Linq To Sql注意事項小結(jié)
對于Linq 連接數(shù)據(jù)庫進行操作時需注意的問題2008-10-10ASP.Net Core基于EF6、Unitwork、Autofac實現(xiàn)Repository模式
這篇文章介紹了ASP.Net Core基于EF6、Unitwork、Autofac實現(xiàn)Repository模式的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-02-02