问题描述
我是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接收器无法正常工作的问题。下面的答案描述了常见的问题:
,默认情况下,您包含在日志语句中的所有日志事件属性都将保存到XML属性列或JSON
ofType(FETCH_USER)
列中。但也可以通过LogEvent
集合将它们存储在自己的单独列中
我认为您混淆了 LogEvent、Properties 和 EventType 列。在开始之前,重要的是要知道 LogEvent 是一个令人困惑的名称,因为它也是用于在 serilog 中写入日志的 name of the class。基本上,当您编写日志时,会创建一个 LogEvent 对象,其中包含所有数据(手动传递的项目,如消息模板和道具值,自动信息,如时间戳,以及通过丰富器附加的任何其他信息) ) 的日志。我将使用斜体来表示类。
- 日志事件列:
- Stores log event property values as JSON. Typically you will use either this column or the XML-based Properties column,but not both。
- 默认情况下启用“属性”列,但您可以通过编辑 Standard Columns 使用“LogEvent”轻松将其关闭。
-
EventType column/enricher:
- 这个扩充器只是简单地对消息模板进行哈希处理,以创建一个唯一标识符,您可以使用它来查找同一消息模板的日志。 扩充器仅将属性附加到每个 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 视为自己的列,则需要告诉接收器创建此列。