Skip to content

Commit 86cec67

Browse files
committed
feat: add SqlInsertBatchWriter
1 parent 287b9d3 commit 86cec67

File tree

6 files changed

+375
-3
lines changed

6 files changed

+375
-3
lines changed

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactory.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,13 @@ internal static SinkDependencies Create(
5252
sqlCreateDatabaseWriter, sqlConnectionFactoryNoDb),
5353
SqlTableCreator = new SqlTableCreator(
5454
sqlCreateTableWriter, sqlConnectionFactory),
55-
SqlBulkBatchWriter = new SqlBulkBatchWriter(
56-
sinkOptions.TableName, sinkOptions.SchemaName, columnOptions.DisableTriggers,
57-
sqlConnectionFactory, logEventDataGenerator),
55+
SqlBulkBatchWriter = sinkOptions.UseSqlBulkCopy
56+
? (ISqlBulkBatchWriter)new SqlBulkBatchWriter(
57+
sinkOptions.TableName, sinkOptions.SchemaName, columnOptions.DisableTriggers,
58+
sqlConnectionFactory, logEventDataGenerator)
59+
: (ISqlBulkBatchWriter)new SqlInsertBatchWriter(
60+
sinkOptions.TableName, sinkOptions.SchemaName,
61+
sqlConnectionFactory, logEventDataGenerator),
5862
SqlLogEventWriter = new SqlLogEventWriter(
5963
sinkOptions.TableName, sinkOptions.SchemaName,
6064
sqlConnectionFactory, logEventDataGenerator)

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/MSSqlServerSinkOptions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public MSSqlServerSinkOptions()
1717
BatchPostingLimit = MSSqlServerSink.DefaultBatchPostingLimit;
1818
BatchPeriod = MSSqlServerSink.DefaultPeriod;
1919
EagerlyEmitFirstEvent = true;
20+
UseSqlBulkCopy = true;
2021
}
2122

2223
internal MSSqlServerSinkOptions(
@@ -77,5 +78,10 @@ internal MSSqlServerSinkOptions(
7778
/// A switch allowing the pass-through minimum level to be changed at runtime
7879
/// </summary>
7980
public LoggingLevelSwitch LevelSwitch { get; set; }
81+
82+
/// <summary>
83+
/// Flag to use <see cref="Microsoft.Data.SqlClient.SqlBulkCopy"/> instead of individual INSERT statements (default: true)
84+
/// </summary>
85+
public bool UseSqlBulkCopy { get; set; }
8086
}
8187
}

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/ISqlCommandWrapper.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Data;
3+
using System.Threading.Tasks;
34

45
namespace Serilog.Sinks.MSSqlServer.Platform.SqlClient
56
{
@@ -10,5 +11,6 @@ internal interface ISqlCommandWrapper : IDisposable
1011

1112
void AddParameter(string parameterName, object value);
1213
int ExecuteNonQuery();
14+
Task<int> ExecuteNonQueryAsync();
1315
}
1416
}

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/SqlCommandWrapper.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Data;
3+
using System.Threading.Tasks;
34
using Microsoft.Data.SqlClient;
45

56
namespace Serilog.Sinks.MSSqlServer.Platform.SqlClient
@@ -43,6 +44,9 @@ public void AddParameter(string parameterName, object value)
4344
public int ExecuteNonQuery() =>
4445
_sqlCommand.ExecuteNonQuery();
4546

47+
public Task<int> ExecuteNonQueryAsync() =>
48+
_sqlCommand.ExecuteNonQueryAsync();
49+
4650
protected virtual void Dispose(bool disposing)
4751
{
4852
if (!_disposedValue)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Data;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using Serilog.Debugging;
8+
using Serilog.Events;
9+
using Serilog.Sinks.MSSqlServer.Output;
10+
using static System.FormattableString;
11+
12+
namespace Serilog.Sinks.MSSqlServer.Platform
13+
{
14+
internal class SqlInsertBatchWriter : ISqlBulkBatchWriter
15+
{
16+
private readonly string _tableName;
17+
private readonly string _schemaName;
18+
private readonly ISqlConnectionFactory _sqlConnectionFactory;
19+
private readonly ILogEventDataGenerator _logEventDataGenerator;
20+
21+
public SqlInsertBatchWriter(
22+
string tableName,
23+
string schemaName,
24+
ISqlConnectionFactory sqlConnectionFactory,
25+
ILogEventDataGenerator logEventDataGenerator)
26+
{
27+
_tableName = tableName ?? throw new ArgumentNullException(nameof(tableName));
28+
_schemaName = schemaName ?? throw new ArgumentNullException(nameof(schemaName));
29+
_sqlConnectionFactory = sqlConnectionFactory ?? throw new ArgumentNullException(nameof(sqlConnectionFactory));
30+
_logEventDataGenerator = logEventDataGenerator ?? throw new ArgumentNullException(nameof(logEventDataGenerator));
31+
}
32+
33+
public async Task WriteBatch(IEnumerable<LogEvent> events, DataTable dataTable)
34+
{
35+
try
36+
{
37+
using (var cn = _sqlConnectionFactory.Create())
38+
{
39+
await cn.OpenAsync().ConfigureAwait(false);
40+
41+
foreach (var logEvent in events)
42+
{
43+
using (var command = cn.CreateCommand())
44+
{
45+
command.CommandType = CommandType.Text;
46+
47+
var fieldList = new StringBuilder(Invariant($"INSERT INTO [{_schemaName}].[{_tableName}] ("));
48+
var parameterList = new StringBuilder(") VALUES (");
49+
50+
var index = 0;
51+
foreach (var field in _logEventDataGenerator.GetColumnsAndValues(logEvent))
52+
{
53+
if (index != 0)
54+
{
55+
fieldList.Append(',');
56+
parameterList.Append(',');
57+
}
58+
59+
fieldList.Append(Invariant($"[{field.Key}]"));
60+
parameterList.Append("@P");
61+
parameterList.Append(index);
62+
63+
command.AddParameter(Invariant($"@P{index}"), field.Value);
64+
65+
index++;
66+
}
67+
68+
parameterList.Append(')');
69+
fieldList.Append(parameterList);
70+
71+
command.CommandText = fieldList.ToString();
72+
73+
await command.ExecuteNonQueryAsync().ConfigureAwait(false);
74+
}
75+
}
76+
}
77+
}
78+
catch (Exception ex)
79+
{
80+
SelfLog.WriteLine("Unable to write batch of {0} log events to the database due to following error: {1}",
81+
events.Count(), ex);
82+
throw;
83+
}
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)