"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 值类型,所以如果你给它一个 Guidstring 表示,它就不会工作,并且会给你那种类型的异常。

也许在代码库的某个地方,您在记录之前在 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.