"Guid should contain 32 digits" serilog 错误 sql 服务器接收器
"Guid should contain 32 digits" serilog error with sql server sink
我偶尔会在使用 MSSQLServer 接收器时遇到此错误。我看不出这个向导有什么问题。有任何想法吗?我已经在每个可以找到的地方进行了验证,源 guid 的数据类型是“Guid”而不是字符串。我只是有点迷惑。
Guid 应包含 32 位数字和 4 个破折号 (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxx)。无法在 UserId 列中存储 <"7526f485-ec2d-4ec8-bd73-12a7d1c49a5d">。预期类型为 Guid。
本例中的 guid 是:
7526f485-ec2d-4ec8-bd73-12a7d1c49a5d
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
模板似乎与我匹配?
更多详情:
这是一个偶然的问题,但是当它出现时它会出现很多。它似乎与特定的 Guids 相关联。大多数 Guids 都很好,但一小部分有这个问题。我们的应用程序每天记录数以千计的消息,但这些消息未被记录(由于问题),因此我很难准确追踪导致此错误的特定日志的来源。但是,我们使用的是 运行 类似这样的集中式日志记录方法。这个测试对我来说通过了,但它反映了我们通常用于日志记录的设置和代码,通常会成功。正如我所说,这是一个间歇性问题:
[Fact]
public void Foobar()
{
// arrange
var columnOptions = new ColumnOptions
{
AdditionalColumns = new Collection<SqlColumn>
{
new SqlColumn {DataType = SqlDbType.UniqueIdentifier, ColumnName = "UserId"},
},
};
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.Store.Remove(StandardColumn.LogEvent);
columnOptions.Properties.ExcludeAdditionalProperties = true;
var badGuid = new Guid("7526f485-ec2d-4ec8-bd73-12a7d1c49a5d");
var connectionString = "Server=(localdb)\MSSQLLocalDB;Database=SomeDb;Trusted_Connection=True;MultipleActiveResultSets=true";
var logConfiguration = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.MSSqlServer(connectionString, "Logs",
restrictedToMinimumLevel: LogEventLevel.Information, autoCreateSqlTable: false,
columnOptions: columnOptions)
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Information);
Log.Logger = logConfiguration.CreateLogger();
// Suspect the issue is with this line
LogContext.PushProperty("UserId", badGuid);
// Best practice would be to do something like this:
// using (LogContext.PushProperty("UserId", badGuid)
// {
Log.Logger.Information(new FormatException("Foobar"),"This is a test");
// }
Log.CloseAndFlush();
}
构建此测试代码后我注意到的一件事是,UserId 属性 的“PushProperty”未被捕获和处理。由于在这种情况下行为是 "undefined",我倾向于修复它并查看问题是否消失。
全栈:
2020-04-20T08:38:17.5145399Z Exception while emitting periodic batch from Serilog.Sinks.MSSqlServer.MSSqlServerSink: System.ArgumentException: Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).Couldn't store <"7526f485-ec2d-4ec8-bd73-12a7d1c49a5d"> in UserId Column. Expected type is Guid.
---> System.FormatException: Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
at System.Guid.GuidResult.SetFailure(Boolean overflow, String failureMessageID)
at System.Guid.TryParseExactD(ReadOnlySpan`1 guidString, GuidResult& result)
at System.Guid.TryParseGuid(ReadOnlySpan`1 guidString, GuidResult& result)
at System.Guid..ctor(String g)
at System.Data.Common.ObjectStorage.Set(Int32 recordNo, Object value)
at System.Data.DataColumn.set_Item(Int32 record, Object value)
--- End of inner exception stack trace ---
at System.Data.DataColumn.set_Item(Int32 record, Object value)
at System.Data.DataRow.set_Item(DataColumn column, Object value)
at Serilog.Sinks.MSSqlServer.MSSqlServerSink.FillDataTable(IEnumerable`1 events)
at Serilog.Sinks.MSSqlServer.MSSqlServerSink.EmitBatchAsync(IEnumerable`1 events)
at Serilog.Sinks.PeriodicBatching.PeriodicBatchingSink.OnTick()
分辨率
造成此问题的原因是有人创建了一个日志消息,其中的占位符与我们的自定义数据列同名,但传入的是 guid 的字符串版本,而不是键入的 guid。
非常简单的例子:
var badGuid = "7526f485-ec2d-4ec8-bd73-12a7d1c49a5d";
var badGuidConverted = Guid.Parse(badGuid); // just proving the guid is actually valid.
var goodGuid = Guid.NewGuid();
using (LogContext.PushProperty("UserId",goodGuid))
{
Log.Logger.Information("This is a problem with my other user {userid} that will crash serilog. This message will never end up in the database.", badGuid);
}
快速解决方法是编辑消息模板,将占位符从 {userid} 更改为其他内容。
由于我们的代码集中在 PushProperty 发生的地方,我在那里进行了一些检查以监控这一点,并在将来有人再次这样做时抛出更有用的错误消息。
我在上面的特定代码中没有看到任何明显的问题。您在设置 Serilog 之前调用 PushProperty
的事实是我会改变的(即先设置 Serilog,然后调用 PushProperty
),但这似乎不是问题的根本原因有。
我的猜测是,您有一些代码路径将 UserId
记录为 string
,而不是 Guid
。 Serilog 需要一个 Guid
值类型,所以如果你给它一个 Guid
的 string
表示,它就不会工作,并且会给你那种类型的异常。
也许在代码库的某个地方,您在记录之前在 UserId
上调用了 .ToString
?或者也许使用字符串插值,例如Log.Information("User is {UserId}", $"{UserId}");
?
例如:
var badGuid = "7526f485-ec2d- 4ec8-bd73-12a7d1c49a5d";
LogContext.PushProperty("UserId", badGuid);
Log.Information(new FormatException("Foobar"), "This is a test");
或者甚至直接使用 UserId
属性 记录消息:
var badGuid = "7526f485-ec2d-4ec8-bd73-12a7d1c49a5d";
Log.Information("The {UserId} is doing work", badGuid);
Both snippets above would throw the same exception you're having, because they use string
values rather than real Guid
values.
我偶尔会在使用 MSSQLServer 接收器时遇到此错误。我看不出这个向导有什么问题。有任何想法吗?我已经在每个可以找到的地方进行了验证,源 guid 的数据类型是“Guid”而不是字符串。我只是有点迷惑。
Guid 应包含 32 位数字和 4 个破折号 (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxx)。无法在 UserId 列中存储 <"7526f485-ec2d-4ec8-bd73-12a7d1c49a5d">。预期类型为 Guid。
本例中的 guid 是:
7526f485-ec2d-4ec8-bd73-12a7d1c49a5d
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
模板似乎与我匹配?
更多详情: 这是一个偶然的问题,但是当它出现时它会出现很多。它似乎与特定的 Guids 相关联。大多数 Guids 都很好,但一小部分有这个问题。我们的应用程序每天记录数以千计的消息,但这些消息未被记录(由于问题),因此我很难准确追踪导致此错误的特定日志的来源。但是,我们使用的是 运行 类似这样的集中式日志记录方法。这个测试对我来说通过了,但它反映了我们通常用于日志记录的设置和代码,通常会成功。正如我所说,这是一个间歇性问题:
[Fact]
public void Foobar()
{
// arrange
var columnOptions = new ColumnOptions
{
AdditionalColumns = new Collection<SqlColumn>
{
new SqlColumn {DataType = SqlDbType.UniqueIdentifier, ColumnName = "UserId"},
},
};
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.Store.Remove(StandardColumn.LogEvent);
columnOptions.Properties.ExcludeAdditionalProperties = true;
var badGuid = new Guid("7526f485-ec2d-4ec8-bd73-12a7d1c49a5d");
var connectionString = "Server=(localdb)\MSSQLLocalDB;Database=SomeDb;Trusted_Connection=True;MultipleActiveResultSets=true";
var logConfiguration = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.MSSqlServer(connectionString, "Logs",
restrictedToMinimumLevel: LogEventLevel.Information, autoCreateSqlTable: false,
columnOptions: columnOptions)
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Information);
Log.Logger = logConfiguration.CreateLogger();
// Suspect the issue is with this line
LogContext.PushProperty("UserId", badGuid);
// Best practice would be to do something like this:
// using (LogContext.PushProperty("UserId", badGuid)
// {
Log.Logger.Information(new FormatException("Foobar"),"This is a test");
// }
Log.CloseAndFlush();
}
构建此测试代码后我注意到的一件事是,UserId 属性 的“PushProperty”未被捕获和处理。由于在这种情况下行为是 "undefined",我倾向于修复它并查看问题是否消失。
全栈:
2020-04-20T08:38:17.5145399Z Exception while emitting periodic batch from Serilog.Sinks.MSSqlServer.MSSqlServerSink: System.ArgumentException: Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).Couldn't store <"7526f485-ec2d-4ec8-bd73-12a7d1c49a5d"> in UserId Column. Expected type is Guid.
---> System.FormatException: Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
at System.Guid.GuidResult.SetFailure(Boolean overflow, String failureMessageID)
at System.Guid.TryParseExactD(ReadOnlySpan`1 guidString, GuidResult& result)
at System.Guid.TryParseGuid(ReadOnlySpan`1 guidString, GuidResult& result)
at System.Guid..ctor(String g)
at System.Data.Common.ObjectStorage.Set(Int32 recordNo, Object value)
at System.Data.DataColumn.set_Item(Int32 record, Object value)
--- End of inner exception stack trace ---
at System.Data.DataColumn.set_Item(Int32 record, Object value)
at System.Data.DataRow.set_Item(DataColumn column, Object value)
at Serilog.Sinks.MSSqlServer.MSSqlServerSink.FillDataTable(IEnumerable`1 events)
at Serilog.Sinks.MSSqlServer.MSSqlServerSink.EmitBatchAsync(IEnumerable`1 events)
at Serilog.Sinks.PeriodicBatching.PeriodicBatchingSink.OnTick()
分辨率
造成此问题的原因是有人创建了一个日志消息,其中的占位符与我们的自定义数据列同名,但传入的是 guid 的字符串版本,而不是键入的 guid。
非常简单的例子:
var badGuid = "7526f485-ec2d-4ec8-bd73-12a7d1c49a5d";
var badGuidConverted = Guid.Parse(badGuid); // just proving the guid is actually valid.
var goodGuid = Guid.NewGuid();
using (LogContext.PushProperty("UserId",goodGuid))
{
Log.Logger.Information("This is a problem with my other user {userid} that will crash serilog. This message will never end up in the database.", badGuid);
}
快速解决方法是编辑消息模板,将占位符从 {userid} 更改为其他内容。
由于我们的代码集中在 PushProperty 发生的地方,我在那里进行了一些检查以监控这一点,并在将来有人再次这样做时抛出更有用的错误消息。
我在上面的特定代码中没有看到任何明显的问题。您在设置 Serilog 之前调用 PushProperty
的事实是我会改变的(即先设置 Serilog,然后调用 PushProperty
),但这似乎不是问题的根本原因有。
我的猜测是,您有一些代码路径将 UserId
记录为 string
,而不是 Guid
。 Serilog 需要一个 Guid
值类型,所以如果你给它一个 Guid
的 string
表示,它就不会工作,并且会给你那种类型的异常。
也许在代码库的某个地方,您在记录之前在 UserId
上调用了 .ToString
?或者也许使用字符串插值,例如Log.Information("User is {UserId}", $"{UserId}");
?
例如:
var badGuid = "7526f485-ec2d- 4ec8-bd73-12a7d1c49a5d";
LogContext.PushProperty("UserId", badGuid);
Log.Information(new FormatException("Foobar"), "This is a test");
或者甚至直接使用 UserId
属性 记录消息:
var badGuid = "7526f485-ec2d-4ec8-bd73-12a7d1c49a5d";
Log.Information("The {UserId} is doing work", badGuid);
Both snippets above would throw the same exception you're having, because they use
string
values rather than realGuid
values.