.Net Core 3.0 使用 Serilog 把日誌記錄到 SqlServer
Serilog簡介
Serilog是.net中的診斷日誌庫,可以在所有的.net平臺上面執行。Serilog支援結構化日誌記錄,對複雜、分散式、非同步應用程式的支援非常出色。Serilog可以通過外掛的方式把日誌寫入到各種終端,控制檯、文字、Sqlserver、ElasticSearch,Serilog支援終端的列表:https://github.com/serilog/serilog/wiki/Provided-Sinks 。
Serilog日誌寫入SqlServer
一、Sink LoggerConfiguration
connectionString 資料庫連線字串
schemaName 資料庫所有者,預設dbo
tableName 記錄日誌的表名
autoCreateSqlTable 是否自動建立表,如果設定為ture,則在Serilog啟動時檢測資料庫是否有對應的表,沒有則建立
columnOptions 日誌表中的列定義
restrictedToMinimumLevel 記錄日誌的最小level
batchPostingLimit 單次批量處理中提交的最大日誌數量
period 進行批量提交的間隔
formatProvider 提供特定的格式化處理,https://github.com/serilog/serilog/wiki/Formatting-Output#format-providers
Serilog為我們定義了一套標準列,預設情況下會生成如下列,當然我們也可以自定義列
StandardColumn.Id 自增Id
StandardColumn.Message 日誌內容
StandardColumn.MessageTemplate 日誌模板
StandardColumn.Level 等級
StandardColumn.TimeStamp 記錄時間
StandardColumn.Exception 異常資訊
StandardColumn.Properties 日誌事件屬性值
刪除標準列:
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
新增自定義列:
columnOptions.AdditionalColumns = new Collection<SqlColumn> { new SqlColumn { DataType = SqlDbType.NVarChar, DataLength = 32, ColumnName = "IP" } };
完整LoggerConfiguration示例如下:
var columnOptions = new ColumnOptions(); columnOptions.Store.Remove(StandardColumn.MessageTemplate);//刪除標準列 columnOptions.Properties.ExcludeAdditionalProperties = true;//排除已經自定義列的資料 columnOptions.AdditionalColumns = new Collection<SqlColumn>//新增自定義列 { new SqlColumn { DataType = SqlDbType.NVarChar, DataLength = 32, ColumnName = "IP" } }; Log.Logger = new LoggerConfiguration() .WriteTo.MSSqlServer( connectionString: Configuration["Serilog:ConnectionString"], tableName: Configuration["Serilog:TableName"], batchPostingLimit: Configuration.GetValue<int>("Serilog:BatchPostingLimit"),//批量插入資料庫條數 period: TimeSpan.FromSeconds(5),//執行時間間隔 restrictedToMinimumLevel: Configuration.GetValue<LogEventLevel>("Serilog:MinimumLevel"), columnOptions: columnOptions, autoCreateSqlTable: true ).CreateLogger();
上面的配置也可以全部從配置檔案讀取:
{ "Serilog": { "Using": [ "Serilog.Sinks.MSSqlServer" ], "MinimumLevel": "Debug", "WriteTo": [ { "Name": "MSSqlServer", "Args": { "connectionString": "NamedConnectionString", "schemaName": "EventLogging", "tableName": "Logs", "autoCreateSqlTable": true, "restrictedToMinimumLevel": "Warning", "batchPostingLimit": 100, "period": "0.00:00:30", "columnOptionsSection": { "disableTriggers": true, "clusteredColumnstoreIndex": false, "primaryKeyColumnName": "Id", "addStandardColumns": [ "LogEvent" ], "removeStandardColumns": [ "MessageTemplate"], "additionalColumns": [ { "ColumnName": "IP", "DataType": "varchar", "DataLength": 32 } ], "id": { "nonClusteredIndex": true }, "properties": { "columnName": "Properties", "excludeAdditionalProperties": true, "dictionaryElementName": "dict", "itemElementName": "item", "omitDictionaryContainerElement": false, "omitSequenceContainerElement": false, "omitStructureContainerElement": false, "omitElementIfEmpty": true, "propertyElementName": "prop", "rootElementName": "root", "sequenceElementName": "seq", "structureElementName": "struct", "usePropertyKeyAsElementName": false }, "timeStamp": { "columnName": "Timestamp", "convertToUtc": true }, "logEvent": { "excludeAdditionalProperties": true, "excludeStandardColumns": true }, "message": { "columnName": "message" }, "exception": { "columnName": "exception" } } } } ] } }
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(Configuration["Serilog"]);//需要引用Microsoft.Extensions.Configuration
二、Logger使用
1、直接使用Serilog提供的靜態類Log
Log.Information(“message”);
2、使用serilog-extensions-logging 替換.net core預設日誌Microsoft.Extensions.Logging,注入Serilog
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.CaptureStartupErrors(true)//捕捉啟動異常 .UseSetting("detailedErrors", "true")//指定程式應用程式會顯示詳細的啟動錯誤資訊 .UseStartup<Startup>() .ConfigureLogging(builder => { builder.ClearProviders(); builder.AddSerilog(); }); });
private readonly ILogger logger; public TestController(ILogger<TestController> logger) { this.logger = logger; } logger.Information("Message")
三、怎麼把資料寫入自定義列
Serilog並沒有提供 Log.Debug(Message,IP)方法,在我們日常開發中可能會有如下幾種需求:
1、設定全域性Property
例如我需要記錄當前程式伺服器的ip,或者我需要記錄當前服務的名稱,需要一個共用的欄位。那麼我們可以在LoggerConfiguration的時候設定一個全域性的Property,即在相同的LoggerConfiguration下面每條日誌都可以共用,我們可以這樣配置
Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() .Enrich.WithProperty("IP", GetIP()) .WriteTo.MSSqlServer( connectionString: Configuration["Serilog:ConnectionString"], tableName: Configuration["Serilog:TableName"], batchPostingLimit: Configuration.GetValue<int>("Serilog:BatchPostingLimit"),//批量插入資料庫條數 period: TimeSpan.FromSeconds(5),//執行時間間隔 restrictedToMinimumLevel: Configuration.GetValue<LogEventLevel>("Serilog:MinimumLevel"), columnOptions: columnOptions, autoCreateSqlTable: true ).CreateLogger();
2、設定ForContext寫入Property
例如我需要記錄當前類,需要在記錄日誌的時候設定ForContext
Log.ForContext("Calss", GetType().FullName).Information("message");
這裡只是一個例子,其實serilog已經自動幫我們記錄了Calss的資訊,在Properties中可以找到SourceContext節點,裡面就記錄了相關的名稱空間和類
四、對日誌進行過濾
如果系統日誌太多,我們很難快速找到有用的資訊,所以很多時候我們會對日誌進行過濾
1、通過MinimumLevel進行過濾
設定MinimumLevel的等級進行過濾,Serilog中Level有Verbose,Debug,Information,Warning,Error,Fatal幾個等級,Serilog只記錄當前等級及比當前等級高的日誌。
2、通過Override進行過濾
原理是serilog會記錄SourceContext,裡面包含了名稱空間和類的資訊,這裡我們把SourceContext包含“Microsoft”的資訊過濾掉,只記錄Error及Error級別以上的資訊,配置如下:
Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() .Enrich.WithProperty("IP", GetIP()) .MinimumLevel.Override("Microsoft", LogEventLevel.Error) .WriteTo.MSSqlServer( connectionString: Configuration["Serilog:ConnectionString"], tableName: Configuration["Serilog:TableName"], batchPostingLimit: Configuration.GetValue<int>("Serilog:BatchPostingLimit"),//批量插入資料庫條數 period: TimeSpan.FromSeconds(5),//執行時間間隔 restrictedToMinimumLevel: Configuration.GetValue<LogEventLevel>("Serilog:MinimumLevel"), columnOptions: columnOptions, autoCreateSqlTable: true ).CreateLogger();
3、通過Filter進行過濾
通過Filter可以過濾Properties中的值,比如一般我們會對資料庫的錯誤比較重視,希望把資料庫錯誤單獨放在一個表中,這時需要用到Filter,我們把SourceContext中包含資料訪問層名稱空間的資訊提取出來
string namespace = "DAL";//資料訪問層名稱空間 Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() .Enrich.WithProperty("IP", GetIP()) .MinimumLevel.Override("Microsoft", LogEventLevel.Error) .WriteTo.Logger(lc => lc.Filter.ByIncludingOnly(Matching.WithProperty(namespace)) .WriteTo.MSSqlServer( connectionString: Configuration["Serilog:ConnectionString"], tableName: Configuration["Serilog:DBErrorTableName"], batchPostingLimit: Configuration.GetValue<int>("Serilog:BatchPostingLimit"),//批量插入資料庫條數 period: TimeSpan.FromSeconds(5),//執行時間間隔 columnOptions: columnOptions, autoCreateSqlTable: true)) .WriteTo.Logger(lc => lc.Filter.ByExcluding(Matching.WithProperty(namespace)) .WriteTo.MSSqlServer( connectionString: Configuration["Serilog:ConnectionString"], tableName: Configuration["Serilog:DefaultTableName"], batchPostingLimit: Configuration.GetValue<int>("Serilog:BatchPostingLimit"),//批量插入資料庫條數 period: TimeSpan.FromSeconds(5),//執行時間間隔 columnOptions: columnOptions, autoCreateSqlTable: true)) .CreateLogger();
五、Enricher
Enricher 的作用主要是增加記錄的資訊,比如Enrich.WithThreadId(),可以記錄執行緒資訊,Enrich.WithProperty()可以增加屬性資訊
自定義Enricher 可以引數這篇文章:https://www.cnblogs.com/weihanli/p/custom-serilog-enricher-to-record-more-info.html
參考資料
https://github.com/serilog/serilog
https://www.cnblogs.com/Leo_wl/p/7643400.html
https://www.cnblogs.com/Leo_wl/p/10943285.html