Skip to content

Commit 2d0eee3

Browse files
nhart12Nathan Hart
authored andcommitted
Truncate additional columns, allow varchar on standard columns
1 parent 3fdce96 commit 2d0eee3

File tree

9 files changed

+61
-16
lines changed

9 files changed

+61
-16
lines changed

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/ColumnOptions/ExceptionColumnOptions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ public ExceptionColumnOptions() : base()
2727
get => base.DataType;
2828
set
2929
{
30-
if (value != SqlDbType.NVarChar)
31-
throw new ArgumentException("The Standard Column \"Exception\" must be NVarChar.");
30+
if (!SqlDataTypes.VariableCharactorColumnTypes.Contains(value))
31+
throw new ArgumentException("The Standard Column \"Exception\" must be NVarChar or VarChar.");
3232
base.DataType = value;
3333
}
3434
}

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/ColumnOptions/LevelColumnOptions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ public LevelColumnOptions() : base()
2828
get => base.DataType;
2929
set
3030
{
31-
if (value != SqlDbType.NVarChar && value != SqlDbType.TinyInt)
32-
throw new ArgumentException("The Standard Column \"Level\" must be of data type NVarChar or TinyInt.");
31+
if (!SqlDataTypes.VariableCharactorColumnTypes.Contains(value) && value != SqlDbType.TinyInt)
32+
throw new ArgumentException("The Standard Column \"Level\" must be of data type NVarChar, VarChar or TinyInt.");
3333
base.DataType = value;
3434
}
3535
}

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/ColumnOptions/LogEventColumnOptions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ public LogEventColumnOptions() : base()
2727
get => base.DataType;
2828
set
2929
{
30-
if (value != SqlDbType.NVarChar)
31-
throw new ArgumentException("The Standard Column \"LogEvent\" must be NVarChar.");
30+
if (!SqlDataTypes.VariableCharactorColumnTypes.Contains(value))
31+
throw new ArgumentException("The Standard Column \"LogEvent\" must be NVarChar or VarChar.");
3232
base.DataType = value;
3333
}
3434
}

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/ColumnOptions/MessageColumnOptions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ public MessageColumnOptions() : base()
2727
get => base.DataType;
2828
set
2929
{
30-
if (value != SqlDbType.NVarChar)
31-
throw new ArgumentException("The Standard Column \"Message\" must be NVarChar.");
30+
if (!SqlDataTypes.VariableCharactorColumnTypes.Contains(value))
31+
throw new ArgumentException("The Standard Column \"Message\" must be NVarChar or VarChar.");
3232
base.DataType = value;
3333
}
3434
}

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/ColumnOptions/MessageTemplateColumnOptions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ public MessageTemplateColumnOptions() : base()
2727
get => base.DataType;
2828
set
2929
{
30-
if (value != SqlDbType.NVarChar)
31-
throw new ArgumentException("The Standard Column \"MessageTemplate\" must be NVarChar.");
30+
if (!SqlDataTypes.VariableCharactorColumnTypes.Contains(value))
31+
throw new ArgumentException("The Standard Column \"MessageTemplate\" must be NVarChar or VarChar.");
3232
base.DataType = value;
3333
}
3434
}

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/AdditionalColumnDataGenerator.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.ComponentModel;
44
using Serilog.Events;
5+
using Serilog.Sinks.MSSqlServer.Extensions;
56

67
namespace Serilog.Sinks.MSSqlServer.Output
78
{
@@ -45,6 +46,11 @@ public KeyValuePair<string, object> GetAdditionalColumnNameAndValue(SqlColumn ad
4546
var columnType = additionalColumn.AsDataColumn().DataType;
4647
if (columnType.IsAssignableFrom(scalarValue.Value.GetType()))
4748
{
49+
if (SqlDataTypes.DataLengthRequired.Contains(additionalColumn.DataType))
50+
{
51+
return new KeyValuePair<string, object>(columnName, TruncateOutput((string)scalarValue.Value, additionalColumn.DataLength));
52+
53+
}
4854
return new KeyValuePair<string, object>(columnName, scalarValue.Value);
4955
}
5056

@@ -54,10 +60,15 @@ public KeyValuePair<string, object> GetAdditionalColumnNameAndValue(SqlColumn ad
5460
}
5561
else
5662
{
57-
return new KeyValuePair<string, object>(columnName, property.Value.ToString());
63+
return new KeyValuePair<string, object>(columnName, DBNull.Value);
5864
}
5965
}
6066

67+
private static string TruncateOutput(string value, int dataLength) =>
68+
dataLength < 0
69+
? value // No need to truncate if length set to maximum
70+
: value.Truncate(dataLength, string.Empty);
71+
6172
private static bool TryChangeType(object obj, Type type, out object conversion)
6273
{
6374
conversion = null;

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/SqlDataTypes.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ public static class SqlDataTypes
5151
// not supported by enum: numeric, FILESTREAM, rowversion
5252
};
5353

54+
/// <summary>
55+
/// SQL column types for supported strings
56+
/// </summary>
57+
public static readonly HashSet<SqlDbType> VariableCharactorColumnTypes = new HashSet<SqlDbType>
58+
{
59+
SqlDbType.NVarChar,
60+
SqlDbType.VarChar
61+
};
62+
5463
/// <summary>
5564
/// The SQL column types which require a non-zero DataLength property.
5665
/// </summary>

test/Serilog.Sinks.MSSqlServer.Tests/Misc/SqlTypesTests.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,12 @@ public void AuditLogCharacterDataSqlTypes()
108108
Log.Information("NVarChar {NVarChar}", twentyChars);
109109
Log.Information("VarChar {VarChar}", twentyChars);
110110

111-
// should throw truncation exception
111+
// should truncate but not throw
112112

113-
Assert.Throws<AggregateException>(() => Log.Information("Char {Char}", thirtyChars));
113+
Log.Information("Char {Char}", thirtyChars);
114+
Log.Information("NChar {NChar}", thirtyChars);
115+
Log.Information("NVarChar {NVarChar}", thirtyChars);
116+
Log.Information("VarChar {VarChar}", thirtyChars);
114117

115118
Log.CloseAndFlush();
116119
}

test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Output/AdditionalColumnDataGeneratorTests.cs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public void GetAdditionalColumnNameAndValueReturnsNullForNotFoundHierachicalProp
114114
}
115115

116116
[Fact]
117-
public void GetAdditionalColumnNameAndValueConvertsValueTypeToStringIfConversionToColumnTypeFails()
117+
public void GetAdditionalColumnNameAndValueUsesNullIfConversionToColumnTypeFails()
118118
{
119119
// Arrange
120120
const string columnName = "AdditionalProperty1";
@@ -131,8 +131,8 @@ public void GetAdditionalColumnNameAndValueConvertsValueTypeToStringIfConversion
131131
// Assert
132132
_columnSimplePropertyValueResolver.Verify(r => r.GetPropertyValueForColumn(additionalColumn, properties), Times.Once);
133133
Assert.Equal(columnName, result.Key);
134-
Assert.IsType<string>(result.Value);
135-
Assert.Equal("1", result.Value); // Cannot convert int to SqlDbType.DateTimeOffset so returns string
134+
Assert.IsType<DBNull>(result.Value);
135+
Assert.Equal(DBNull.Value, result.Value); // Cannot convert int to SqlDbType.DateTimeOffset so returns null
136136
}
137137

138138
[Fact]
@@ -180,5 +180,27 @@ public void GetAdditionalColumnNameAndValueConvertsNullValueForNullable()
180180
Assert.IsType<DBNull>(result.Value);
181181
Assert.Equal(DBNull.Value, result.Value);
182182
}
183+
184+
[Fact]
185+
public void GetAdditionalColumnNameAndValueReturnsTruncatedForCharacterTypesWithDataLength()
186+
{
187+
// Arrange
188+
const string columnName = "AdditionalProperty1";
189+
const string propertyValue = "Additional Property Value";
190+
var additionalColumn = new SqlColumn(columnName, SqlDbType.NVarChar);
191+
additionalColumn.DataLength = 10;
192+
var properties = new Dictionary<string, LogEventPropertyValue>();
193+
_columnSimplePropertyValueResolver.Setup(r => r.GetPropertyValueForColumn(
194+
It.IsAny<SqlColumn>(), It.IsAny<IReadOnlyDictionary<string, LogEventPropertyValue>>()))
195+
.Returns(new KeyValuePair<string, LogEventPropertyValue>(columnName, new ScalarValue(propertyValue)));
196+
197+
// Act
198+
var result = _sut.GetAdditionalColumnNameAndValue(additionalColumn, properties);
199+
200+
// Assert
201+
_columnSimplePropertyValueResolver.Verify(r => r.GetPropertyValueForColumn(additionalColumn, properties), Times.Once);
202+
Assert.Equal(columnName, result.Key);
203+
Assert.Equal("Additional", result.Value);
204+
}
183205
}
184206
}

0 commit comments

Comments
 (0)