C# Serilog的使用

发布时间 2023-09-14 21:14:27作者: 说不出来

Serilog简介

Serilog为文件、控制台和其他地方提供诊断日志记录。它易于设置,有一个干净的API,并且可以在最新的.NET平台之间移植。同时Serilog的也提供了强大的结构化事件数据的能力。

下文设计的示例均为案例一二下的代码修改

Serilog等级

Serilog等级设置如下:

等级 说明
Verbose 最低级别,很少(如果有的话)为生产应用程序启用
Debug 调试用于内部系统事件,主要用于系统调试下的信息,不建议用于常规记录
Information 信息事件描述了系统中发生的与其职责和功能相对应的事情。一般来说,主要记录系统运行信息
Warning 当服务降级、受到威胁或可能超出其预期参数时,将使用警告级别事件
Error 当功能不可用,期望值被打破时或出现程序bug时,将使用“错误”事件
Fatal 最严重的一级,出现严重影响程序运行时,致命事件需要立即关注

Serilog Enricher

Serilog Enricher可以通过各种方式丰富提供更加丰富属性。Serilog同时存在内置Enricher和自定义Enricher

  • 部分系统内置Enricher为:
位置
Serilog.Enrichers.Environment WithMachineName() 和 WithEnvironmentUserName()
Serilog.Enrichers.Process WithProcessId()
Serilog.Enrichers.Thread WithThreadId()
Serilog.Web.Classic WithHttpRequestId() 和其他
Serilog.Exceptions WithExceptionDetails()
Serilog.Enrichers.Demystify WithDemystifiedStackTraces()
Serilog.Enrichers.ClientInfo WithClientIp(), WithCorrelationId() ,WithRequestHeader("header") 和WithRequestHeader("name")
Serilog.Enrichers.ExcelDna WithXllPath() 和其他
Serilog.Enrichers.Sensitive WithSensitiveDataMasking()
Serilog.Enrichers.GlobalLogContext FromGlobalLogContext()

第一步,首先安装Nuget包Serilog.Enrichers.Thread

Install-Package Serilog.Enrichers.Thread

第二部,代码中添加对应的内置的Enricher
一下示例Enrich.WithEnvironmentUserName()将计算机用户打印至日志中

services.AddLogging(logBuilder =>
            {
                Log.Logger = new LoggerConfiguration().MinimumLevel.Debug()
                .Enrich.WithEnvironmentUserName()
                .WriteTo.Console(new JsonFormatter())
                .WriteTo.File("SerilFileLog.txt", outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}]{Message:lj}{NewLine}{Exception}")
                .CreateLogger();
                logBuilder.AddSerilog();
            });

以上的测试结果为:

  • 自定义Enricher
    第一步,首先安装Nuget包Serilog
Install-Package Serilog

第二步,自定类实现对应的接口,此处代码演示多新增一个扩展方法
自定义Enricher类

//新增类文件
using Microsoft.AspNetCore.Http;
using Serilog.Configuration;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SerilogTest
{
    public class InfoEnricher : ILogEventEnricher
    {
        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
        {
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Hello", "Hello World"));
        }
    }

    public static class EnricherExtensions
    {
        public static LoggerConfiguration WithInfo(this LoggerEnrichmentConfiguration enrich)
        {
            if (enrich == null)
                throw new ArgumentNullException(nameof(enrich));

            return enrich.With<InfoEnricher>();
        }
    }
}

控制台入口类

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Formatting.Json;
using Serilog.Settings.Configuration;
using System.Diagnostics.CodeAnalysis;
using Serilog.Core;
using Serilog.Events;
using Serilog.Enrichers;

namespace SerilogTest
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            ServiceCollection services = new ServiceCollection();
            services.AddLogging(logBuilder =>
            {
                Log.Logger = new LoggerConfiguration().MinimumLevel.Debug()
                .Enrich.WithEnvironmentUserName()
                .Enrich.WithInfo()
   //使用自定义的Enricher
                .WriteTo.Console(new JsonFormatter())
                .WriteTo.File("SerilFileLog.txt", outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}]{Message:lj}{NewLine}{Exception}")
                .CreateLogger();
                logBuilder.AddSerilog();
            });

            services.AddScoped<Test1>();

            using (var sp = services.BuildServiceProvider())
            {
                var test1 = sp.GetRequiredService<Test1>();
                for (int i = 0; i < 1; i++)
                {
                    test1.Test();
                }
            }
        }
    }
}

Test1类

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SerilogTest
{
    public class Test1
    {
        private readonly ILogger<Test1> logger;

        public Test1(ILogger<Test1> logger)
        {
            this.logger = logger;
        }

        public void Test()
        {
            logger.LogInformation("开始");
            logger.LogDebug("开始执行数据库同步");
            logger.LogDebug("连接数据库成功");
            logger.LogWarning("查找数据库失败,重试第一次");
            logger.LogWarning("查找数据库失败,重试第二次");
            logger.LogError("查找数据最终失败");

            var user = new { Name = "admin", Email = "123@qq.com" };
            logger.LogDebug("注册一个用户{@user}", user);
        }
    }
}

以上代码的测试结果为:

Serilog使用Filter

Filter可以有选择性的筛选出某些数据,可以通过Func委托来过滤数据

  • ByIncludingOnly筛选出Key为Hello,值为Hello World的数据
  • ByExcluding筛选时去除掉Key为Hello,值为Hello World的数据

控制台入口类

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Formatting.Json;
using Serilog.Settings.Configuration;
using System.Diagnostics.CodeAnalysis;
using Serilog.Core;
using Serilog.Events;
using Serilog.Enrichers;
using Serilog.Filters;
using System.Reflection.Emit;

namespace SerilogTest
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            ServiceCollection services = new ServiceCollection();
            services.AddLogging(logBuilder =>
            {
                Log.Logger = new LoggerConfiguration().MinimumLevel.Debug()
                .Enrich.WithEnvironmentUserName()
                .Enrich.WithInfo()
                .Filter.ByIncludingOnly(Matching.WithProperty<string>("Hello", p => p.Equals("Hello World")))
                //.Filter.ByExcluding(Matching.WithProperty<string>("Hello", p => p.Equals("Hello World")))
                .WriteTo.Console(new JsonFormatter())
                .WriteTo.File("SerilFileLog.txt", outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}]{Message:lj}{NewLine}{Exception}")
                .CreateLogger();
                logBuilder.AddSerilog();
            });

            services.AddScoped<Test1>();

            using (var sp = services.BuildServiceProvider())
            {
                var test1 = sp.GetRequiredService<Test1>();
                for (int i = 0; i < 1; i++)
                {
                    test1.Test();
                }
            }
        }
    }
}

自定义Enricher类

using Microsoft.AspNetCore.Http;
using Serilog.Configuration;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SerilogTest
{
    public class InfoEnricher : ILogEventEnricher
    {
        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
        {
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Hello", "Hello World"));
        }
    }

    public static class EnricherExtensions
    {
        public static LoggerConfiguration WithInfo(this LoggerEnrichmentConfiguration enrich)
        {
            if (enrich == null)
                throw new ArgumentNullException(nameof(enrich));

            return enrich.With<InfoEnricher>();
        }
    }
}

Test1类

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SerilogTest
{
    public class Test1
    {
        private readonly ILogger<Test1> logger;

        public Test1(ILogger<Test1> logger)
        {
            this.logger = logger;
        }

        public void Test()
        {
            logger.LogInformation("开始");
            logger.LogDebug("开始执行数据库同步");
            logger.LogDebug("连接数据库成功");
            logger.LogWarning("查找数据库失败,重试第一次");
            logger.LogWarning("查找数据库失败,重试第二次");
            logger.LogError("查找数据最终失败");

            var user = new { Name = "admin", Email = "123@qq.com" };
            logger.LogDebug("注册一个用户{@user}", user);
        }
    }
}

测试结果分别如下:

案例演示一

第一步,首先安装Nuget包Serilog.AspNetCore,该包依赖于Seilog同时还依赖于其他相关的组件

Install-Package Serilog.AspNetCore

第二步,此处我们建立控制台程序用于测试输出
代码方式控制日志输出格式为:

  1. 设置格式为Json格式
  2. 打印日志至控制台
  3. 以"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"输出至文件中
  4. 设置最小输出等级为Debug

控制台入口类

using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Formatting.Json;

namespace SerilogTest
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            ServiceCollection services = new ServiceCollection();
            services.AddLogging(logBuilder =>
            {
                Log.Logger = new LoggerConfiguration().MinimumLevel.Debug()
                .Enrich.FromLogContext()
                .WriteTo.Console(new JsonFormatter())
                .WriteTo.File("SerilFileLog.txt", outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
                .CreateLogger();
                logBuilder.AddSerilog();
            });

            services.AddScoped<Test1>();

            using (var sp = services.BuildServiceProvider())
            {
                var test1 = sp.GetRequiredService<Test1>();
                for (int i = 0; i < 1; i++)
                {
                    test1.Test();
                }
            }
        }
    }
}

Test1类

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SerilogTest
{
    public class Test1
    {
        private readonly ILogger<Test1> logger;

        public Test1(ILogger<Test1> logger)
        {
            this.logger = logger;
        }

        public void Test()
        {
            logger.LogInformation("开始");
            logger.LogDebug("开始执行数据库同步");
            logger.LogDebug("连接数据库成功");
            logger.LogWarning("查找数据库失败,重试第一次");
            logger.LogWarning("查找数据库失败,重试第二次");
            logger.LogError("查找数据最终失败");

            var user = new { Name = "admin", Email = "123@qq.com" };
            //结构化输出类,类可以保存成Json格式
            logger.LogDebug("注册一个用户{@user}", user);
        }
    }
}

测试结果如下:

案例演示二

第一步,首先安装Nuget包Serilog.AspNetCore,该包依赖于Seilog同时还依赖于其他相关的组件

Install-Package Serilog.AspNetCore
Install-Package Microsoft.Extensions.Configuration.Json
Install-Package Serilog.Sinks.Console
Install-Package Serilog.Sinks.File
Install-Package Serilog.Enrichers.Environment 
Install-Package Serilog.Enrichers.Thread 

第二步,此处我们建立控制台程序用于测试输出
配置文件设置配置项方式控制日志输出格式为:

  1. 设置格式为Json格式
  2. 打印日志至控制台
  3. 以"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"输出至文件中
  4. 设置最小输出等级为Debug

控制台入口类

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Formatting.Json;
using Serilog.Settings.Configuration;
using System.Diagnostics.CodeAnalysis;
using Serilog.Core;
using Serilog.Events;

namespace SerilogTest
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            ServiceCollection services = new ServiceCollection();
            //services.AddLogging(logBuilder =>
            //{
            //    Log.Logger = new LoggerConfiguration().MinimumLevel.Debug()
            //    .Enrich.FromLogContext()
            //    .WriteTo.Console(new JsonFormatter())
            //    .WriteTo.File("SerilFileLog.txt", outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
            //    .CreateLogger();
            //    logBuilder.AddSerilog();
            //});

            services.AddLogging(logBuilder =>
            {
                Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(
                    new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json")
                    .Build()
                )
                .CreateLogger();
                logBuilder.AddSerilog();
            });

            services.AddScoped<Test1>();

            using (var sp = services.BuildServiceProvider())
            {
                var test1 = sp.GetRequiredService<Test1>();
                for (int i = 0; i < 1; i++)
                {
                    test1.Test();
                }
            }
        }
    }
}

Test1类

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SerilogTest
{
    public class Test1
    {
        private readonly ILogger<Test1> logger;

        public Test1(ILogger<Test1> logger)
        {
            this.logger = logger;
        }

        public void Test()
        {
            logger.LogInformation("开始");
            logger.LogDebug("开始执行数据库同步");
            logger.LogDebug("连接数据库成功");
            logger.LogWarning("查找数据库失败,重试第一次");
            logger.LogWarning("查找数据库失败,重试第二次");
            logger.LogError("查找数据最终失败");

            var user = new { Name = "admin", Email = "123@qq.com" };
            //结构化输出类,类可以保存成Json格式
            logger.LogDebug("注册一个用户{@user}", user);
        }
    }
}

配置项类

//appsettings.json
{
 "Serilog": {
 "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
 "MinimumLevel": "Debug",
 "WriteTo": [
{ "Name": "Console" },
      {
 "Name": "File",
 "Args": { "path": "Logs/Serilog.txt" }
      }
    ],
 "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
 "Destructure": [
      {
 "Name": "ToMaximumDepth",
 "Args": { "maximumDestructuringDepth": 4 }
      },
      {
 "Name": "ToMaximumStringLength",
 "Args": { "maximumStringLength": 100 }
      },
      {
 "Name": "ToMaximumCollectionCount",
 "Args": { "maximumCollectionCount": 10 }
      }
    ],
 "Properties": {
 "Application": "Sample"
    }
  }
}

以上测试结果为:

案例演示三

将数据写入到sql server数据库中
第一步,首先安装Nuget包Serilog.AspNetCore,该包依赖于Seilog同时还依赖于其他相关的组件

Install-Package Serilog.AspNetCore
Install-Package Microsoft.Extensions.Configuration.Json
Install-Package Serilog.Sinks.Console
Install-Package Serilog.Sinks.File
Install-Package Serilog.Enrichers.Environment 
Install-Package Serilog.Enrichers.Thread 
Install-Package Serilog.Sinks.MSSqlServer

控制台入口类

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Formatting.Json;
using Serilog.Settings.Configuration;
using System.Diagnostics.CodeAnalysis;
using Serilog.Core;
using Serilog.Events;
using Serilog.Enrichers;
using Serilog.Filters;
using System.Reflection.Emit;
using Serilog.Sinks.MSSqlServer;

namespace SerilogTest
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            ServiceCollection services = new ServiceCollection();
            //services.AddLogging(logBuilder =>
            //{
            //    Log.Logger = new LoggerConfiguration().MinimumLevel.Debug()
            //    .Enrich.WithEnvironmentUserName()
            //    .Enrich.WithInfo()
            //    .WriteTo.Console(new JsonFormatter())
            //    .WriteTo.File("SerilFileLog.txt", outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}]{Message:lj}{NewLine}{Exception}")
            //    .WriteTo.MSSqlServer(connectionString: "Server=localhost;Database=SerilogTest;User Id=sa; Password=123456;", sinkOptions: new MSSqlServerSinkOptions { TableName = "Log" })
            //    .CreateLogger();
            //    logBuilder.AddSerilog();
            //});

            services.AddLogging(logBuilder =>
            {
                Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(
                    new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json")
                    .Build()
                )
                .CreateLogger();
                logBuilder.AddSerilog();
            });

            services.AddScoped<Test1>();

            using (var sp = services.BuildServiceProvider())
            {
                var test1 = sp.GetRequiredService<Test1>();
                for (int i = 0; i < 1; i++)
                {
                    test1.Test();
                }
            }
        }
    }
}

Test1类

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SerilogTest
{
    public class Test1
    {
        private readonly ILogger<Test1> logger;

        public Test1(ILogger<Test1> logger)
        {
            this.logger = logger;
        }

        public void Test()
        {
            logger.LogInformation("开始");
            logger.LogDebug("开始执行数据库同步");
            logger.LogDebug("连接数据库成功");
            logger.LogWarning("查找数据库失败,重试第一次");
            logger.LogWarning("查找数据库失败,重试第二次");
            logger.LogError("查找数据最终失败");

            var user = new { Name = "admin", Email = "123@qq.com" };
            //结构化输出类,类可以保存成Json格式
            logger.LogDebug("注册一个用户{@user}", user);
        }
    }
}

配置项类

//appsettings.json
{
  "Serilog": {
    "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
    "MinimumLevel": "Debug",
    "WriteTo": [
      { "Name": "Console" },
      {
        "Name": "File",
        "Args": { "path": "Logs/Serilog.txt" }
      },
      {
        "Name": "MSSqlServer",
        "Args": {
          "connectionString": "Server=localhost;Database=SerilogTest;User Id=sa; Password=123456;",
          "tableName": "Log",
          "autoCreateSqlTable": true
        }
      }
    ],
    "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
    "Destructure": [
      {
        "Name": "ToMaximumDepth",
        "Args": { "maximumDestructuringDepth": 4 }
      },
      {
        "Name": "ToMaximumStringLength",
        "Args": { "maximumStringLength": 100 }
      },
      {
        "Name": "ToMaximumCollectionCount",
        "Args": { "maximumCollectionCount": 10 }
      }
    ],
    "Properties": {
      "Application": "Sample"
    }
  }
}

Serilog自动创建的数据库表格等同如下如下:

CREATE TABLE [Logs] (

   [Id] int IDENTITY(1,1) NOT NULL,
   [Message] nvarchar(max) NULL,
   [MessageTemplate] nvarchar(max) NULL,
   [Level] nvarchar(128) NULL,
   [TimeStamp] datetime NOT NULL,
   [Exception] nvarchar(max) NULL,
   [Properties] nvarchar(max) NULL

   CONSTRAINT [PK_Logs] PRIMARY KEY CLUSTERED ([Id] ASC)
);

Serilog官方Github wiki Configuration Basics · serilog/serilog Wiki (github.com)