diff --git a/src/Serilog.Sinks.MSSqlServer/Configuration/Extensions/Hybrid/LoggerConfigurationMSSqlServerExtensions.cs b/src/Serilog.Sinks.MSSqlServer/Configuration/Extensions/Hybrid/LoggerConfigurationMSSqlServerExtensions.cs
index eefb88af..8df8ce9c 100644
--- a/src/Serilog.Sinks.MSSqlServer/Configuration/Extensions/Hybrid/LoggerConfigurationMSSqlServerExtensions.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Configuration/Extensions/Hybrid/LoggerConfigurationMSSqlServerExtensions.cs
@@ -13,6 +13,7 @@
// limitations under the License.
using System;
+using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Configuration;
using Serilog.Configuration;
using Serilog.Debugging;
@@ -167,6 +168,85 @@ internal static LoggerConfiguration MSSqlServerInternal(
return loggerConfiguration.Sink(periodicBatchingSink, restrictedToMinimumLevel, sinkOptions?.LevelSwitch);
}
+ ///
+ /// Adds a sink that writes log events to a table in a MSSqlServer database.
+ /// Create a database and execute the table creation script found here
+ /// https://gist.github.com/mivano/10429656
+ /// or use the autoCreateSqlTable option.
+ ///
+ /// The logger configuration.
+ /// A function to initialize a connection to the database where to store the events.
+ /// The initial catalog within the database (used if AutoCreateSqlDatabase is enabled).
+ /// Supplies additional settings for the sink
+ /// A config section defining additional settings for the sink
+ /// Additional application-level configuration. Required if connectionString is a name.
+ /// The minimum level for events passed through the sink. Ignored when LevelSwitch in is specified.
+ /// Supplies culture-specific formatting information, or null.
+ /// An externally-modified group of column settings
+ /// A config section defining various column settings
+ /// Supplies custom formatter for the LogEvent column, or null
+ /// Logger configuration, allowing configuration to continue.
+ /// A required parameter is null.
+ public static LoggerConfiguration MSSqlServer(
+ this LoggerSinkConfiguration loggerConfiguration,
+ Func sqlConnectionFactory,
+ string initialCatalog,
+ MSSqlServerSinkOptions sinkOptions = null,
+ IConfigurationSection sinkOptionsSection = null,
+ IConfiguration appConfiguration = null,
+ LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
+ IFormatProvider formatProvider = null,
+ ColumnOptions columnOptions = null,
+ IConfigurationSection columnOptionsSection = null,
+ ITextFormatter logEventFormatter = null) =>
+ loggerConfiguration.MSSqlServerInternal(
+ sqlConnectionFactory: sqlConnectionFactory,
+ initialCatalog: initialCatalog,
+ sinkOptions: sinkOptions,
+ sinkOptionsSection: sinkOptionsSection,
+ appConfiguration: appConfiguration,
+ restrictedToMinimumLevel: restrictedToMinimumLevel,
+ formatProvider: formatProvider,
+ columnOptions: columnOptions,
+ columnOptionsSection: columnOptionsSection,
+ logEventFormatter: logEventFormatter,
+ applySystemConfiguration: new ApplySystemConfiguration(),
+ applyMicrosoftExtensionsConfiguration: new ApplyMicrosoftExtensionsConfiguration(),
+ sinkFactory: new MSSqlServerSinkFactory(),
+ batchingSinkFactory: new PeriodicBatchingSinkFactory());
+
+ // Internal overload with parameters used by tests to override the config section and inject mocks
+ internal static LoggerConfiguration MSSqlServerInternal(
+ this LoggerSinkConfiguration loggerConfiguration,
+ Func sqlConnectionFactory,
+ string initialCatalog,
+ MSSqlServerSinkOptions sinkOptions,
+ IConfigurationSection sinkOptionsSection,
+ IConfiguration appConfiguration,
+ LogEventLevel restrictedToMinimumLevel,
+ IFormatProvider formatProvider,
+ ColumnOptions columnOptions,
+ IConfigurationSection columnOptionsSection,
+ ITextFormatter logEventFormatter,
+ IApplySystemConfiguration applySystemConfiguration,
+ IApplyMicrosoftExtensionsConfiguration applyMicrosoftExtensionsConfiguration,
+ IMSSqlServerSinkFactory sinkFactory,
+ IPeriodicBatchingSinkFactory batchingSinkFactory)
+ {
+ if (loggerConfiguration == null)
+ throw new ArgumentNullException(nameof(loggerConfiguration));
+
+ ReadConfiguration(ref sinkOptions, sinkOptionsSection, appConfiguration,
+ ref columnOptions, columnOptionsSection, applySystemConfiguration, applyMicrosoftExtensionsConfiguration);
+
+ var sink = sinkFactory.Create(sqlConnectionFactory, initialCatalog, sinkOptions, formatProvider, columnOptions, logEventFormatter);
+
+ var periodicBatchingSink = batchingSinkFactory.Create(sink, sinkOptions);
+
+ return loggerConfiguration.Sink(periodicBatchingSink, restrictedToMinimumLevel, sinkOptions?.LevelSwitch);
+ }
+
+
///
/// Adds a sink that writes log events to a table in a MSSqlServer database.
///
@@ -313,6 +393,40 @@ private static void ReadConfiguration(
connectionString = applyMicrosoftExtensionsConfiguration.GetConnectionString(connectionString, appConfiguration);
}
+ if (columnOptionsSection != null)
+ {
+ columnOptions = applyMicrosoftExtensionsConfiguration.ConfigureColumnOptions(columnOptions, columnOptionsSection);
+ }
+
+ if (sinkOptionsSection != null)
+ {
+ sinkOptions = applyMicrosoftExtensionsConfiguration.ConfigureSinkOptions(sinkOptions, sinkOptionsSection);
+ }
+ }
+
+ private static void ReadConfiguration(
+ ref MSSqlServerSinkOptions sinkOptions,
+ IConfigurationSection sinkOptionsSection,
+ IConfiguration appConfiguration,
+ ref ColumnOptions columnOptions,
+ IConfigurationSection columnOptionsSection,
+ IApplySystemConfiguration applySystemConfiguration,
+ IApplyMicrosoftExtensionsConfiguration applyMicrosoftExtensionsConfiguration)
+ {
+ sinkOptions = sinkOptions ?? new MSSqlServerSinkOptions();
+ columnOptions = columnOptions ?? new ColumnOptions();
+
+ var serviceConfigSection = applySystemConfiguration.GetSinkConfigurationSection(AppConfigSectionName);
+ if (serviceConfigSection != null)
+ {
+ columnOptions = applySystemConfiguration.ConfigureColumnOptions(serviceConfigSection, columnOptions);
+ sinkOptions = applySystemConfiguration.ConfigureSinkOptions(serviceConfigSection, sinkOptions);
+
+ if (appConfiguration != null || columnOptionsSection != null || sinkOptionsSection != null)
+ SelfLog.WriteLine("Warning: Both System.Configuration (app.config or web.config) and Microsoft.Extensions.Configuration are being applied to the MSSQLServer sink.");
+ }
+
+
if (columnOptionsSection != null)
{
columnOptions = applyMicrosoftExtensionsConfiguration.ConfigureColumnOptions(columnOptions, columnOptionsSection);
diff --git a/src/Serilog.Sinks.MSSqlServer/Configuration/Extensions/Microsoft.Extensions.Configuration/LoggerConfigurationMSSqlServerExtensions.cs b/src/Serilog.Sinks.MSSqlServer/Configuration/Extensions/Microsoft.Extensions.Configuration/LoggerConfigurationMSSqlServerExtensions.cs
index 0084b132..85b3b836 100644
--- a/src/Serilog.Sinks.MSSqlServer/Configuration/Extensions/Microsoft.Extensions.Configuration/LoggerConfigurationMSSqlServerExtensions.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Configuration/Extensions/Microsoft.Extensions.Configuration/LoggerConfigurationMSSqlServerExtensions.cs
@@ -13,6 +13,7 @@
// limitations under the License.
using System;
+using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Configuration;
using Serilog.Configuration;
using Serilog.Events;
@@ -131,6 +132,54 @@ public static LoggerConfiguration MSSqlServer(
return loggerConfiguration.Sink(periodicBatchingSink, restrictedToMinimumLevel, sinkOptions?.LevelSwitch);
}
+
+ ///
+ /// Adds a sink that writes log events to a table in a MSSqlServer database.
+ /// Create a database and execute the table creation script found here
+ /// https://gist.github.com/mivano/10429656
+ /// or use the autoCreateSqlTable option.
+ ///
+ /// The logger configuration.
+ /// A function to initialize a connection to the database where to store the events.
+ /// The initial catalog within the database (used if AutoCreateSqlDatabase is enabled).
+ /// Supplies additional settings for the sink
+ /// A config section defining additional settings for the sink
+ /// Additional application-level configuration. Required if connectionString is a name.
+ /// The minimum level for events passed through the sink. Ignored when LevelSwitch in is specified.
+ /// Supplies culture-specific formatting information, or null.
+ /// An externally-modified group of column settings
+ /// A config section defining various column settings
+ /// Supplies custom formatter for the LogEvent column, or null
+ /// Logger configuration, allowing configuration to continue.
+ /// A required parameter is null.
+ public static LoggerConfiguration MSSqlServer(
+ this LoggerSinkConfiguration loggerConfiguration,
+ Func sqlConnectionFactory,
+ string initialCatalog,
+ MSSqlServerSinkOptions sinkOptions = null,
+ IConfigurationSection sinkOptionsSection = null,
+ IConfiguration appConfiguration = null,
+ LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
+ IFormatProvider formatProvider = null,
+ ColumnOptions columnOptions = null,
+ IConfigurationSection columnOptionsSection = null,
+ ITextFormatter logEventFormatter = null)
+ {
+ if (loggerConfiguration == null)
+ throw new ArgumentNullException(nameof(loggerConfiguration));
+
+ ReadConfiguration(ref sinkOptions, appConfiguration, ref columnOptions,
+ columnOptionsSection, sinkOptionsSection);
+
+ IMSSqlServerSinkFactory sinkFactory = new MSSqlServerSinkFactory();
+ var sink = sinkFactory.Create(sqlConnectionFactory, initialCatalog, sinkOptions, formatProvider, columnOptions, logEventFormatter);
+
+ IPeriodicBatchingSinkFactory periodicBatchingSinkFactory = new PeriodicBatchingSinkFactory();
+ var periodicBatchingSink = periodicBatchingSinkFactory.Create(sink, sinkOptions);
+
+ return loggerConfiguration.Sink(periodicBatchingSink, restrictedToMinimumLevel, sinkOptions?.LevelSwitch);
+ }
+
///
/// Adds a sink that writes log events to a table in a MSSqlServer database.
///
@@ -236,5 +285,20 @@ private static void ReadConfiguration(
columnOptions = microsoftExtensionsConfiguration.ConfigureColumnOptions(columnOptions, columnOptionsSection);
sinkOptions = microsoftExtensionsConfiguration.ConfigureSinkOptions(sinkOptions, sinkOptionsSection);
}
+
+ private static void ReadConfiguration(
+ ref MSSqlServerSinkOptions sinkOptions,
+ IConfiguration appConfiguration,
+ ref ColumnOptions columnOptions,
+ IConfigurationSection columnOptionsSection,
+ IConfigurationSection sinkOptionsSection)
+ {
+ sinkOptions = sinkOptions ?? new MSSqlServerSinkOptions();
+ columnOptions = columnOptions ?? new ColumnOptions();
+
+ IApplyMicrosoftExtensionsConfiguration microsoftExtensionsConfiguration = new ApplyMicrosoftExtensionsConfiguration();
+ columnOptions = microsoftExtensionsConfiguration.ConfigureColumnOptions(columnOptions, columnOptionsSection);
+ sinkOptions = microsoftExtensionsConfiguration.ConfigureSinkOptions(sinkOptions, sinkOptionsSection);
+ }
}
}
diff --git a/src/Serilog.Sinks.MSSqlServer/Configuration/Factories/IMSSqlServerSinkFactory.cs b/src/Serilog.Sinks.MSSqlServer/Configuration/Factories/IMSSqlServerSinkFactory.cs
index 4ff2b293..0babb81a 100644
--- a/src/Serilog.Sinks.MSSqlServer/Configuration/Factories/IMSSqlServerSinkFactory.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Configuration/Factories/IMSSqlServerSinkFactory.cs
@@ -1,6 +1,7 @@
using System;
using Serilog.Formatting;
using Serilog.Core;
+using Microsoft.Data.SqlClient;
namespace Serilog.Sinks.MSSqlServer.Configuration.Factories
{
@@ -12,5 +13,13 @@ IBatchedLogEventSink Create(
IFormatProvider formatProvider,
ColumnOptions columnOptions,
ITextFormatter logEventFormatter);
+
+ IBatchedLogEventSink Create(
+ Func sqlConnectionFactory,
+ string initialCatalog,
+ MSSqlServerSinkOptions sinkOptions,
+ IFormatProvider formatProvider,
+ ColumnOptions columnOptions,
+ ITextFormatter logEventFormatter);
}
}
diff --git a/src/Serilog.Sinks.MSSqlServer/Configuration/Factories/MSSqlServerSinkFactory.cs b/src/Serilog.Sinks.MSSqlServer/Configuration/Factories/MSSqlServerSinkFactory.cs
index 4ba6d714..a78e5ebc 100644
--- a/src/Serilog.Sinks.MSSqlServer/Configuration/Factories/MSSqlServerSinkFactory.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Configuration/Factories/MSSqlServerSinkFactory.cs
@@ -1,6 +1,7 @@
using System;
using Serilog.Formatting;
using Serilog.Core;
+using Microsoft.Data.SqlClient;
namespace Serilog.Sinks.MSSqlServer.Configuration.Factories
{
@@ -18,5 +19,20 @@ public IBatchedLogEventSink Create(
formatProvider,
columnOptions,
logEventFormatter);
+
+ public IBatchedLogEventSink Create(
+ Func sqlConnectionFactory,
+ string initialCatalog,
+ MSSqlServerSinkOptions sinkOptions,
+ IFormatProvider formatProvider,
+ ColumnOptions columnOptions,
+ ITextFormatter logEventFormatter) =>
+ new MSSqlServerSink(
+ sqlConnectionFactory,
+ initialCatalog,
+ sinkOptions,
+ formatProvider,
+ columnOptions,
+ logEventFormatter);
}
}
diff --git a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactory.cs b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactory.cs
index f9a5cee7..878da6c4 100644
--- a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactory.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactory.cs
@@ -1,4 +1,5 @@
using System;
+using Microsoft.Data.SqlClient;
using Serilog.Formatting;
using Serilog.Sinks.MSSqlServer.Output;
using Serilog.Sinks.MSSqlServer.Platform;
@@ -60,6 +61,49 @@ internal static SinkDependencies Create(
sqlConnectionFactory, sqlCommandFactory, logEventDataGenerator)
};
+ return sinkDependencies;
+ }
+
+ internal static SinkDependencies Create(
+ Func sqlConnectionFactory,
+ string initialCatalog,
+ MSSqlServerSinkOptions sinkOptions,
+ IFormatProvider formatProvider,
+ ColumnOptions columnOptions,
+ ITextFormatter logEventFormatter)
+ {
+ columnOptions = columnOptions ?? new ColumnOptions();
+ columnOptions.FinalizeConfigurationForSinkConstructor();
+
+ var connectionFactory = new SqlConnectionFactory(sqlConnectionFactory);
+ var sqlCommandFactory = new SqlCommandFactory();
+ var dataTableCreator = new DataTableCreator(sinkOptions.TableName, columnOptions);
+ var sqlCreateTableWriter = new SqlCreateTableWriter(sinkOptions.SchemaName,
+ sinkOptions.TableName, columnOptions, dataTableCreator);
+
+ var logEventDataGenerator =
+ new LogEventDataGenerator(columnOptions,
+ new StandardColumnDataGenerator(columnOptions, formatProvider,
+ new XmlPropertyFormatter(),
+ logEventFormatter),
+ new AdditionalColumnDataGenerator(
+ new ColumnSimplePropertyValueResolver(),
+ new ColumnHierarchicalPropertyValueResolver()));
+ var sqlCreateDatabaseWriter = new SqlCreateDatabaseWriter(initialCatalog);
+ var sinkDependencies = new SinkDependencies
+ {
+ SqlDatabaseCreator = new SqlDatabaseCreator(
+ sqlCreateDatabaseWriter, connectionFactory, sqlCommandFactory),
+ SqlTableCreator = new SqlTableCreator(
+ sqlCreateTableWriter, connectionFactory, sqlCommandFactory),
+ SqlBulkBatchWriter = new SqlBulkBatchWriter(
+ sinkOptions.TableName, sinkOptions.SchemaName, columnOptions.DisableTriggers,
+ dataTableCreator, connectionFactory, logEventDataGenerator),
+ SqlLogEventWriter = new SqlInsertStatementWriter(
+ sinkOptions.TableName, sinkOptions.SchemaName,
+ connectionFactory, sqlCommandFactory, logEventDataGenerator)
+ };
+
return sinkDependencies;
}
}
diff --git a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/MSSqlServerSink.cs b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/MSSqlServerSink.cs
index d81f674a..f1378a82 100644
--- a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/MSSqlServerSink.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/MSSqlServerSink.cs
@@ -20,6 +20,7 @@
using Serilog.Sinks.MSSqlServer.Dependencies;
using Serilog.Sinks.MSSqlServer.Platform;
using Serilog.Core;
+using Microsoft.Data.SqlClient;
namespace Serilog.Sinks.MSSqlServer
{
@@ -100,6 +101,26 @@ public MSSqlServerSink(
{
}
+ ///
+ /// Construct a sink posting to the specified database.
+ ///
+ /// Factory to initialize connection to the database.
+ /// The initial catalog within the database (used if AutoCreateSqlDatabase is enabled).
+ /// Supplies additional options for the sink
+ /// Supplies culture-specific formatting information, or null.
+ /// Options that pertain to columns
+ /// Supplies custom formatter for the LogEvent column, or null
+ public MSSqlServerSink(
+ Func sqlConnectionFactory,
+ string initialCatalog,
+ MSSqlServerSinkOptions sinkOptions,
+ IFormatProvider formatProvider = null,
+ ColumnOptions columnOptions = null,
+ ITextFormatter logEventFormatter = null)
+ : this(sinkOptions, SinkDependenciesFactory.Create(sqlConnectionFactory, initialCatalog, sinkOptions, formatProvider, columnOptions, logEventFormatter))
+ {
+ }
+
// Internal constructor with injectable dependencies for better testability
internal MSSqlServerSink(
MSSqlServerSinkOptions sinkOptions,
diff --git a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/SqlConnectionWrapper.cs b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/SqlConnectionWrapper.cs
index 638ae47d..4a6ed618 100644
--- a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/SqlConnectionWrapper.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/SqlConnectionWrapper.cs
@@ -14,6 +14,11 @@ public SqlConnectionWrapper(string connectionString)
_sqlConnection = new SqlConnection(connectionString);
}
+ public SqlConnectionWrapper(Func connectionFactory)
+ {
+ _sqlConnection = connectionFactory();
+ }
+
public SqlConnection SqlConnection => _sqlConnection;
public void Open()
diff --git a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlConnectionFactory.cs b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlConnectionFactory.cs
index b18f7878..5c7f6e0a 100644
--- a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlConnectionFactory.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlConnectionFactory.cs
@@ -1,4 +1,5 @@
using System;
+using Microsoft.Data.SqlClient;
using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
namespace Serilog.Sinks.MSSqlServer.Platform
@@ -7,6 +8,7 @@ internal class SqlConnectionFactory : ISqlConnectionFactory
{
private readonly string _connectionString;
private readonly ISqlConnectionStringBuilderWrapper _sqlConnectionStringBuilderWrapper;
+ private readonly Func _sqlConnectionFactory;
public SqlConnectionFactory(ISqlConnectionStringBuilderWrapper sqlConnectionStringBuilderWrapper)
{
@@ -16,8 +18,17 @@ public SqlConnectionFactory(ISqlConnectionStringBuilderWrapper sqlConnectionStri
_connectionString = _sqlConnectionStringBuilderWrapper.ConnectionString;
}
+ public SqlConnectionFactory(Func connectionFactory)
+ {
+ _sqlConnectionFactory = connectionFactory;
+ }
+
public ISqlConnectionWrapper Create()
{
+ if(_sqlConnectionFactory != null)
+ {
+ return new SqlConnectionWrapper(_sqlConnectionFactory);
+ }
return new SqlConnectionWrapper(_connectionString);
}
}
diff --git a/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlConnectionFactoryTests.cs b/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlConnectionFactoryTests.cs
index f14674f1..cb59ec9c 100644
--- a/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlConnectionFactoryTests.cs
+++ b/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlConnectionFactoryTests.cs
@@ -21,7 +21,7 @@ public SqlConnectionFactoryTests()
[Fact]
public void IntializeThrowsIfSqlConnectionStringBuilderWrapperIsNull()
{
- Assert.Throws(() => new SqlConnectionFactory(null));
+ Assert.Throws(() => new SqlConnectionFactory((ISqlConnectionStringBuilderWrapper)null));
}
[Fact]