MSSQL中的Serilog事件的事件类型

问题描述

我是Serilog的新手。最近,我刚开始将Serilog记录到我们的MSsql数据库中。我已经做到了;但是,在尝试将事件类型分配给不同的Serilog事件时遇到问题。当我尝试将事件类型信息保存到文本文件时有效,但在写入数据库时​​却无效。我已经阅读了许多不同的文章,但是我仍然必须缺少一些东西。任何帮助将不胜感激。我正在使用.NET Core 3.1。这就是我所拥有的。

这是我安装的与Serilog相关的Nuget软件包。

  • Serilog.AspNetCore(v 3.2.0)
  • Serilog.Enrichers.Environment(v 2.1.3)
  • Serilog.Enrichers.Process(v 2.0.1)
  • Serilog.Enrichers.Thread(v 3.1.0)
  • Serilog.Settings.Configuration(v 3.1.0)
  • Serilog.Sinks.MSsqlServer(v 5.5.1)
  • Serilog.Sinks.RollingFile(v 3.3.0)
  • MurmurHash-net-core(v 1.0.0)

数据库

USE [MyDatabase]
GO

/****** Object:  Table [dbo].[Logs]    Script Date: 8/17/2020 6:10:12 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Logs](
    [Id] [int] IDENTITY(1,1) NOT NULL,[Message] [nvarchar](max) NULL,[MessageTemplate] [nvarchar](max) NULL,[Level] [nvarchar](128) NULL,[TimeStamp] [datetimeoffset](7) NOT NULL,[Exception] [nvarchar](max) NULL,[Properties] [xml] NULL,[LogEvent] [nvarchar](max) NULL,CONSTRAINT [PK_Logs] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF,STATISTICS_norECOmpuTE = OFF,IGnorE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON,ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXtimage_ON [PRIMARY]
GO

appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "@R_210_4045@ion"
    }
  },"ConnectionStrings": {
    "datamart": "Data Source=.\\MSsqlSERVER2K16,53307;Initial Catalog=MyDatabase;Integrated Security=sspI;MultipleActiveResultSets=True"
  },"Serilog": {
    "Using": [ "Serilog.Sinks.MSsqlServer" ],"MinimumLevel": {
      "Default": "@R_210_4045@ion","Override": {
        "Microsoft": "@R_210_4045@ion","System": "@R_210_4045@ion"
      }
    },"Writeto": [
      {
        "Name": "RollingFile","Args": {
          "pathFormat": "C:\\Temp\\Application-API-log-{Date}.txt","outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{EventType:x8} {Level:u3}] {Message:lj}{NewLine}{Exception}"
        }
      },{
        "Name": "MSsqlServer","Args": {
          "connectionString": "Data Source=.\\MSsqlSERVER2k16,53307;Initial Catalog=MyDatabase;Integrated Security=sspI;MultipleActiveResultSets=True","schemaName": "dbo","tableName": "Logs","autocreatesqlTable": false,"restrictedToMinimumLevel": "@R_210_4045@ion"
        }
      }
    ],"Properties": {
      "Application": "Application Api"
    }
  },"AllowedHosts": "*"
}

EventTypeEnricher.cs

class EventTypeEnricher : ILogEventEnricher
{
    public void Enrich(LogEvent logEvent,ILogEventPropertyFactory propertyFactory)
    {
        var murmur = MurmurHash.Create32();
        var bytes = Encoding.UTF8.GetBytes(logEvent.MessageTemplate.Text);
        var hash = murmur.ComputeHash(bytes);
        var numericHash = BitConverter.ToUInt32(hash,0);
        var eventType = propertyFactory.CreateProperty("EventType",numericHash);
        logEvent.AddPropertyIfAbsent(eventType);
    }
}

Program.cs

public static class Program
{
    public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json",optional: false,reloadOnChange: true)
        .AddJsonFile($"appsettings.{Environment.GetEnvironmentvariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json",optional: true)
        .Build();

    public static void Main(string[] args)
    {
        Log.Logger = new LoggerConfiguration()
            .ReadFrom.Configuration(Configuration)
            .Enrich.With<EventTypeEnricher>()
            .Enrich.WithThreadId()
            .Enrich.WithProcessId()
            .Enrich.WithMachineName()
            .Enrich.WithEnvironmentUserName()
            .CreateLogger();

        Serilog.Debugging.SelfLog.Enable(msg =>
        {
            Debug.Print(msg);
            Debugger.Break();
        });

        try
        {
            Log.@R_210_4045@ion("Application version {Version} starting up",typeof(Program).Assembly.GetName().Version);
            BuildWebHost(args).Run();
        }
        catch (Exception ex)
        {
            Log.Fatal(ex,"Host terminated unexpectedly");
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .UseConfiguration(Configuration)
            .UseSerilog()
            .Build();
}

解决方法

您可以采取许多步骤来解决SQL Server接收器无法正常工作的问题。下面的答案描述了常见的问题:

Serilog MSSQL Sink doesn't write logs to database

默认情况下,您包含在日志语句中的所有日志事件属性都将保存到XML属性列或JSON ofType(FETCH_USER)列中。但也可以通过LogEvent集合将它们存储在自己的单独列中

Custom Property Columns

,

我认为您混淆了 LogEvent、Properties 和 EventType 列。在开始之前,重要的是要知道 LogEvent 是一个令人困惑的名称,因为它也是用于在 serilog 中写入日志的 name of the class。基本上,当您编写日志时,会创建一个 LogEvent 对象,其中包含所有数据(手动传递的项目,如消息模板和道具值,自动信息,如时间戳,以及通过丰富器附加的任何其他信息) ) 的日志。我将使用斜体来表示类。

为了连接这个新属性,您需要通过以下任一方式将该列添加到接收器的附加列选项中:

var columnOpts = new ColumnOptions();
columnOpts.Store.Remove(StandardColumn.Properties ); // removes the xml serialization of log event properties
columnOpts.Store.Add(StandardColumn.LogEvent ); // add the json serialization of log event props
columnOpts.AdditionalColumns = new List<SqlColumn>() // tell the sink to create a column for the EventType
{
    new SqlColumn { DataType = SqlDbType.NVarChar,ColumnName = "EventType",AllowNull = true }
};

return new LoggerConfiguration()
    .MinimumLevel.Information()
    .Enrich.With<EventTypeEnricher>()
    .WriteTo.MSSqlServer(connectionString: connString,sinkOptions: sinkOpts,columnOptions: columnOpts,restrictedToMinimumLevel: LogEventLevel.Information )
    .CreateLogger();

您在“属性”列中看到 EventType 的原因是因为该列捕获发送到日志事件的所有数据。

TL;博士 LogEvent/Properties 列将 日志事件 的所有数据存储为序列化的 json/xml 字符串。如果您想将 EventType 视为自己的列,则需要告诉接收器创建此列。