zoukankan      html  css  js  c++  java
  • .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

    https://www.cnblogs.com/Quinnz/p/12202633.html

  • 相关阅读:
    Linux-获取当前正在执行脚本的绝对路径
    Linux-SSL和SSH和OpenSSH,OpenSSL有什么区别
    Nginx-SSI
    Linux-TCP/IP TIME_WAIT状态原理
    Linux-IP地址后边加个/8(16,24,32)是什么意思?
    Nginx-/etc/sysctl.conf 参数解释
    Linux-内核缓存区和write行为
    常用音频软件:Cool edit pro
    常用音频软件:Wavesufer
    【转】智能音箱技术概览
  • 原文地址:https://www.cnblogs.com/fengchao1000/p/11811244.html
Copyright © 2011-2022 走看看