ASP.NET Core擴充套件庫之日誌
阿新 • • 發佈:2021-03-10
上一篇我們對Xfrogcn.AspNetCore.Extensions擴充套件庫功能進行了簡單的介紹,從這一篇文章開始,我將逐步介紹擴充套件庫中的核心功能。
日誌作為非業務的通用領域基礎功能,有非常多的技術實現,這些第三方庫避免了我們花費時間去重複實現,不過,很多日誌庫配置複雜,不易於使用,入手較難,而有些庫可能與ASP.NET Core的結合並不好。
如果我們沒有對所使用的日誌庫進行詳細瞭解,日誌庫也可能產生嚴重的問題,在我的開發生涯中,曾經遇到過多次因為日誌庫而導致的生產事故。
擴充套件庫日誌模組致力於將日誌相關的最佳實踐進行封裝,簡化日誌庫的使用,讓我們真正從非業務程式碼中解放出來。
日誌庫是隨著擴充套件庫一起啟用的,最簡單的情況是啟用擴充套件庫即可,預設配置將開啟檔案日誌目標,日誌存入應用下Logs目錄,以日期為資料夾,以日誌名稱為檔名稱。
開啟擴充套件庫有兩種方式,可以在IHostBuilder上通過UseExtensions方法,或者在Startup啟動類ConfigureServices方法中通過IServiceCollection的AddExtensions方法。
或者:
由於LogPathTemplate為字串配置,你也可以配置其他的路徑模板。
關於日誌的定時清理,可以通過MaxLogDays配置來指定日誌保留的天數,如果設定為0,表示不清理,這是預設配置。
通過MaxLogFileSize以及RetainedFileCount配置可以設定日誌檔案的自動壓縮策略,MaxLogFileSize預設設定為100mb,超過此大小後,日誌將寫到新的檔案,RetainedFileCount為可旋轉的日誌檔案數量,預設為31個,超過此數量後的日誌將被自動壓縮。
一、簡介
ASP.NET Core擴充套件庫中日誌功能是對Serilog的進一步封裝,之所以選擇Serilog,源於我們在開發工程中的實踐,我們的日誌庫經歷了自己開發、選擇使用NLog,最後定格在使用Serilog庫上。 Serilog日誌庫也並不是非常易於使用,而且可能也缺少一些必要功能,這就是我們需要進一步封裝的原因。 日誌功能預設提供了Console及File兩種日誌目標,他們都分別支援文字和Json格式。 我們也添加了日誌的分類、日誌記錄層級的動態修改、本地檔案日誌的定時清理、本地日誌檔案的按目錄儲存、對容器化下EFK日誌架構的支援、以及日誌在測試中的支援功能等。二、使用
// 通過IHostBuilder上的UseExtensions方法 // Program.cs .NET 5.0 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.UseExtensions(args); webBuilder.UseStartup<Startup>(); }); }
// 在Startup類中 public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddExtensions(Configuration); } }
三、配置
日誌的配置可以通過程式碼方式或者通過配置檔案方式。 採用程式碼方式,在UseExtensions方法或者AddExtensions中傳入配置物件委託即可:public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseExtensions(args, config=> { config.AppLogLevel = Serilog.Events.LogEventLevel.Verbose; config.SystemLogLevel = Serilog.Events.LogEventLevel.Verbose; }); webBuilder.UseStartup<Startup>(); });
如果採用配置檔案方式,只需在配置源(如appsettings.json)中設定相關的配置欄位:
{ "AppLogLevel": "Verbose", "AllowedHosts": "*" }如果都採用,程式碼方式將會覆蓋配置檔案方式。 除此之外,如果你需要對Serilog配置進行更詳細的控制,那麼可以直接在UseExtensions方法或者AddExtensions中傳入Serilog的日誌配置委託。此項委託的設定將覆蓋上述的自動配置。
四、配置日誌級別
為了簡化配置,在擴充套件庫中,我們根據日誌名稱將日誌分為系統日誌、應用日誌以及EFCore日誌,他們分別通過配置中的AppLogLevel、SystemLogLevel及EFCoreCommandLevel屬性來控制。日誌級別的配置都支援執行時動態修改,無需重啟應用。日誌分類 | 對應日誌名 | 對應配置欄位 | 預設級別 |
系統日誌 | Microsoft.* 以及 System.* | SystemLogLevel | Warning |
EFCore日誌 | Microsoft.EntityFrameworkCore.Database.Command | EFCoreCommandLevel | Information |
應用日誌 | 除開系統日誌及EFCore日誌之外的日誌 | AppLogLevel | Information |
五、日誌級別的動態修改
如果你是通過配置源來配置的日誌級別,那麼當配置源更新時(一般通過配置物件的Reload方法),日誌級別將自動修改。 如果需要採用程式碼方式,你可以通過全域性的WebApiConfig例項進行配置:// 動態修改日誌級別 var apiConfig = host.Services.GetRequiredService<WebApiConfig>(); apiConfig.AppLogLevel = Serilog.Events.LogEventLevel.Error;
六、本地檔案日誌配置
針對本地日誌的配置,包含日誌檔案的路徑模板、日誌檔案的定時清理、日誌的自動壓縮等。 本地檔案日誌路徑通過LogPathTemplate設定來配置,預設為LogPathTemplates.DayFolderAndLoggerNameFile,表示以每天作為子目錄,以日誌名稱作為日誌檔名。通過LogPathTemplates也內建了其他的路徑模板:路徑模板名 | 說明 |
DayFolderAndLoggerNameFile | 以每天日期為目錄,日誌名稱為檔名 |
DayFile | 以每天日期為日誌名稱 |
LoggerNameAndDayFile | 以[日誌名稱_每天日誌]為日誌檔名稱 |
LevelFile | 以日誌級別縮寫為日誌檔名稱 |
DayFolderAndLevelFile | 以每天日期為目錄,日誌級別縮寫為日誌名稱 |
七、容器化支援
在容器化環境下,日誌一般會採用EFK的架構,在k8s中,我們推薦F採用fluent-bit而不是filebeat。這種框架下,我們只需將日誌輸出到控制檯,容器將控制檯輸出定位到Docker宿主機,然後通過fluent-bit掃描日誌檔案,進行解析處理,傳送給ES。 在這種模式下,你需要將ConsoleJsonLog設定為true來開啟JSON格式的控制檯日誌目標。同時,由於控制檯單行字數有限制,可能導致日誌被擷取,故可能需要通過MaxLogLength來設定單條日誌的長度限制,此設定預設為8kb,適合大多數場景。超出長度的日誌並不會被忽略,而是會拆分成多條日誌,以此來保證日誌的完整性。// 要支援容器化EFK日誌模式,一般只需要設定ConsoleJsonLog為true即可 public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseExtensions(args, config=> { config.ConsoleJsonLog = true; }); webBuilder.UseStartup<Startup>(); });
八、測試支援
有時我們可能需要在單元測試中檢查日誌的輸出,這時,我們可以使用擴充套件庫在ILoggingBuilder上的擴充套件方法來新增測試日誌目標。隨後,你可以通過IServiceProvider上的GetTestLogContent方法獲取已記錄的日誌內容列表。IServiceCollection services = new ServiceCollection() .AddExtensions() .AddLogging(logBuilder => { // 新增測試日誌記錄器 logBuilder.AddTestLogger(); }); IServiceProvider provider = services.BuildServiceProvider(); // 獲取日誌內容 var logContent = provider.GetTestLogContent();