Skip to content

Commit f83de79

Browse files
Nathan HartNathan Hart
authored andcommitted
Sql Connection factory
1 parent ce7b2ee commit f83de79

File tree

8 files changed

+284
-0
lines changed

8 files changed

+284
-0
lines changed

src/Serilog.Sinks.MSSqlServer/Configuration/Extensions/Hybrid/LoggerConfigurationMSSqlServerExtensions.cs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
using System;
16+
using Microsoft.Data.SqlClient;
1617
using Microsoft.Extensions.Configuration;
1718
using Serilog.Configuration;
1819
using Serilog.Debugging;
@@ -167,6 +168,85 @@ internal static LoggerConfiguration MSSqlServerInternal(
167168
return loggerConfiguration.Sink(periodicBatchingSink, restrictedToMinimumLevel, sinkOptions?.LevelSwitch);
168169
}
169170

171+
/// <summary>
172+
/// Adds a sink that writes log events to a table in a MSSqlServer database.
173+
/// Create a database and execute the table creation script found here
174+
/// https://gist.github.com/mivano/10429656
175+
/// or use the autoCreateSqlTable option.
176+
/// </summary>
177+
/// <param name="loggerConfiguration">The logger configuration.</param>
178+
/// <param name="sqlConnectionFactory">A function to initialize a connection to the database where to store the events.</param>
179+
/// <param name="initialCatalog">The initial catalog within the database (used if AutoCreateSqlDatabase is enabled).</param>
180+
/// <param name="sinkOptions">Supplies additional settings for the sink</param>
181+
/// <param name="sinkOptionsSection">A config section defining additional settings for the sink</param>
182+
/// <param name="appConfiguration">Additional application-level configuration. Required if connectionString is a name.</param>
183+
/// <param name="restrictedToMinimumLevel">The minimum level for events passed through the sink. Ignored when LevelSwitch in <paramref name="sinkOptions"/> is specified.</param>
184+
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
185+
/// <param name="columnOptions">An externally-modified group of column settings</param>
186+
/// <param name="columnOptionsSection">A config section defining various column settings</param>
187+
/// <param name="logEventFormatter">Supplies custom formatter for the LogEvent column, or null</param>
188+
/// <returns>Logger configuration, allowing configuration to continue.</returns>
189+
/// <exception cref="ArgumentNullException">A required parameter is null.</exception>
190+
public static LoggerConfiguration MSSqlServer(
191+
this LoggerSinkConfiguration loggerConfiguration,
192+
Func<SqlConnection> sqlConnectionFactory,
193+
string initialCatalog,
194+
MSSqlServerSinkOptions sinkOptions = null,
195+
IConfigurationSection sinkOptionsSection = null,
196+
IConfiguration appConfiguration = null,
197+
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
198+
IFormatProvider formatProvider = null,
199+
ColumnOptions columnOptions = null,
200+
IConfigurationSection columnOptionsSection = null,
201+
ITextFormatter logEventFormatter = null) =>
202+
loggerConfiguration.MSSqlServerInternal(
203+
sqlConnectionFactory: sqlConnectionFactory,
204+
initialCatalog: initialCatalog,
205+
sinkOptions: sinkOptions,
206+
sinkOptionsSection: sinkOptionsSection,
207+
appConfiguration: appConfiguration,
208+
restrictedToMinimumLevel: restrictedToMinimumLevel,
209+
formatProvider: formatProvider,
210+
columnOptions: columnOptions,
211+
columnOptionsSection: columnOptionsSection,
212+
logEventFormatter: logEventFormatter,
213+
applySystemConfiguration: new ApplySystemConfiguration(),
214+
applyMicrosoftExtensionsConfiguration: new ApplyMicrosoftExtensionsConfiguration(),
215+
sinkFactory: new MSSqlServerSinkFactory(),
216+
batchingSinkFactory: new PeriodicBatchingSinkFactory());
217+
218+
// Internal overload with parameters used by tests to override the config section and inject mocks
219+
internal static LoggerConfiguration MSSqlServerInternal(
220+
this LoggerSinkConfiguration loggerConfiguration,
221+
Func<SqlConnection> sqlConnectionFactory,
222+
string initialCatalog,
223+
MSSqlServerSinkOptions sinkOptions,
224+
IConfigurationSection sinkOptionsSection,
225+
IConfiguration appConfiguration,
226+
LogEventLevel restrictedToMinimumLevel,
227+
IFormatProvider formatProvider,
228+
ColumnOptions columnOptions,
229+
IConfigurationSection columnOptionsSection,
230+
ITextFormatter logEventFormatter,
231+
IApplySystemConfiguration applySystemConfiguration,
232+
IApplyMicrosoftExtensionsConfiguration applyMicrosoftExtensionsConfiguration,
233+
IMSSqlServerSinkFactory sinkFactory,
234+
IPeriodicBatchingSinkFactory batchingSinkFactory)
235+
{
236+
if (loggerConfiguration == null)
237+
throw new ArgumentNullException(nameof(loggerConfiguration));
238+
239+
ReadConfiguration(ref sinkOptions, sinkOptionsSection, appConfiguration,
240+
ref columnOptions, columnOptionsSection, applySystemConfiguration, applyMicrosoftExtensionsConfiguration);
241+
242+
var sink = sinkFactory.Create(sqlConnectionFactory, initialCatalog, sinkOptions, formatProvider, columnOptions, logEventFormatter);
243+
244+
var periodicBatchingSink = batchingSinkFactory.Create(sink, sinkOptions);
245+
246+
return loggerConfiguration.Sink(periodicBatchingSink, restrictedToMinimumLevel, sinkOptions?.LevelSwitch);
247+
}
248+
249+
170250
/// <summary>
171251
/// Adds a sink that writes log events to a table in a MSSqlServer database.
172252
///
@@ -313,6 +393,40 @@ private static void ReadConfiguration(
313393
connectionString = applyMicrosoftExtensionsConfiguration.GetConnectionString(connectionString, appConfiguration);
314394
}
315395

396+
if (columnOptionsSection != null)
397+
{
398+
columnOptions = applyMicrosoftExtensionsConfiguration.ConfigureColumnOptions(columnOptions, columnOptionsSection);
399+
}
400+
401+
if (sinkOptionsSection != null)
402+
{
403+
sinkOptions = applyMicrosoftExtensionsConfiguration.ConfigureSinkOptions(sinkOptions, sinkOptionsSection);
404+
}
405+
}
406+
407+
private static void ReadConfiguration(
408+
ref MSSqlServerSinkOptions sinkOptions,
409+
IConfigurationSection sinkOptionsSection,
410+
IConfiguration appConfiguration,
411+
ref ColumnOptions columnOptions,
412+
IConfigurationSection columnOptionsSection,
413+
IApplySystemConfiguration applySystemConfiguration,
414+
IApplyMicrosoftExtensionsConfiguration applyMicrosoftExtensionsConfiguration)
415+
{
416+
sinkOptions = sinkOptions ?? new MSSqlServerSinkOptions();
417+
columnOptions = columnOptions ?? new ColumnOptions();
418+
419+
var serviceConfigSection = applySystemConfiguration.GetSinkConfigurationSection(AppConfigSectionName);
420+
if (serviceConfigSection != null)
421+
{
422+
columnOptions = applySystemConfiguration.ConfigureColumnOptions(serviceConfigSection, columnOptions);
423+
sinkOptions = applySystemConfiguration.ConfigureSinkOptions(serviceConfigSection, sinkOptions);
424+
425+
if (appConfiguration != null || columnOptionsSection != null || sinkOptionsSection != null)
426+
SelfLog.WriteLine("Warning: Both System.Configuration (app.config or web.config) and Microsoft.Extensions.Configuration are being applied to the MSSQLServer sink.");
427+
}
428+
429+
316430
if (columnOptionsSection != null)
317431
{
318432
columnOptions = applyMicrosoftExtensionsConfiguration.ConfigureColumnOptions(columnOptions, columnOptionsSection);

src/Serilog.Sinks.MSSqlServer/Configuration/Extensions/Microsoft.Extensions.Configuration/LoggerConfigurationMSSqlServerExtensions.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
using System;
16+
using Microsoft.Data.SqlClient;
1617
using Microsoft.Extensions.Configuration;
1718
using Serilog.Configuration;
1819
using Serilog.Events;
@@ -131,6 +132,54 @@ public static LoggerConfiguration MSSqlServer(
131132
return loggerConfiguration.Sink(periodicBatchingSink, restrictedToMinimumLevel, sinkOptions?.LevelSwitch);
132133
}
133134

135+
136+
/// <summary>
137+
/// Adds a sink that writes log events to a table in a MSSqlServer database.
138+
/// Create a database and execute the table creation script found here
139+
/// https://gist.github.com/mivano/10429656
140+
/// or use the autoCreateSqlTable option.
141+
/// </summary>
142+
/// <param name="loggerConfiguration">The logger configuration.</param>
143+
/// <param name="sqlConnectionFactory">A function to initialize a connection to the database where to store the events.</param>
144+
/// <param name="initialCatalog">The initial catalog within the database (used if AutoCreateSqlDatabase is enabled).</param>
145+
/// <param name="sinkOptions">Supplies additional settings for the sink</param>
146+
/// <param name="sinkOptionsSection">A config section defining additional settings for the sink</param>
147+
/// <param name="appConfiguration">Additional application-level configuration. Required if connectionString is a name.</param>
148+
/// <param name="restrictedToMinimumLevel">The minimum level for events passed through the sink. Ignored when LevelSwitch in <paramref name="sinkOptions"/> is specified.</param>
149+
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
150+
/// <param name="columnOptions">An externally-modified group of column settings</param>
151+
/// <param name="columnOptionsSection">A config section defining various column settings</param>
152+
/// <param name="logEventFormatter">Supplies custom formatter for the LogEvent column, or null</param>
153+
/// <returns>Logger configuration, allowing configuration to continue.</returns>
154+
/// <exception cref="ArgumentNullException">A required parameter is null.</exception>
155+
public static LoggerConfiguration MSSqlServer(
156+
this LoggerSinkConfiguration loggerConfiguration,
157+
Func<SqlConnection> sqlConnectionFactory,
158+
string initialCatalog,
159+
MSSqlServerSinkOptions sinkOptions = null,
160+
IConfigurationSection sinkOptionsSection = null,
161+
IConfiguration appConfiguration = null,
162+
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
163+
IFormatProvider formatProvider = null,
164+
ColumnOptions columnOptions = null,
165+
IConfigurationSection columnOptionsSection = null,
166+
ITextFormatter logEventFormatter = null)
167+
{
168+
if (loggerConfiguration == null)
169+
throw new ArgumentNullException(nameof(loggerConfiguration));
170+
171+
ReadConfiguration(ref sinkOptions, appConfiguration, ref columnOptions,
172+
columnOptionsSection, sinkOptionsSection);
173+
174+
IMSSqlServerSinkFactory sinkFactory = new MSSqlServerSinkFactory();
175+
var sink = sinkFactory.Create(sqlConnectionFactory, initialCatalog, sinkOptions, formatProvider, columnOptions, logEventFormatter);
176+
177+
IPeriodicBatchingSinkFactory periodicBatchingSinkFactory = new PeriodicBatchingSinkFactory();
178+
var periodicBatchingSink = periodicBatchingSinkFactory.Create(sink, sinkOptions);
179+
180+
return loggerConfiguration.Sink(periodicBatchingSink, restrictedToMinimumLevel, sinkOptions?.LevelSwitch);
181+
}
182+
134183
/// <summary>
135184
/// Adds a sink that writes log events to a table in a MSSqlServer database.
136185
///
@@ -236,5 +285,20 @@ private static void ReadConfiguration(
236285
columnOptions = microsoftExtensionsConfiguration.ConfigureColumnOptions(columnOptions, columnOptionsSection);
237286
sinkOptions = microsoftExtensionsConfiguration.ConfigureSinkOptions(sinkOptions, sinkOptionsSection);
238287
}
288+
289+
private static void ReadConfiguration(
290+
ref MSSqlServerSinkOptions sinkOptions,
291+
IConfiguration appConfiguration,
292+
ref ColumnOptions columnOptions,
293+
IConfigurationSection columnOptionsSection,
294+
IConfigurationSection sinkOptionsSection)
295+
{
296+
sinkOptions = sinkOptions ?? new MSSqlServerSinkOptions();
297+
columnOptions = columnOptions ?? new ColumnOptions();
298+
299+
IApplyMicrosoftExtensionsConfiguration microsoftExtensionsConfiguration = new ApplyMicrosoftExtensionsConfiguration();
300+
columnOptions = microsoftExtensionsConfiguration.ConfigureColumnOptions(columnOptions, columnOptionsSection);
301+
sinkOptions = microsoftExtensionsConfiguration.ConfigureSinkOptions(sinkOptions, sinkOptionsSection);
302+
}
239303
}
240304
}

src/Serilog.Sinks.MSSqlServer/Configuration/Factories/IMSSqlServerSinkFactory.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using Serilog.Formatting;
33
using Serilog.Core;
4+
using Microsoft.Data.SqlClient;
45

56
namespace Serilog.Sinks.MSSqlServer.Configuration.Factories
67
{
@@ -12,5 +13,13 @@ IBatchedLogEventSink Create(
1213
IFormatProvider formatProvider,
1314
ColumnOptions columnOptions,
1415
ITextFormatter logEventFormatter);
16+
17+
IBatchedLogEventSink Create(
18+
Func<SqlConnection> sqlConnectionFactory,
19+
string initialCatalog,
20+
MSSqlServerSinkOptions sinkOptions,
21+
IFormatProvider formatProvider,
22+
ColumnOptions columnOptions,
23+
ITextFormatter logEventFormatter);
1524
}
1625
}

src/Serilog.Sinks.MSSqlServer/Configuration/Factories/MSSqlServerSinkFactory.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using Serilog.Formatting;
33
using Serilog.Core;
4+
using Microsoft.Data.SqlClient;
45

56
namespace Serilog.Sinks.MSSqlServer.Configuration.Factories
67
{
@@ -18,5 +19,20 @@ public IBatchedLogEventSink Create(
1819
formatProvider,
1920
columnOptions,
2021
logEventFormatter);
22+
23+
public IBatchedLogEventSink Create(
24+
Func<SqlConnection> sqlConnectionFactory,
25+
string initialCatalog,
26+
MSSqlServerSinkOptions sinkOptions,
27+
IFormatProvider formatProvider,
28+
ColumnOptions columnOptions,
29+
ITextFormatter logEventFormatter) =>
30+
new MSSqlServerSink(
31+
sqlConnectionFactory,
32+
initialCatalog,
33+
sinkOptions,
34+
formatProvider,
35+
columnOptions,
36+
logEventFormatter);
2137
}
2238
}

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using Microsoft.Data.SqlClient;
23
using Serilog.Formatting;
34
using Serilog.Sinks.MSSqlServer.Output;
45
using Serilog.Sinks.MSSqlServer.Platform;
@@ -60,6 +61,49 @@ internal static SinkDependencies Create(
6061
sqlConnectionFactory, sqlCommandFactory, logEventDataGenerator)
6162
};
6263

64+
return sinkDependencies;
65+
}
66+
67+
internal static SinkDependencies Create(
68+
Func<SqlConnection> sqlConnectionFactory,
69+
string initialCatalog,
70+
MSSqlServerSinkOptions sinkOptions,
71+
IFormatProvider formatProvider,
72+
ColumnOptions columnOptions,
73+
ITextFormatter logEventFormatter)
74+
{
75+
columnOptions = columnOptions ?? new ColumnOptions();
76+
columnOptions.FinalizeConfigurationForSinkConstructor();
77+
78+
var connectionFactory = new SqlConnectionFactory(sqlConnectionFactory);
79+
var sqlCommandFactory = new SqlCommandFactory();
80+
var dataTableCreator = new DataTableCreator(sinkOptions.TableName, columnOptions);
81+
var sqlCreateTableWriter = new SqlCreateTableWriter(sinkOptions.SchemaName,
82+
sinkOptions.TableName, columnOptions, dataTableCreator);
83+
84+
var logEventDataGenerator =
85+
new LogEventDataGenerator(columnOptions,
86+
new StandardColumnDataGenerator(columnOptions, formatProvider,
87+
new XmlPropertyFormatter(),
88+
logEventFormatter),
89+
new AdditionalColumnDataGenerator(
90+
new ColumnSimplePropertyValueResolver(),
91+
new ColumnHierarchicalPropertyValueResolver()));
92+
var sqlCreateDatabaseWriter = new SqlCreateDatabaseWriter(initialCatalog);
93+
var sinkDependencies = new SinkDependencies
94+
{
95+
SqlDatabaseCreator = new SqlDatabaseCreator(
96+
sqlCreateDatabaseWriter, connectionFactory, sqlCommandFactory),
97+
SqlTableCreator = new SqlTableCreator(
98+
sqlCreateTableWriter, connectionFactory, sqlCommandFactory),
99+
SqlBulkBatchWriter = new SqlBulkBatchWriter(
100+
sinkOptions.TableName, sinkOptions.SchemaName, columnOptions.DisableTriggers,
101+
dataTableCreator, connectionFactory, logEventDataGenerator),
102+
SqlLogEventWriter = new SqlInsertStatementWriter(
103+
sinkOptions.TableName, sinkOptions.SchemaName,
104+
connectionFactory, sqlCommandFactory, logEventDataGenerator)
105+
};
106+
63107
return sinkDependencies;
64108
}
65109
}

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using Serilog.Sinks.MSSqlServer.Dependencies;
2121
using Serilog.Sinks.MSSqlServer.Platform;
2222
using Serilog.Core;
23+
using Microsoft.Data.SqlClient;
2324

2425
namespace Serilog.Sinks.MSSqlServer
2526
{
@@ -100,6 +101,26 @@ public MSSqlServerSink(
100101
{
101102
}
102103

104+
/// <summary>
105+
/// Construct a sink posting to the specified database.
106+
/// </summary>
107+
/// <param name="sqlConnectionFactory">Factory to initialize connection to the database.</param>
108+
/// <param name="initialCatalog">The initial catalog within the database (used if AutoCreateSqlDatabase is enabled).</param>
109+
/// <param name="sinkOptions">Supplies additional options for the sink</param>
110+
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
111+
/// <param name="columnOptions">Options that pertain to columns</param>
112+
/// <param name="logEventFormatter">Supplies custom formatter for the LogEvent column, or null</param>
113+
public MSSqlServerSink(
114+
Func<SqlConnection> sqlConnectionFactory,
115+
string initialCatalog,
116+
MSSqlServerSinkOptions sinkOptions,
117+
IFormatProvider formatProvider = null,
118+
ColumnOptions columnOptions = null,
119+
ITextFormatter logEventFormatter = null)
120+
: this(sinkOptions, SinkDependenciesFactory.Create(sqlConnectionFactory, initialCatalog, sinkOptions, formatProvider, columnOptions, logEventFormatter))
121+
{
122+
}
123+
103124
// Internal constructor with injectable dependencies for better testability
104125
internal MSSqlServerSink(
105126
MSSqlServerSinkOptions sinkOptions,

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ public SqlConnectionWrapper(string connectionString)
1414
_sqlConnection = new SqlConnection(connectionString);
1515
}
1616

17+
public SqlConnectionWrapper(Func<SqlConnection> connectionFactory)
18+
{
19+
_sqlConnection = connectionFactory();
20+
}
21+
1722
public SqlConnection SqlConnection => _sqlConnection;
1823

1924
public void Open()

0 commit comments

Comments
 (0)