Skip to content

Commit d6b5bec

Browse files
authored
Merge pull request #60 from serilog/dev
4.2.0 Release - fixes #59
2 parents b4ba091 + fc9186c commit d6b5bec

File tree

8 files changed

+212
-20
lines changed

8 files changed

+212
-20
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ public ICollection<StandardColumn> Store
6363
}
6464
}
6565

66+
/// <summary>
67+
/// Indicates if triggers should be disabled when inserting log entries.
68+
/// </summary>
69+
public bool DisableTriggers { get; set; }
70+
6671
/// <summary>
6772
/// Additional columns for data storage.
6873
/// </summary>

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/MSSqlServerSink.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,10 @@ protected override async Task EmitBatchAsync(IEnumerable<LogEvent> events)
129129
using (var cn = new SqlConnection(_connectionString))
130130
{
131131
await cn.OpenAsync().ConfigureAwait(false);
132-
using (var copy = new SqlBulkCopy(cn))
132+
using (var copy = _columnOptions.DisableTriggers
133+
? new SqlBulkCopy(cn)
134+
: new SqlBulkCopy(cn, SqlBulkCopyOptions.CheckConstraints | SqlBulkCopyOptions.FireTriggers, null)
135+
)
133136
{
134137
copy.DestinationTableName = _tableName;
135138
foreach (var column in _eventsTable.Columns)
@@ -253,25 +256,25 @@ void FillDataTable(IEnumerable<LogEvent> events)
253256
switch (column)
254257
{
255258
case StandardColumn.Message:
256-
row["Message"] = logEvent.RenderMessage(_formatProvider);
259+
row[_columnOptions.Message.ColumnName ?? "Message"] = logEvent.RenderMessage(_formatProvider);
257260
break;
258261
case StandardColumn.MessageTemplate:
259-
row["MessageTemplate"] = logEvent.MessageTemplate;
262+
row[_columnOptions.MessageTemplate.ColumnName ?? "MessageTemplate"] = logEvent.MessageTemplate;
260263
break;
261264
case StandardColumn.Level:
262-
row["Level"] = logEvent.Level;
265+
row[_columnOptions.Level.ColumnName ?? "Level"] = logEvent.Level;
263266
break;
264267
case StandardColumn.TimeStamp:
265-
row["TimeStamp"] = _columnOptions.TimeStamp.ConvertToUtc ? logEvent.Timestamp.DateTime.ToUniversalTime() : logEvent.Timestamp.DateTime;
268+
row[_columnOptions.TimeStamp.ColumnName ?? "TimeStamp"] = _columnOptions.TimeStamp.ConvertToUtc ? logEvent.Timestamp.DateTime.ToUniversalTime() : logEvent.Timestamp.DateTime;
266269
break;
267270
case StandardColumn.Exception:
268-
row["Exception"] = logEvent.Exception != null ? logEvent.Exception.ToString() : null;
271+
row[_columnOptions.Exception.ColumnName ?? "Exception"] = logEvent.Exception != null ? logEvent.Exception.ToString() : null;
269272
break;
270273
case StandardColumn.Properties:
271-
row["Properties"] = ConvertPropertiesToXmlStructure(logEvent.Properties);
274+
row[_columnOptions.Properties.ColumnName ?? "Properties"] = ConvertPropertiesToXmlStructure(logEvent.Properties);
272275
break;
273276
case StandardColumn.LogEvent:
274-
row["LogEvent"] = LogEventToJson(logEvent);
277+
row[_columnOptions.LogEvent.ColumnName ?? "LogEvent"] = LogEventToJson(logEvent);
275278
break;
276279
default:
277280
throw new ArgumentOutOfRangeException();

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/SqlTableCreator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ private static string GetSqlFromDataTable(string tableName, DataTable table)
5555
foreach (DataColumn column in table.Columns)
5656
{
5757
sql.AppendFormat("[{0}] {1}", column.ColumnName, SqlGetType(column));
58-
if (column.ColumnName.ToUpper().Equals("ID"))
58+
if (column.ColumnName.ToUpper().Equals("ID") || column.AutoIncrement)
5959
sql.Append(" IDENTITY(1,1) ");
6060
if (numOfColumns > i)
6161
sql.AppendFormat(", ");

src/Serilog.Sinks.MSSqlServer/project.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
{
2-
"version": "4.1.1-*",
1+
{
2+
"version": "4.2.0-*",
33
"description": "A Serilog sink that writes events to Microsoft SQL Server",
44
"authors": [
55
"Michiel van Oudheusden",

test/Serilog.Sinks.MSSqlServer.Tests/CustomStandardColumnNames.cs

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Data.SqlClient;
4+
using System.Data.SqlTypes;
5+
using System.Diagnostics;
46
using System.IO;
57
using System.Linq;
68
using Dapper;
79
using Xunit;
810
using FluentAssertions;
9-
using Serilog.Events;
1011

1112
namespace Serilog.Sinks.MSSqlServer.Tests
1213
{
@@ -34,6 +35,18 @@ public void CustomIdColumn()
3435

3536
infoSchema.Should().Contain(columns => columns.ColumnName == customIdName);
3637
}
38+
39+
// verify Id column has identity property
40+
using (var conn = new SqlConnection(DatabaseFixture.LogEventsConnectionString))
41+
{
42+
var isIdentity = conn.Query<IdentityQuery>($"SELECT COLUMNPROPERTY(object_id('{logTableName}'), '{customIdName}', 'IsIdentity') AS IsIdentity");
43+
isIdentity.Should().Contain(i => i.IsIdentity == 1);
44+
}
45+
}
46+
47+
internal class IdentityQuery
48+
{
49+
public int IsIdentity { get; set; }
3750
}
3851

3952
[Fact]
@@ -47,13 +60,21 @@ public void DefaultIdColumn()
4760
var sink = new MSSqlServerSink(DatabaseFixture.LogEventsConnectionString, logTableName, 1, TimeSpan.FromSeconds(1), null, true, options);
4861

4962
// assert
63+
var idColumnName = "Id";
5064
using (var conn = new SqlConnection(DatabaseFixture.MasterConnectionString))
5165
{
5266
conn.Execute($"use {DatabaseFixture.Database}");
5367
var logEvents = conn.Query<InfoSchema>($@"SELECT COLUMN_NAME AS ColumnName FROM {DatabaseFixture.Database}.INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{logTableName}'");
5468
var infoSchema = logEvents as InfoSchema[] ?? logEvents.ToArray();
5569

56-
infoSchema.Should().Contain(columns => columns.ColumnName == "Id");
70+
infoSchema.Should().Contain(columns => columns.ColumnName == idColumnName);
71+
}
72+
73+
// verify Id column has identity property
74+
using (var conn = new SqlConnection(DatabaseFixture.LogEventsConnectionString))
75+
{
76+
var isIdentity = conn.Query<IdentityQuery>($"SELECT COLUMNPROPERTY(object_id('{logTableName}'), '{idColumnName}', 'IsIdentity') AS IsIdentity");
77+
isIdentity.Should().Contain(i => i.IsIdentity == 1);
5778
}
5879
}
5980

@@ -116,6 +137,46 @@ public void TableCreatedWithDefaultNames()
116137
}
117138
}
118139

140+
[Fact]
141+
public void WriteEventToCustomStandardColumns()
142+
{
143+
// arrange
144+
var options = new ColumnOptions();
145+
146+
options.Message.ColumnName = "CustomMessage";
147+
options.MessageTemplate.ColumnName = "CustomMessageTemplate";
148+
options.Level.ColumnName = "CustomLevel";
149+
options.TimeStamp.ColumnName = "CustomTimeStamp";
150+
options.Exception.ColumnName = "CustomException";
151+
options.Properties.ColumnName = "CustomProperties";
152+
options.Id.ColumnName = "CustomId";
153+
154+
var logTableName = $"{DatabaseFixture.LogTableName}CustomEvent";
155+
var loggerConfiguration = new LoggerConfiguration();
156+
Log.Logger = loggerConfiguration.WriteTo.MSSqlServer(
157+
connectionString: DatabaseFixture.LogEventsConnectionString,
158+
tableName: logTableName,
159+
autoCreateSqlTable: true,
160+
columnOptions: options)
161+
.CreateLogger();
162+
163+
var file = File.CreateText("CustomColumnsEvent.Self.log");
164+
Serilog.Debugging.SelfLog.Enable(TextWriter.Synchronized(file));
165+
166+
// act
167+
const string loggingInformationMessage = "Logging Information message";
168+
Log.Information(loggingInformationMessage);
169+
Log.CloseAndFlush();
170+
171+
// assert
172+
using (var conn = new SqlConnection(DatabaseFixture.LogEventsConnectionString))
173+
{
174+
var logEvents = conn.Query<CustomStandardLogColumns>($"SELECT * FROM {logTableName}");
175+
176+
logEvents.Should().Contain(e => e.CustomMessage.Contains(loggingInformationMessage));
177+
}
178+
}
179+
119180
[Fact]
120181
public void WriteEventToDefaultStandardColumns()
121182
{
@@ -148,11 +209,4 @@ public void WriteEventToDefaultStandardColumns()
148209
}
149210
}
150211
}
151-
152-
public class DefaultStandardLogColumns
153-
{
154-
public string Message { get; set; }
155-
156-
public LogEventLevel Level { get; set; }
157-
}
158212
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
3+
namespace Serilog.Sinks.MSSqlServer.Tests
4+
{
5+
public class CustomStandardLogColumns
6+
{
7+
public int CustomId { get; set; }
8+
public string CustomMessage { get; set; }
9+
public string CustomMessageTemplate { get; set; }
10+
public string CustomLevel { get; set; }
11+
public DateTime CustomTimeStamp { get; set; }
12+
public string CustomException { get; set; }
13+
public string CustomProperties { get; set; }
14+
}
15+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using Serilog.Events;
2+
3+
namespace Serilog.Sinks.MSSqlServer.Tests
4+
{
5+
public class DefaultStandardLogColumns
6+
{
7+
public string Message { get; set; }
8+
9+
public LogEventLevel Level { get; set; }
10+
}
11+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using System;
2+
using System.Data.SqlClient;
3+
using System.Diagnostics;
4+
using Dapper;
5+
using FluentAssertions;
6+
using Xunit;
7+
8+
namespace Serilog.Sinks.MSSqlServer.Tests
9+
{
10+
[Collection("LogTest")]
11+
public class TestTriggersOnLogTable
12+
{
13+
[Fact]
14+
public void TestTriggerOnLogTableFire()
15+
{
16+
// arrange
17+
var logTriggerTableName = $"Trigger{DatabaseFixture.LogTableName}Trigger";
18+
var logTableName = $"{DatabaseFixture.LogTableName}WithTrigger";
19+
var loggerConfiguration = new LoggerConfiguration();
20+
Log.Logger = loggerConfiguration.WriteTo.MSSqlServer(
21+
connectionString: DatabaseFixture.LogEventsConnectionString,
22+
tableName: logTableName,
23+
autoCreateSqlTable: true,
24+
batchPostingLimit: 1,
25+
period: TimeSpan.FromSeconds(10),
26+
columnOptions: new ColumnOptions())
27+
.CreateLogger();
28+
29+
using (var conn = new SqlConnection(DatabaseFixture.LogEventsConnectionString))
30+
{
31+
conn.Execute($"CREATE TABLE {logTriggerTableName} ([Id] [UNIQUEIDENTIFIER] NOT NULL, [Data] [NVARCHAR](50) NOT NULL)");
32+
conn.Execute($@"CREATE TRIGGER {logTriggerTableName}NoTrigger ON {logTableName}
33+
AFTER INSERT
34+
AS
35+
BEGIN
36+
INSERT INTO {logTriggerTableName} VALUES (NEWID(), 'Data')
37+
END");
38+
}
39+
40+
// act
41+
const string loggingInformationMessage = "Logging Information message";
42+
Log.Information(loggingInformationMessage);
43+
44+
Log.CloseAndFlush();
45+
46+
// assert
47+
using (var conn = new SqlConnection(DatabaseFixture.LogEventsConnectionString))
48+
{
49+
var logTriggerEvents = conn.Query<TestTriggerEntry>($"SELECT * FROM {logTriggerTableName}");
50+
51+
logTriggerEvents.Should().NotBeNullOrEmpty();
52+
}
53+
}
54+
55+
[Fact]
56+
public void TestOptionsDisableTriggersOnLogTable()
57+
{
58+
// arrange
59+
var options = new ColumnOptions { DisableTriggers = true };
60+
var logTriggerTableName = $"{DatabaseFixture.LogTableName}NoTrigger";
61+
var logTableName = $"{DatabaseFixture.LogTableName}WithTrigger";
62+
var loggerConfiguration = new LoggerConfiguration();
63+
Log.Logger = loggerConfiguration.WriteTo.MSSqlServer(
64+
connectionString: DatabaseFixture.LogEventsConnectionString,
65+
tableName: logTableName,
66+
autoCreateSqlTable: true,
67+
batchPostingLimit: 1,
68+
period: TimeSpan.FromSeconds(10),
69+
columnOptions: options)
70+
.CreateLogger();
71+
72+
using (var conn = new SqlConnection(DatabaseFixture.LogEventsConnectionString))
73+
{
74+
conn.Execute($"CREATE TABLE {logTriggerTableName} ([Id] [UNIQUEIDENTIFIER] NOT NULL, [Data] [NVARCHAR](50) NOT NULL)");
75+
conn.Execute($@"CREATE TRIGGER {logTableName}NoTrigger ON {logTableName}
76+
AFTER INSERT
77+
AS
78+
BEGIN
79+
INSERT INTO {logTriggerTableName} VALUES (NEWID(), 'Data')
80+
END");
81+
}
82+
83+
// act
84+
const string loggingInformationMessage = "Logging Information message";
85+
Log.Information(loggingInformationMessage);
86+
87+
Log.CloseAndFlush();
88+
89+
// assert
90+
using (var conn = new SqlConnection(DatabaseFixture.LogEventsConnectionString))
91+
{
92+
var logTriggerEvents = conn.Query<TestTriggerEntry>($"SELECT * FROM {logTriggerTableName}");
93+
94+
logTriggerEvents.Should().BeEmpty();
95+
}
96+
}
97+
98+
internal class TestTriggerEntry
99+
{
100+
public Guid Id { get; set; }
101+
public string Data { get; set; }
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)