.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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM