Asp.Net Core 使用Serilog记录日志

忘忧 2022年08月12日 459次浏览

Serilog简介

Serilog是Asp.Net Core 新兴的日志框架,本文这里简单的介绍一下它的用法以及对他进行简单的封装
官网

Serilog配置

安装基础库

  • Install-Package Serilog
  • Install-Package Serilog.Sinks.Console
    其中包Serilog是Log核心库,Serilog.Sinks.Console是Log的控制台输出库,这个也是日志框架的一贯策略,一个核心库加多个输出库组合使用,这样可以保持良好的扩展性。

简单的示例:

using (var log = new LoggerConfiguration()
           .WriteTo.Console()
           .CreateLogger())
{
    log.Information("Hello, world!");
    log.Warning("Goodbye, world.");
    log.Error("Goodbye, world.");
}

image-1660302334737

Serilog介绍

LoggerConfiguration

这里用了一个LoggerConfiguration对象,它主要用于创建和设置Log对象,类似于Nlog里面的LogManager类。这里主要用它两个方法:

  • WriteTo:WriteTo属性用来设置日志的输出,Serilog将其称为Sink(水槽),还是比较形象的。
  • CreateLogger:用于创建一个ILogger类型的Logger对象.

Serilog封装

 public static class SerilogHostingExtensions
    {
        /// <summary>
        /// 加载Serilog配置文件
        /// </summary>
        /// <returns></returns>
        public static IConfiguration AddDefaultConfiguration()
        {
            var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
            return new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{environmentName}.json", optional: false, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();
        }
        /// <summary>
        /// 使用Serilog日志记录 以文件的方式记录
        /// </summary>
        /// <param name="hostBuilder"></param>
        /// <returns></returns>
        public static IWebHostBuilder UseSerilogDefault(this IWebHostBuilder hostBuilder)
        {
            
            var config = AddDefaultConfiguration();
            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(config)
                .WriteTo.Console()
                .CreateLogger();
            hostBuilder.UseSerilog();

            return hostBuilder;

        }
        /// <summary>
        /// 获取log文件
        /// </summary>
        /// <param name="config"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static string GetLogFile(this IConfiguration config, string defaultValue)
        {
            return config.GetValue<string>("LogFile", defaultValue);
        }
    }

Program.cs

//注入日志
builder.WebHost.UseSerilogDefault();

如何使用

ILogger

ILogger对象用于记录日志,和其他日志框架差不多。Serilog日志级别分为如下5级

  • Verbose
  • Debug
  • Information
  • Warning
  • Error
  • Fatal
    大多数的日志也是这样5级,只是有的名称叫的不同(NLog第1级叫Trace,其它的一致),每一级别对应一个写Log的函数:

全局Logger对象

在实际的使用过程中,往往并不是每次使用都去创建一个ILogger,一种方式是通过依赖注入的方式创建一个全局的Logger。不过这种方式需要引入DI框架。在小程序中使用不算方便。

另一种方式是直接使用静态的Log类,它也携带了写入日志的方法,用起来非常方便。
image-1660303085369

常见存储格式

Serilog的输出对象称之为Sink(水槽),github上提供了大量的第三方的可用sinks,这里简单的列举几个常用的:

  • Console 输出到控制台
  • Debug 输出到VS的Debug窗口
  • File 输出到文件
  • MongoDB 输出到MongoDB
  • LiteDB 输出到文件数据库LiteDB
  • SQLite 输出到文件数据库SQLite
  • SignalR 输出为SignalR服务
  • HTTP 输出到REST服务
  • Loki 输出Loki分布式分布式日志框架
  • Elasticsearch 输出到ELK分布式日志框架

输出格式配置

Serilog的日志输出通过LoggerConfiguration类配置,详细的配置参数可以参看官方文档:Configuration Basics。在日常使用中,感觉更多的是直接通过LoggerConfiguration在代码中配置。

输出到文件中

安装扩展

  • Install-Package Serilog.Sinks.File
        /// <summary>
        /// 使用Serilog日志记录 以Loki分布式的方式记录
        /// </summary>
        /// <param name="hostBuilder"></param>
        /// <returns></returns>
        public static IWebHostBuilder UseSerilogDefault(this IWebHostBuilder hostBuilder)
        {
            
            var config = AddDefaultConfiguration();
            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(config)
                .WriteTo.Console()
                .WriteTo.File(config.GetLogFile("logs/.log"), rollingInterval: RollingInterval.Day)
                .CreateLogger();
            hostBuilder.UseSerilog();

            return hostBuilder;

        }

配合Loki使用

安装扩展

  • Install-Package Serilog.Sinks.Grafana.Loki
  • Install-Package Serilog.Settings.Configuration
        /// <summary>
        /// 使用Serilog日志记录 以Loki分布式的方式记录
        /// </summary>
        /// <param name="hostBuilder"></param>
        /// <returns></returns>
        public static IWebHostBuilder UseSerilogDefault(this IWebHostBuilder hostBuilder)
        {
            
            var config = AddDefaultConfiguration();
            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(config)
                .WriteTo.Console()
				.Enrich.FromLogContext()
    			.WriteTo.GrafanaLoki(
        		"http://localhost:3100")
                .CreateLogger();
            hostBuilder.UseSerilog();

            return hostBuilder;

        }

json配置文件形式

{
  "Serilog": {
    "Using": [
      "Serilog.Sinks.Grafana.Loki"
    ],
    "MinimumLevel": {
      "Default": "Debug"
    },
    "WriteTo": [
      {
        "Name": "GrafanaLoki",
        "Args": {
          "uri": "http://localhost:3100",
          "labels": [
            {
              "key": "app",
              "value": "web_app"
            }
          ],
          "propertiesAsLabels": [
            "app"
          ]
        }
      }
    ]
  }
}

配合ELK使用

安装扩展

  • Install-Package serilog.formatting.elasticsearch
  • Install-Package serilog.sinks.elasticsearch
  • Install-Package Serilog.Settings.Configuration
        /// <summary>
        /// 使用Serilog日志记录 以ELK分布式的方式记录
        /// </summary>
        /// <param name="hostBuilder"></param>
        /// <returns></returns>
        public static IWebHostBuilder UseSerilogDefault(this IWebHostBuilder hostBuilder)
        {
            
            var config = AddDefaultConfiguration();
            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(config)
                .WriteTo.Console()
				.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9200") ){
             AutoRegisterTemplate = true,
             AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv6
     })
                .CreateLogger();
            hostBuilder.UseSerilog();

            return hostBuilder;

        }

json配置项

        /// <summary>
        /// 使用Serilog日志记录 以ELK分布式的方式记录
        /// </summary>
        /// <param name="hostBuilder"></param>
        /// <returns></returns>
        public static IWebHostBuilder UseSerilogDefault(this IWebHostBuilder hostBuilder)
        {
            var config = new ConfigurationBuilder()
    .SetBasePath(env.ContentRootPath)
    .AddJsonFile("appsettings.json")
    .Build();
            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(config)
                .WriteTo.Console()
				.Enrich.FromLogContext()
            hostBuilder.UseSerilog();

            return hostBuilder;

        }

var logger = new LoggerConfiguration()
    .ReadFrom.Configuration(configuration)
    .CreateLogger();
{
  "Serilog": {
    "WriteTo": [{
        "Name": "Elasticsearch",
        "Args": {
          "nodeUris": "http://localhost:9200;http://remotehost:9200/",
          "indexFormat": "custom-index-{0:yyyy.MM}",
          "templateName": "myCustomTemplate",
          "typeName": "myCustomLogEventType",
          "pipelineName": "myCustomPipelineName",
          "batchPostingLimit": 50,
          "batchAction": "Create",
          "period": 2,
          "inlineFields": true,
          "restrictedToMinimumLevel": "Warning",
          "bufferBaseFilename":  "C:/Temp/docker-elk-serilog-web-buffer",
          "bufferFileSizeLimitBytes": 5242880,
          "bufferLogShippingInterval": 5000,
          "bufferRetainedInvalidPayloadsLimitBytes": 5000,
          "bufferFileCountLimit": 31,
          "connectionGlobalHeaders" :"Authorization=Bearer SOME-TOKEN;OtherHeader=OTHER-HEADER-VALUE",
          "connectionTimeout": 5,
          "emitEventFailure": "WriteToSelfLog",
          "queueSizeLimit": "100000",
          "autoRegisterTemplate": true,
          "autoRegisterTemplateVersion": "ESv2",
          "overwriteTemplate": false,
          "registerTemplateFailure": "IndexAnyway",
          "deadLetterIndexName": "deadletter-{0:yyyy.MM}",
          "numberOfShards": 20,
          "numberOfReplicas": 10,
          "templateCustomSettings": [{ "index.mapping.total_fields.limit": "10000000" } ],
          "formatProvider": "My.Namespace.MyFormatProvider, My.Assembly.Name",
          "connection": "My.Namespace.MyConnection, My.Assembly.Name",
          "serializer": "My.Namespace.MySerializer, My.Assembly.Name",
          "connectionPool": "My.Namespace.MyConnectionPool, My.Assembly.Name",
          "customFormatter": "My.Namespace.MyCustomFormatter, My.Assembly.Name",
          "customDurableFormatter": "My.Namespace.MyCustomDurableFormatter, My.Assembly.Name",
          "failureSink": "My.Namespace.MyFailureSink, My.Assembly.Name"
        }
    }]
  }
}

参考文章

Serilog简介