From da7d219f60dda72cc41f8fcd59298a3291996555 Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:15:36 +0200 Subject: [PATCH 1/5] Bumped minor version Bumped minor version after release --- src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj b/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj index babe262a..8240a56b 100644 --- a/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj +++ b/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj @@ -2,7 +2,7 @@ A Serilog sink that writes events to Microsoft SQL Server and Azure SQL - 9.0.1 + 9.0.2 true 9.0.0 Michiel van Oudheusden;Christian Kadluba;Serilog Contributors From 3904789484d344f109dcce90f9062268841b8496 Mon Sep 17 00:00:00 2001 From: Gianni Bonicolini Date: Thu, 23 Oct 2025 15:25:38 +0200 Subject: [PATCH 2/5] Fix TraceId and SpanId with null value --- .../Output/StandardColumnDataGenerator.cs | 4 +- .../StandardColumnDataGeneratorTests.cs | 38 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/StandardColumnDataGenerator.cs b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/StandardColumnDataGenerator.cs index 2766504b..938b5271 100644 --- a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/StandardColumnDataGenerator.cs +++ b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/StandardColumnDataGenerator.cs @@ -51,9 +51,9 @@ public KeyValuePair GetStandardColumnNameAndValue(StandardColumn case StandardColumn.Level: return new KeyValuePair(_columnOptions.Level.ColumnName, _columnOptions.Level.StoreAsEnum ? (object)logEvent.Level : logEvent.Level.ToString()); case StandardColumn.TraceId: - return new KeyValuePair(_columnOptions.TraceId.ColumnName, logEvent.TraceId.ToString()); + return new KeyValuePair(_columnOptions.TraceId.ColumnName, logEvent.TraceId?.ToString()); case StandardColumn.SpanId: - return new KeyValuePair(_columnOptions.SpanId.ColumnName, logEvent.SpanId.ToString()); + return new KeyValuePair(_columnOptions.SpanId.ColumnName, logEvent.SpanId?.ToString()); case StandardColumn.TimeStamp: return GetTimeStampStandardColumnNameAndValue(logEvent); case StandardColumn.Exception: diff --git a/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Output/StandardColumnDataGeneratorTests.cs b/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Output/StandardColumnDataGeneratorTests.cs index a14b7302..87ef6023 100644 --- a/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Output/StandardColumnDataGeneratorTests.cs +++ b/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Output/StandardColumnDataGeneratorTests.cs @@ -282,6 +282,25 @@ public void GetStandardColumnNameAndValueForTraceIdReturnsLogLevelKeyValue() Assert.Equal("34898a9020e0390190b0982370034f00", result.Value); } + [Fact] + public void GetStandardColumnNameAndNullValueForTraceIdReturnsLogLevelKeyValue() + { + // Arrange + var traceId = default(ActivityTraceId); + var logEvent = new LogEvent( + new DateTimeOffset(2020, 1, 1, 0, 0, 0, 0, TimeSpan.Zero), + LogEventLevel.Debug, null, new MessageTemplate(new List() { new TextToken("Test message") }), + new List(), traceId, ActivitySpanId.CreateRandom()); + SetupSut(new MSSqlServer.ColumnOptions(), CultureInfo.InvariantCulture); + + // Act + var result = _sut.GetStandardColumnNameAndValue(StandardColumn.TraceId, logEvent); + + // Assert + Assert.Equal("TraceId", result.Key); + Assert.Null(result.Value); + } + [Fact] public void GetStandardColumnNameAndValueForSpanIdReturnsLogLevelKeyValue() { @@ -301,6 +320,25 @@ public void GetStandardColumnNameAndValueForSpanIdReturnsLogLevelKeyValue() Assert.Equal("0390190b09823700", result.Value); } + [Fact] + public void GetStandardColumnNameAndNullValueForSpanIdReturnsLogLevelKeyValue() + { + // Arrange + var spanId = default(ActivitySpanId); + var logEvent = new LogEvent( + new DateTimeOffset(2020, 1, 1, 0, 0, 0, 0, TimeSpan.Zero), + LogEventLevel.Debug, null, new MessageTemplate(new List() { new TextToken("Test message") }), + new List(), ActivityTraceId.CreateRandom(), spanId); + SetupSut(new MSSqlServer.ColumnOptions(), CultureInfo.InvariantCulture); + + // Act + var result = _sut.GetStandardColumnNameAndValue(StandardColumn.SpanId, logEvent); + + // Assert + Assert.Equal("SpanId", result.Key); + Assert.Null(result.Value); + } + [Fact] public void GetStandardColumnNameAndValueForLogLevelReturnsLogLevelKeyValueAsEnum() { From 32eea6c84b006ce1dda8f12ff7b40ad011af664b Mon Sep 17 00:00:00 2001 From: Gianni Bonicolini Date: Fri, 24 Oct 2025 09:15:03 +0200 Subject: [PATCH 3/5] Added default value when field doesn't allow null --- .../Output/StandardColumnDataGenerator.cs | 9 ++- .../StandardColumnDataGeneratorTests.cs | 63 +++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/StandardColumnDataGenerator.cs b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/StandardColumnDataGenerator.cs index 938b5271..ff090665 100644 --- a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/StandardColumnDataGenerator.cs +++ b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/StandardColumnDataGenerator.cs @@ -51,13 +51,16 @@ public KeyValuePair GetStandardColumnNameAndValue(StandardColumn case StandardColumn.Level: return new KeyValuePair(_columnOptions.Level.ColumnName, _columnOptions.Level.StoreAsEnum ? (object)logEvent.Level : logEvent.Level.ToString()); case StandardColumn.TraceId: - return new KeyValuePair(_columnOptions.TraceId.ColumnName, logEvent.TraceId?.ToString()); + var userDefaultTraceIdValue = logEvent.TraceId is null && !_columnOptions.TraceId.AllowNull; + return new KeyValuePair(_columnOptions.TraceId.ColumnName, userDefaultTraceIdValue ? _columnOptions.TraceId.AsDataColumn().DefaultValue.ToString() : logEvent.TraceId?.ToString()); case StandardColumn.SpanId: - return new KeyValuePair(_columnOptions.SpanId.ColumnName, logEvent.SpanId?.ToString()); + var userDefaultSpanIdValue = logEvent.SpanId is null && !_columnOptions.SpanId.AllowNull; + return new KeyValuePair(_columnOptions.SpanId.ColumnName, userDefaultSpanIdValue ? _columnOptions.SpanId.AsDataColumn().DefaultValue.ToString() : logEvent.SpanId?.ToString()); case StandardColumn.TimeStamp: return GetTimeStampStandardColumnNameAndValue(logEvent); case StandardColumn.Exception: - return new KeyValuePair(_columnOptions.Exception.ColumnName, logEvent.Exception?.ToString().TruncateOutput(_columnOptions.Exception.DataLength)); + var userDefaultExceptionValue = logEvent.Exception is null && !_columnOptions.Exception.AllowNull; + return new KeyValuePair(_columnOptions.Exception.ColumnName, userDefaultExceptionValue ? _columnOptions.Exception.AsDataColumn().DefaultValue.ToString() : logEvent.Exception?.ToString().TruncateOutput(_columnOptions.Exception.DataLength)); case StandardColumn.Properties: return new KeyValuePair(_columnOptions.Properties.ColumnName, ConvertPropertiesToXmlStructure(logEvent.Properties)); case StandardColumn.LogEvent: diff --git a/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Output/StandardColumnDataGeneratorTests.cs b/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Output/StandardColumnDataGeneratorTests.cs index 87ef6023..9c0b3265 100644 --- a/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Output/StandardColumnDataGeneratorTests.cs +++ b/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Output/StandardColumnDataGeneratorTests.cs @@ -301,6 +301,27 @@ public void GetStandardColumnNameAndNullValueForTraceIdReturnsLogLevelKeyValue() Assert.Null(result.Value); } + [Fact] + public void GetStandardColumnNameAndNullValueForTraceIdWithoutAllowNullReturnsLogLevelKeyValueEmpty() + { + // Arrange + var traceId = default(ActivityTraceId); + var logEvent = new LogEvent( + new DateTimeOffset(2020, 1, 1, 0, 0, 0, 0, TimeSpan.Zero), + LogEventLevel.Debug, null, new MessageTemplate(new List() { new TextToken("Test message") }), + new List(), traceId, ActivitySpanId.CreateRandom()); + var columnOptions = new MSSqlServer.ColumnOptions(); + columnOptions.TraceId.AllowNull = false; + SetupSut(columnOptions, CultureInfo.InvariantCulture); + + // Act + var result = _sut.GetStandardColumnNameAndValue(StandardColumn.TraceId, logEvent); + + // Assert + Assert.Equal("TraceId", result.Key); + Assert.Equal(string.Empty, result.Value); + } + [Fact] public void GetStandardColumnNameAndValueForSpanIdReturnsLogLevelKeyValue() { @@ -339,6 +360,27 @@ public void GetStandardColumnNameAndNullValueForSpanIdReturnsLogLevelKeyValue() Assert.Null(result.Value); } + [Fact] + public void GetStandardColumnNameAndNullValueForSpanIdWithoutAllowNullReturnsLogLevelKeyValueEmpty() + { + // Arrange + var spanId = default(ActivitySpanId); + var logEvent = new LogEvent( + new DateTimeOffset(2020, 1, 1, 0, 0, 0, 0, TimeSpan.Zero), + LogEventLevel.Debug, null, new MessageTemplate(new List() { new TextToken("Test message") }), + new List(), ActivityTraceId.CreateRandom(), spanId); + var columnOptions = new MSSqlServer.ColumnOptions(); + columnOptions.SpanId.AllowNull = false; + SetupSut(columnOptions, CultureInfo.InvariantCulture); + + // Act + var result = _sut.GetStandardColumnNameAndValue(StandardColumn.SpanId, logEvent); + + // Assert + Assert.Equal("SpanId", result.Key); + Assert.Equal(string.Empty, result.Value); + } + [Fact] public void GetStandardColumnNameAndValueForLogLevelReturnsLogLevelKeyValueAsEnum() { @@ -487,6 +529,27 @@ public void GetStandardColumnNameAndValueForExceptionWhenCalledWithoutExceptionR Assert.Null(result.Value); } + [Fact] + public void GetStandardColumnNameAndValueForExceptionWhenCalledWithoutExceptionAndNotAllowedNullReturnsEmptyValue() + { + // Arrange + var logEvent = new LogEvent( + new DateTimeOffset(2020, 1, 1, 0, 0, 0, 0, TimeSpan.Zero), + LogEventLevel.Debug, null, new MessageTemplate(new List() { new TextToken("Test message") }), + new List()); + var columnOptions = new Serilog.Sinks.MSSqlServer.ColumnOptions(); + columnOptions.Level.StoreAsEnum = true; + columnOptions.Exception.AllowNull = false; + SetupSut(columnOptions, CultureInfo.InvariantCulture); + + // Act + var result = _sut.GetStandardColumnNameAndValue(StandardColumn.Exception, logEvent); + + // Assert + Assert.Equal("Exception", result.Key); + Assert.Equal(string.Empty, result.Value); + } + [Fact] public void GetStandardColumnNameAndValueForPropertiesUsesRootElementName() { From bc42981ee9bf74fe1ca0649ab1051fa81515d669 Mon Sep 17 00:00:00 2001 From: Gianni Bonicolini Date: Sat, 25 Oct 2025 12:34:15 +0200 Subject: [PATCH 4/5] Fix typo --- .../Output/StandardColumnDataGenerator.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/StandardColumnDataGenerator.cs b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/StandardColumnDataGenerator.cs index ff090665..435dc741 100644 --- a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/StandardColumnDataGenerator.cs +++ b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/StandardColumnDataGenerator.cs @@ -51,16 +51,16 @@ public KeyValuePair GetStandardColumnNameAndValue(StandardColumn case StandardColumn.Level: return new KeyValuePair(_columnOptions.Level.ColumnName, _columnOptions.Level.StoreAsEnum ? (object)logEvent.Level : logEvent.Level.ToString()); case StandardColumn.TraceId: - var userDefaultTraceIdValue = logEvent.TraceId is null && !_columnOptions.TraceId.AllowNull; - return new KeyValuePair(_columnOptions.TraceId.ColumnName, userDefaultTraceIdValue ? _columnOptions.TraceId.AsDataColumn().DefaultValue.ToString() : logEvent.TraceId?.ToString()); + var useDefaultTraceIdValue = logEvent.TraceId is null && !_columnOptions.TraceId.AllowNull; + return new KeyValuePair(_columnOptions.TraceId.ColumnName, useDefaultTraceIdValue ? _columnOptions.TraceId.AsDataColumn().DefaultValue.ToString() : logEvent.TraceId?.ToString()); case StandardColumn.SpanId: - var userDefaultSpanIdValue = logEvent.SpanId is null && !_columnOptions.SpanId.AllowNull; - return new KeyValuePair(_columnOptions.SpanId.ColumnName, userDefaultSpanIdValue ? _columnOptions.SpanId.AsDataColumn().DefaultValue.ToString() : logEvent.SpanId?.ToString()); + var useDefaultSpanIdValue = logEvent.SpanId is null && !_columnOptions.SpanId.AllowNull; + return new KeyValuePair(_columnOptions.SpanId.ColumnName, useDefaultSpanIdValue ? _columnOptions.SpanId.AsDataColumn().DefaultValue.ToString() : logEvent.SpanId?.ToString()); case StandardColumn.TimeStamp: return GetTimeStampStandardColumnNameAndValue(logEvent); case StandardColumn.Exception: - var userDefaultExceptionValue = logEvent.Exception is null && !_columnOptions.Exception.AllowNull; - return new KeyValuePair(_columnOptions.Exception.ColumnName, userDefaultExceptionValue ? _columnOptions.Exception.AsDataColumn().DefaultValue.ToString() : logEvent.Exception?.ToString().TruncateOutput(_columnOptions.Exception.DataLength)); + var useDefaultExceptionValue = logEvent.Exception is null && !_columnOptions.Exception.AllowNull; + return new KeyValuePair(_columnOptions.Exception.ColumnName, useDefaultExceptionValue ? _columnOptions.Exception.AsDataColumn().DefaultValue.ToString() : logEvent.Exception?.ToString().TruncateOutput(_columnOptions.Exception.DataLength)); case StandardColumn.Properties: return new KeyValuePair(_columnOptions.Properties.ColumnName, ConvertPropertiesToXmlStructure(logEvent.Properties)); case StandardColumn.LogEvent: From 0e5287abfb2738105f8d06040f6976b949dc097e Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Sun, 2 Nov 2025 15:49:11 +0100 Subject: [PATCH 5/5] Update CHANGES.md --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 72b8da47..0bd08f88 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +# 9.0.2 +* Fixed issue #643: TraceId and SpanId are saved as empty string instead of NULL (thanks to @nanny07) + # 9.0.1 * Fixed issue #642: NuGet package downgrade System.Configuration.ConfigurationManager error * Updated sample apps to .NET 9