diff --git a/README.md b/README.md index 7a0b7deb..686abcc4 100644 --- a/README.md +++ b/README.md @@ -249,6 +249,7 @@ Basic settings of the sink are configured using the properties in a `MSSqlServer * `EagerlyEmitFirstEvent` * `LevelSwitch` * `UseSqlBulkCopy` +* `ConnectionConfiguration` ### TableName @@ -299,6 +300,10 @@ A switch allowing the pass-through minimum level to be changed at runtime. If th A flag to use `SqlBulkCopy` instead of individual INSERT statements when writing log events. The default is `true`. This setting is not used by the audit sink as it always uses INSERT statements to write events. +### ConnectionConfiguration + +An optional action to customize the underlying SqlConnection object. Can be used to set properties such as `AccessTokenCallback`. + ## ColumnOptions Object Features of the log table are defined by changing properties on a `ColumnOptions` object: diff --git a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactory.cs b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactory.cs index f9a5cee7..ae84c4fd 100644 --- a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactory.cs +++ b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactory.cs @@ -22,7 +22,7 @@ internal static SinkDependencies Create( // unless sink option EnlistInTransaction is set to true. var sqlConnectionStringBuilderWrapper = new SqlConnectionStringBuilderWrapper( connectionString, sinkOptions.EnlistInTransaction); - var sqlConnectionFactory = new SqlConnectionFactory(sqlConnectionStringBuilderWrapper); + var sqlConnectionFactory = new SqlConnectionFactory(sqlConnectionStringBuilderWrapper, sinkOptions.ConnectionConfiguration); var sqlCommandFactory = new SqlCommandFactory(); var dataTableCreator = new DataTableCreator(sinkOptions.TableName, columnOptions); var sqlCreateTableWriter = new SqlCreateTableWriter(sinkOptions.SchemaName, @@ -34,7 +34,7 @@ internal static SinkDependencies Create( InitialCatalog = "" }; var sqlConnectionFactoryNoDb = - new SqlConnectionFactory(sqlConnectionStringBuilderWrapperNoDb); + new SqlConnectionFactory(sqlConnectionStringBuilderWrapperNoDb, sinkOptions.ConnectionConfiguration); var sqlCreateDatabaseWriter = new SqlCreateDatabaseWriter(sqlConnectionStringBuilderWrapper.InitialCatalog); var logEventDataGenerator = diff --git a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/MSSqlServerSinkOptions.cs b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/MSSqlServerSinkOptions.cs index c53dba43..34c16445 100644 --- a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/MSSqlServerSinkOptions.cs +++ b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/MSSqlServerSinkOptions.cs @@ -1,4 +1,5 @@ using System; +using Microsoft.Data.SqlClient; using Serilog.Core; namespace Serilog.Sinks.MSSqlServer @@ -83,5 +84,10 @@ internal MSSqlServerSinkOptions( /// Flag to use instead of individual INSERT statements (default: true) /// public bool UseSqlBulkCopy { get; set; } + + /// + /// Supplies an extension point for customizing the underyling SqlConnection + /// + public Action ConnectionConfiguration { get; set; } } } 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..5b398f8f 100644 --- a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/SqlConnectionWrapper.cs +++ b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/SqlConnectionWrapper.cs @@ -9,9 +9,13 @@ internal class SqlConnectionWrapper : ISqlConnectionWrapper private readonly SqlConnection _sqlConnection; private bool _disposedValue; - public SqlConnectionWrapper(string connectionString) + public SqlConnectionWrapper(string connectionString, Action connectionConfiguration = null) { _sqlConnection = new SqlConnection(connectionString); + if (connectionConfiguration != null) + { + connectionConfiguration(_sqlConnection); + } } public SqlConnection SqlConnection => _sqlConnection; diff --git a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlConnectionFactory.cs b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlConnectionFactory.cs index b18f7878..004e1d84 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,18 +8,21 @@ internal class SqlConnectionFactory : ISqlConnectionFactory { private readonly string _connectionString; private readonly ISqlConnectionStringBuilderWrapper _sqlConnectionStringBuilderWrapper; + private readonly Action _connectionConfiguration; - public SqlConnectionFactory(ISqlConnectionStringBuilderWrapper sqlConnectionStringBuilderWrapper) + public SqlConnectionFactory(ISqlConnectionStringBuilderWrapper sqlConnectionStringBuilderWrapper, + Action connectionConfiguration = null) { _sqlConnectionStringBuilderWrapper = sqlConnectionStringBuilderWrapper ?? throw new ArgumentNullException(nameof(sqlConnectionStringBuilderWrapper)); _connectionString = _sqlConnectionStringBuilderWrapper.ConnectionString; + _connectionConfiguration = connectionConfiguration; } public ISqlConnectionWrapper Create() { - return new SqlConnectionWrapper(_connectionString); + return new SqlConnectionWrapper(_connectionString, _connectionConfiguration); } } } diff --git a/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactoryTests.cs b/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactoryTests.cs index 72d1bd38..91fd202c 100644 --- a/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactoryTests.cs +++ b/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactoryTests.cs @@ -1,7 +1,10 @@ -using Serilog.Sinks.MSSqlServer.Dependencies; +using Moq; +using System; +using Serilog.Sinks.MSSqlServer.Dependencies; using Serilog.Sinks.MSSqlServer.Platform; using Serilog.Sinks.MSSqlServer.Tests.TestUtils; using Xunit; +using Microsoft.Data.SqlClient; namespace Serilog.Sinks.MSSqlServer.Tests.Dependencies { @@ -68,5 +71,19 @@ public void DefaultsColumnOptionsIfNull() // Act (should not throw) SinkDependenciesFactory.Create(_connectionString, _sinkOptions, null, null, null); } + + [Fact] + public void CreatesSinkDependenciesWithSqlConnectionConfiguration() + { + // Arrange + var mockConfigurationAction = new Mock>(); + var sinkOptions = new MSSqlServerSinkOptions { TableName = "LogEvents", ConnectionConfiguration = mockConfigurationAction.Object }; + + // Act + var result = SinkDependenciesFactory.Create(_connectionString, sinkOptions, null, _columnOptions, null); + + // Assert + Assert.NotNull(result.SqlDatabaseCreator); + } } } 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..6f49e1c7 100644 --- a/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlConnectionFactoryTests.cs +++ b/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlConnectionFactoryTests.cs @@ -1,4 +1,5 @@ using System; +using Microsoft.Data.SqlClient; using Moq; using Serilog.Sinks.MSSqlServer.Platform; using Serilog.Sinks.MSSqlServer.Platform.SqlClient; @@ -38,5 +39,23 @@ public void CreateConnectionReturnsConnectionWrapper() Assert.IsAssignableFrom(connection); } } + + + [Fact] + public void CreateConnectionCallsCustomConfigurationAction() + { + // Arrange + var mockAction = new Mock>(); + var sut = new SqlConnectionFactory(_sqlConnectionStringBuilderWrapperMock.Object, mockAction.Object); + + // Act + using (var connection = sut.Create()) + { + // Assert + Assert.NotNull(connection); + Assert.IsAssignableFrom(connection); + mockAction.Verify(m => m.Invoke(connection.SqlConnection), Times.Once); + } + } } }