前言 Serilog是 .NET 上的一個原生結構化高性能日誌庫,這個庫能實現一些比內置庫更高度的定製。日誌持久化是其中一個非常重要的功能,生產環境通常很難掛接調試器或者某些bug的觸發條件很奇怪。為了在脫離調試環境的情況下儘可能保留更多線索來輔助解決生產問題,持久化的日誌就顯得很重要了。目前Ser ...
前言
Serilog是 .NET 上的一個原生結構化高性能日誌庫,這個庫能實現一些比內置庫更高度的定製。日誌持久化是其中一個非常重要的功能,生產環境通常很難掛接調試器或者某些bug的觸發條件很奇怪。為了在脫離調試環境的情況下儘可能保留更多線索來輔助解決生產問題,持久化的日誌就顯得很重要了。目前Serilog支持文件和部分資料庫持久化,文件日誌的查找分析比較麻煩,而使用資料庫持久化則會導致特定資料庫依賴。既然有EF Core這種專門負責抽象底層資料庫的持久化框架,為何不直接使用呢。懷著這樣的心情去Nuget找了一圈,結果一無所獲,無奈又只能自己寫一個。
新書宣傳
有關新書的更多介紹歡迎查看《C#與.NET6 開發從入門到實踐》上市,作者親自來打廣告了!
正文
對代碼感興趣的朋友可以移步Github。這裡直接介紹一下基本用法。
這個庫分為四個包:實體模型包定義基本實體類型;基本擴展包定義了模擬日誌類別和嚴重性級別篩選的過濾器,方便為不同的輸出目標自定義過濾器(內置的篩選器僅支持在全局使用,且會對所有輸出目標生效,粒度不夠細,只能自己寫一個基於過濾器的擴展模擬相同的行為);配置擴展包定義了從IConfiguration
讀取並構建過濾器的輔助方法,支持配置的實時自動更新;EF Core服務包定義了基於EF Core的Serilog的Sink,Sink實現批處理介面,能避免頻繁向資料庫插入單條日誌記錄。方便為分離項目的解決方案按需引用,減少無關類型的污染。
以在ASP.NET Core中使用為例:
實體模型和上下文
public class YourLogRecord : LogRecord
{
public int YourProperty { get; set; }
}
public class YourApplicationDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 使用預設類型。
modelBuilder.UseLogRecord(b =>
{
b.ToTable($"{nameof(LogRecord)}s");
});
// 使用自定義類型,需要繼承LogRecord。
modelBuilder.UseLogRecord<YourLogRecord>(b =>
{
b.ToTable($"{nameof(YourLogRecord)}s");
});
}
}
public class YourLogDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.UseLogRecord(b =>
{
b.ToTable($"{nameof(LogRecord)}s", tb => tb.ExcludeFromMigrations());
});
modelBuilder.UseLogRecord<YourLogRecord>(b =>
{
b.ToTable($"{nameof(YourLogRecord)}s", tb => tb.ExcludeFromMigrations());
});
}
}
需要註意,一定要使用兩個不同的上下文類型,其中一個專用於存儲日誌數據。因為EF Core本身也會產生日誌,如果使用一個上下文,一般配置下一定會產生無限迴圈。EF Core產生日誌,通過EF Core寫入日誌,寫入日誌會導致產生新的EF Core日誌……讀取日誌可以使用日誌上下文,這樣的話日誌實體只需要日誌上下文配置即可。不過還是推薦在主要上下文同時註冊日誌模型,這樣讀取日誌產生的EF Core日誌就可以安全的寫入了。
使用兩個上下文的情況下可以在日誌上下文中配置實體從遷移中排除,把日誌表遷移托管給主上下文。
服務註冊
// 註冊主上下文
services.AddDbContext<YourApplicationDbContext>(options =>
{
options.UseSqlite("app.db")
});
// 註冊日誌上下文
services.AddDbContext<YourLogDbContext>(options =>
{
// 重要!
// 抑制此上下文的命令執行相關日誌生成以消除無限寫入迴圈。
options.ConfigureWarnings(b => b.Ignore(RelationalEventId.CommandExecuted, RelationalEventId.CommandError));
options.UseSqlite("app.db")
});
// 註冊日誌過濾器配置監視器管理器服務。
services.AddMinimumLevelOverridableSerilogFilterConfigurationMonitorManager();
基礎使用(Program.cs)
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((hostBuilder, serviceProvider, configuration) =>
{
configuration
.ReadFrom.Configuration(hostBuilder.Configuration)
.ReadFrom.Services(serviceProvider)
.WriteTo.Logger(internalConfiguration =>
{
internalConfiguration
.Filter.ByIncludingOnly(
// 添加一個基於配置監視器的日誌過濾器
new MinimumLevelOverridableSerilogFilterConfigurationMonitor(
serviceProvider,
// 配置路徑
"SerilogFilterExtensions:EntityFrameworkCore"
).Filter)
// 使用預設日誌類型
.WriteTo.EntityFrameworkCore(
serviceProvider.GetRequiredService<IServiceScopeFactory>(),
// 日誌上下文提取工廠,取決於上下文服務應該如何獲取,例如使用上下文工廠服務或者直接獲取
static sp => sp.GetRequiredService<YourLogDbContext>(),
// 日誌的JSON序列化選項
new()
{
ReferenceHandler = ReferenceHandler.IgnoreCycles,
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
});
// 使用自定義日誌類型
.WriteTo.EntityFrameworkCore<YourLogDbContext, YourLogRecord>(
serviceProvider.GetRequiredService<IServiceScopeFactory>(),
static sp => sp.GetRequiredService<YourLogDbContext>(),
new()
{
ReferenceHandler = ReferenceHandler.IgnoreCycles,
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
});
});
}, writeToProviders: true);
Serilog的內置日誌級別篩選僅可用於全局,無法針對各個Sink獨立配置,因此筆者只能自己實現一個相同效果的過濾器。其中CoreDX.Serilog.Extensions
是過濾器本體,可手動基於代碼構建,CoreDX.Serilog.Extensions.Configuration
是配置擴展,可自動監控配置。配置應該類似以下結構:
{
"SerilogFilterExtensions": {
"EntityFrameworkCore": {
"Default": "Warning",
"Override": {
"Microsoft.AspNetCore.DataProtection.KeyManagement": "Error",
"Microsoft.AspNetCore.DataProtection.Repositories": "Error",
"Microsoft.EntityFrameworkCore.Database.Command": "Error",
"Microsoft.EntityFrameworkCore.Model.Validation": "Error"
}
}
}
}
結語
為了實現對 .NETStantard 2.0 的相容代碼上使用了條件編譯預處理實現一份代碼一個項目同時編譯到所有框架,最大程度共用代碼簡化代碼管理。其中 .NET 6 以下使用Json.NET序列化,其他的使用System.Text.Json序列化。
許可證:MIT
代碼倉庫:CoreDX.Serilog.Sinks.EntityFrameworkCore - Github
Nuget:CoreDX.Serilog.Sinks.EntityFrameworkCore
Nuget:CoreDX.Serilog.Sinks.EntityFrameworkCore.Models
Nuget:CoreDX.Serilog.Extensions
Nuget:CoreDX.Serilog.Extensions.Configuration
QQ群
讀者交流QQ群:540719365
歡迎讀者和廣大朋友一起交流,如發現本書錯誤也歡迎通過博客園、QQ群等方式告知筆者。
本文地址:https://www.cnblogs.com/coredx/p/18298297.html