diff --git a/.editorconfig b/.editorconfig
index ed09cfcf..0feb7640 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -9,9 +9,6 @@ trim_trailing_whitespace = true
[*.{csproj,json,config,yml}]
indent_size = 2
-[*.sh]
-end_of_line = lf
-
[*.{cmd,bat}]
end_of_line = crlf
@@ -43,7 +40,6 @@ csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
-csharp_space_between_parentheses = false
csharp_style_conditional_delegate_call = true:suggestion
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_constructors = false:silent
diff --git a/.github/workflows/perftests.yml b/.github/workflows/perftests.yml
new file mode 100644
index 00000000..d8bc1d45
--- /dev/null
+++ b/.github/workflows/perftests.yml
@@ -0,0 +1,25 @@
+name: Performance Tests
+
+on:
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+jobs:
+ build-and-perftest:
+ runs-on: windows-latest # Build on Windows to ensure .NET Framework targets
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Run build
+ run: ./Build.ps1 -SkipTests
+ shell: pwsh
+
+ - name: Run performance tests
+ run: ./RunPerfTests.ps1 -Filter ${{ secrets.PERF_TESTS_FILTER }}
+ shell: pwsh
+
+ - name: Upload perf test results artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: perftestresults
+ path: artifacts\perftests
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 3573faf2..abebc5f3 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -8,7 +8,7 @@ on:
workflow_dispatch:
jobs:
- build-and-release:
+ build-perftest-and-release:
runs-on: windows-latest # Build on Windows to ensure .NET Framework targets
steps:
- uses: actions/checkout@v4
@@ -35,6 +35,10 @@ jobs:
run: ./Build.ps1 -SkipTests
shell: pwsh
+ - name: Run performance tests
+ run: ./RunPerfTests.ps1 -Filter "*QuickBenchmarks*"
+ shell: pwsh
+
- name: Get last commit message
id: last_commit
if: success() && github.ref == 'refs/heads/main'
@@ -47,34 +51,34 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
- # Der Basisname der Dateien basierend auf der Versionsnummer
$baseFileName = "Serilog.Sinks.MSSqlServer.${{ env.VERSION }}"
- # Suche die exakten Dateipfade für .nupkg und .snupkg
$nupkgFile = Get-ChildItem -Path "artifacts/$baseFileName*.nupkg" | Select-Object -First 1
$snupkgFile = Get-ChildItem -Path "artifacts/$baseFileName*.snupkg" | Select-Object -First 1
+ $perfReportSinkFile = Get-ChildItem -Path "artifacts/perftests/Serilog.Sinks.MSSqlServer.PerformanceTests.SinkQuickBenchmarks-report.csv" `
+ | Select-Object -First 1
+ $perfReportAuditSinkFile = Get-ChildItem -Path "artifacts/perftests/Serilog.Sinks.MSSqlServer.PerformanceTests.AuditSinkQuickBenchmarks-report.csv" `
+ | Select-Object -First 1
- # Überprüfe, ob beide Dateien gefunden wurden
if (-not $nupkgFile) { Write-Error "nupkg file not found" ; exit 1 }
if (-not $snupkgFile) { Write-Error "snupkg file not found" ; exit 1 }
+ if (-not $perfReportSinkFile) { Write-Error "Benchmark report for sink file not found" ; exit 1 }
+ if (-not $perfReportAuditSinkFile) { Write-Error "Benchmark report for audit sink file not found" ; exit 1 }
- # Ersetze Backslashes durch Forward Slashes für GitHub CLI-Kompatibilität
$nupkgFilePath = $nupkgFile.FullName -replace '\\', '/'
$snupkgFilePath = $snupkgFile.FullName -replace '\\', '/'
+ $perfReportSinkFilePath = $perfReportSinkFile.FullName -replace '\\', '/'
+ $perfReportAuditSinkFilePath = $perfReportAuditSinkFile.FullName -replace '\\', '/'
- # Ausgabe der Dateipfade zu Debugging-Zwecken
- Write-Host "Uploading files: $nupkgFilePath, $snupkgFilePath"
+ Write-Host "Uploading files: $nupkgFilePath, $snupkgFilePath $perfReportSinkFilePath $perfReportAuditSinkFilePath"
- # Erstelle das Release mit den genauen Dateipfaden
gh release create v${{ env.VERSION }} `
--title "v${{ env.VERSION }}" `
--notes "$(Get-Content last_commit_message.txt)" `
- $nupkgFilePath $snupkgFilePath
+ $nupkgFilePath $snupkgFilePath $perfReportSinkFilePath $perfReportAuditSinkFilePath
shell: pwsh
- name: Publish to nuget.org
- env:
- NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
nuget push artifacts\*.nupkg -Source https://api.nuget.org/v3/index.json -ApiKey ${{ secrets.NUGET_API_KEY }}
shell: pwsh
diff --git a/.gitignore b/.gitignore
index 4b3eeff0..9c1f0a18 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,8 @@ build/
bld/
[Bb]in/
[Oo]bj/
+artifacts/
+BenchmarkDotNet.Artifacts/
# Roslyn cache directories
*.ide/
@@ -125,7 +127,7 @@ publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
-# TODO: Comment the next line if you want to checkin your web deploy settings
+# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
diff --git a/Build.ps1 b/Build.ps1
index 9beacfc8..9f020c6d 100644
--- a/Build.ps1
+++ b/Build.ps1
@@ -38,17 +38,6 @@ foreach ($src in Get-ChildItem "$PSScriptRoot/src" -Directory) {
}
if ($SkipTests -eq $false) {
- foreach ($test in Get-ChildItem "$PSScriptRoot/test" -Filter "*.PerformanceTests" -Directory) {
- Push-Location $test.FullName
-
- echo "build: Building performance test project in $($test.FullName)"
-
- & dotnet build -c Release
- if ($LASTEXITCODE -ne 0) { exit 2 }
-
- Pop-Location
- }
-
foreach ($test in Get-ChildItem "$PSScriptRoot/test" -Filter "*.Tests" -Directory) {
Push-Location $test.FullName
@@ -59,6 +48,15 @@ if ($SkipTests -eq $false) {
Pop-Location
}
+
+ # The performance benchmark tests should at least build without errors during PR validation
+ $perfTestProjectPath = "$PSScriptRoot/test/Serilog.Sinks.MSSqlServer.PerformanceTests"
+ Push-Location "$perfTestProjectPath"
+
+ echo "build: Building performance test project in $perfTestProjectPath"
+ & dotnet build -c Release
+
+ Pop-Location
}
Pop-Location
diff --git a/CHANGES.md b/CHANGES.md
index 4be1cfc1..b104f85c 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,9 @@
+# 7.0.2
+* Fixed issue #580: Removed deprecated transitive dependency on Microsoft.NETCore.Targets by removing runtime identifier (thanks to @david-brink-talogy)
+* Fixed issues #540 and #541 in README
+* Added performance tests including a GitHub actions workflow
+* Minor cleanups and fixes
+
# 7.0.1
* Fixed issue #567: .NET Framework assemblies were not built properly
* Consolidated PR validation action workflows and updated some task versions
diff --git a/Directory.Packages.props b/Directory.Packages.props
index be2b31db..6ee1a16a 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,26 +1,27 @@
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 180117e8..3fc1eecd 100644
--- a/README.md
+++ b/README.md
@@ -187,8 +187,8 @@ CREATE TABLE [Logs] (
[Id] int IDENTITY(1,1) NOT NULL,
[Message] nvarchar(max) NULL,
[MessageTemplate] nvarchar(max) NULL,
- [Level] nvarchar(128) NULL,
- [TimeStamp] datetime NOT NULL,
+ [Level] nvarchar(max) NULL,
+ [TimeStamp] datetime NULL,
[Exception] nvarchar(max) NULL,
[Properties] nvarchar(max) NULL
@@ -468,7 +468,7 @@ If `DataLength` is set to a value different to -1 longer text will be truncated.
### Level
-This column stores the event level (Error, Information, etc.). For backwards-compatibility reasons it defaults to a length of 128 characters, but 12 characters is recommended. Alternately, the `StoreAsEnum` property can be set to `true` which causes the underlying level enum integer value to be stored as a SQL `tinyint` column. The `DataType` property can only be set to `nvarchar` or `tinyint`. Setting the `DataType` to `tinyint` is identical to setting `StoreAsEnum` to `true`.
+This column stores the event level (Error, Information, etc.). For backwards-compatibility reasons it defaults to a length of `nvarchar(max)` characters, but 12 characters is recommended. Alternately, the `StoreAsEnum` property can be set to `true` which causes the underlying level enum integer value to be stored as a SQL `tinyint` column. The `DataType` property can only be set to `nvarchar` or `tinyint`. Setting the `DataType` to `tinyint` is identical to setting `StoreAsEnum` to `true`.
### TimeStamp
diff --git a/RunPerfTests.ps1 b/RunPerfTests.ps1
new file mode 100644
index 00000000..fd3e4b15
--- /dev/null
+++ b/RunPerfTests.ps1
@@ -0,0 +1,30 @@
+[CmdletBinding()]
+param (
+ [Parameter(Mandatory = $false)]
+ [string]
+ $Filter = "*"
+)
+
+echo "perf: Performance tests started with Filter = $Filter"
+
+Push-Location $PSScriptRoot
+
+$artifactsPath = "$PSScriptRoot\artifacts\perftests"
+
+if (Test-Path "$artifactsPath") {
+ echo "perf: Cleaning $artifactsPath"
+ Remove-Item "$artifactsPath" -Force -Recurse
+}
+
+New-Item -Path "$artifactsPath" -ItemType Directory
+
+$perfTestProjectPath = "$PSScriptRoot/test/Serilog.Sinks.MSSqlServer.PerformanceTests"
+Push-Location "$perfTestProjectPath"
+
+echo "perf: Running performance test project in $perfTestProjectPath"
+& dotnet run -c Release -- -f $Filter
+
+cp ".\BenchmarkDotNet.Artifacts\results\*.*" "$artifactsPath\"
+Pop-Location
+
+Pop-Location
diff --git a/serilog-sinks-mssqlserver.sln b/serilog-sinks-mssqlserver.sln
index 2c4ed1be..9768fe57 100644
--- a/serilog-sinks-mssqlserver.sln
+++ b/serilog-sinks-mssqlserver.sln
@@ -33,6 +33,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\pr-validation.yml = .github\workflows\pr-validation.yml
README.md = README.md
.github\workflows\release.yml = .github\workflows\release.yml
+ RunPerfTests.ps1 = RunPerfTests.ps1
+ .github\workflows\perftests.yml = .github\workflows\perftests.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetStandardDemoLib", "sample\NetStandardDemo\NetStandardDemoLib\NetStandardDemoLib.csproj", "{8E69E31B-61C7-4175-B886-9C2078FCA477}"
@@ -43,6 +45,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NetStandardDemo", "NetStand
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppConfigDemo", "sample\AppConfigDemo\AppConfigDemo.csproj", "{6BFE1D21-1442-4375-AB69-14160B906A64}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Sinks.MSSqlServer.PerformanceTests", "test\Serilog.Sinks.MSSqlServer.PerformanceTests\Serilog.Sinks.MSSqlServer.PerformanceTests.csproj", "{106A6BAF-F8E4-408B-BB09-391330DA87F2}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -81,6 +85,10 @@ Global
{6BFE1D21-1442-4375-AB69-14160B906A64}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6BFE1D21-1442-4375-AB69-14160B906A64}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6BFE1D21-1442-4375-AB69-14160B906A64}.Release|Any CPU.Build.0 = Release|Any CPU
+ {106A6BAF-F8E4-408B-BB09-391330DA87F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {106A6BAF-F8E4-408B-BB09-391330DA87F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {106A6BAF-F8E4-408B-BB09-391330DA87F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {106A6BAF-F8E4-408B-BB09-391330DA87F2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -95,6 +103,7 @@ Global
{F908C46D-E72E-41E4-975D-73733294F93F} = {7B2B80DE-427A-4FEC-A7CE-7AD81FED73DE}
{7B2B80DE-427A-4FEC-A7CE-7AD81FED73DE} = {AA346332-5BAF-47F1-B8FB-7600ED61265D}
{6BFE1D21-1442-4375-AB69-14160B906A64} = {AA346332-5BAF-47F1-B8FB-7600ED61265D}
+ {106A6BAF-F8E4-408B-BB09-391330DA87F2} = {F02D6513-6F45-452E-85A0-41A872A2C1F8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AAA6BF8D-7B53-4A5F-A79A-D1B306383B45}
diff --git a/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj b/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj
index 8e82e129..a98903cb 100644
--- a/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj
+++ b/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj
@@ -2,7 +2,7 @@
A Serilog sink that writes events to Microsoft SQL Server and Azure SQL
- 7.0.1
+ 7.0.2
true
7.0.0
Michiel van Oudheusden;Christian Kadluba;Serilog Contributors
@@ -25,7 +25,6 @@
true
true
snupkg
- win
false
false
false
@@ -40,7 +39,6 @@
-
@@ -76,6 +74,7 @@
+
diff --git a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/AuditSinkExtendedBenchmarks.cs b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/AuditSinkExtendedBenchmarks.cs
new file mode 100644
index 00000000..f45fb2b4
--- /dev/null
+++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/AuditSinkExtendedBenchmarks.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using BenchmarkDotNet.Attributes;
+
+namespace Serilog.Sinks.MSSqlServer.PerformanceTests.Misc;
+
+[MemoryDiagnoser]
+public class AuditSinkExtendedBenchmarks
+{
+ private const string _connectionString = @"Data Source=(localdb)\MSSQLLocalDB;Database=LogAuditExtPerfTest;Integrated Security=SSPI;Encrypt=False;";
+ private const string _schemaName = "dbo";
+ private const string _tableName = "LogEvents";
+ private ILogger _log = null!;
+ private DateTimeOffset _additionalColumn7;
+
+
+ [Params("String One", "String Two")]
+ public string AdditionalColumn1 { get; set; }
+
+ [Params(1, 2)]
+ public int AdditionalColumn2 { get; set; }
+
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ var options = new ColumnOptions
+ {
+ AdditionalColumns = new List
+ {
+ new() { DataType = SqlDbType.NVarChar, ColumnName = "AdditionalColumn1", DataLength = 40 },
+ new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn2" },
+ new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn3" },
+ new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn4" },
+ new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn5" },
+ new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn6" },
+ new() { DataType = SqlDbType.DateTimeOffset, ColumnName = "AdditionalColumn7" }
+ }
+ };
+ options.Store.Add(StandardColumn.LogEvent);
+ _log = new LoggerConfiguration()
+ .AuditTo.MSSqlServer(_connectionString,
+ sinkOptions: new MSSqlServerSinkOptions
+ {
+ TableName = _tableName,
+ SchemaName = _schemaName,
+ AutoCreateSqlTable = true,
+ AutoCreateSqlDatabase = true
+ },
+ appConfiguration: null,
+ restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Verbose,
+ formatProvider: null,
+ columnOptions: options,
+ columnOptionsSection: null)
+ .CreateLogger();
+
+ _additionalColumn7 = new DateTimeOffset(2024, 01, 01, 00, 00, 00, TimeSpan.FromHours(1));
+ }
+
+ [Benchmark]
+ public void EmitComplexLogEvent()
+ {
+ _log.Information("Hello, {AdditionalColumn1} {AdditionalColumn2} {AdditionalColumn3} {AdditionalColumn4} {AdditionalColumn5} {AdditionalColumn6} {AdditionalColumn7}!",
+ AdditionalColumn1, AdditionalColumn2, 3, 4, 5, 6, _additionalColumn7);
+ }
+}
diff --git a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/AuditSinkQuickBenchmarks.cs b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/AuditSinkQuickBenchmarks.cs
new file mode 100644
index 00000000..71a6c1c7
--- /dev/null
+++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/AuditSinkQuickBenchmarks.cs
@@ -0,0 +1,46 @@
+using BenchmarkDotNet.Attributes;
+
+namespace Serilog.Sinks.MSSqlServer.PerformanceTests.Misc;
+
+[MemoryDiagnoser]
+public class AuditSinkQuickBenchmarks
+{
+ private const string _connectionString = @"Data Source=(localdb)\MSSQLLocalDB;Database=LogAuditQuickPerfTest;Integrated Security=SSPI;Encrypt=False;";
+ private const string _schemaName = "dbo";
+ private const string _tableName = "LogEvents";
+ private ILogger _log = null!;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ var options = new ColumnOptions();
+ options.Store.Add(StandardColumn.LogEvent);
+ _log = new LoggerConfiguration()
+ .AuditTo.MSSqlServer(_connectionString,
+ sinkOptions: new MSSqlServerSinkOptions
+ {
+ TableName = _tableName,
+ SchemaName = _schemaName,
+ AutoCreateSqlTable = true,
+ AutoCreateSqlDatabase = true
+ },
+ appConfiguration: null,
+ restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Verbose,
+ formatProvider: null,
+ columnOptions: options,
+ columnOptionsSection: null)
+ .CreateLogger();
+ }
+
+ [Benchmark]
+ public void EmitLogEvent()
+ {
+ _log.Information("Hello, {Name}!", "World");
+ }
+
+ [Benchmark]
+ public void IntProperties()
+ {
+ _log.Information("Hello, {A} {B} {C}!", 1, 2, 3);
+ }
+}
diff --git a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/SinkExtendedBenchmarks.cs b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/SinkExtendedBenchmarks.cs
new file mode 100644
index 00000000..9e21e646
--- /dev/null
+++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/SinkExtendedBenchmarks.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using BenchmarkDotNet.Attributes;
+
+namespace Serilog.Sinks.MSSqlServer.PerformanceTests.Misc;
+
+[MemoryDiagnoser]
+public class SinkExtendedBenchmarks
+{
+ private const string _connectionString = @"Data Source=(localdb)\MSSQLLocalDB;Database=LogExtPerfTest;Integrated Security=SSPI;Encrypt=False;";
+ private const string _schemaName = "dbo";
+ private const string _tableName = "LogEvents";
+ private ILogger _log = null!;
+ private DateTimeOffset _additionalColumn7;
+
+
+ [Params("String One", "String Two")]
+ public string AdditionalColumn1 { get; set; }
+
+ [Params(1, 2)]
+ public int AdditionalColumn2 { get; set; }
+
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ var options = new ColumnOptions
+ {
+ AdditionalColumns = new List
+ {
+ new() { DataType = SqlDbType.NVarChar, ColumnName = "AdditionalColumn1", DataLength = 40 },
+ new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn2" },
+ new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn3" },
+ new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn4" },
+ new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn5" },
+ new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn6" },
+ new() { DataType = SqlDbType.DateTimeOffset, ColumnName = "AdditionalColumn7" }
+ }
+ };
+ options.Store.Add(StandardColumn.LogEvent);
+ _log = new LoggerConfiguration()
+ .WriteTo.MSSqlServer(_connectionString,
+ sinkOptions: new MSSqlServerSinkOptions
+ {
+ TableName = _tableName,
+ SchemaName = _schemaName,
+ AutoCreateSqlTable = true,
+ AutoCreateSqlDatabase = true
+ },
+ appConfiguration: null,
+ restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Verbose,
+ formatProvider: null,
+ columnOptions: options,
+ columnOptionsSection: null)
+ .CreateLogger();
+
+ _additionalColumn7 = new DateTimeOffset(2024, 01, 01, 00, 00, 00, TimeSpan.FromHours(1));
+ }
+
+ [Benchmark]
+ public void EmitComplexLogEvent()
+ {
+ _log.Information("Hello, {AdditionalColumn1} {AdditionalColumn2} {AdditionalColumn3} {AdditionalColumn4} {AdditionalColumn5} {AdditionalColumn6} {AdditionalColumn7}!",
+ AdditionalColumn1, AdditionalColumn2,3, 4, 5, 6, _additionalColumn7);
+ }
+}
diff --git a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/SinkQuickBenchmarks.cs b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/SinkQuickBenchmarks.cs
new file mode 100644
index 00000000..60c843ba
--- /dev/null
+++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/SinkQuickBenchmarks.cs
@@ -0,0 +1,46 @@
+using BenchmarkDotNet.Attributes;
+
+namespace Serilog.Sinks.MSSqlServer.PerformanceTests.Misc;
+
+[MemoryDiagnoser]
+public class SinkQuickBenchmarks
+{
+ private const string _connectionString = @"Data Source=(localdb)\MSSQLLocalDB;Database=LogQuickPerfTest;Integrated Security=SSPI;Encrypt=False;";
+ private const string _schemaName = "dbo";
+ private const string _tableName = "LogEvents";
+ private ILogger _log = null!;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ var options = new ColumnOptions();
+ options.Store.Add(StandardColumn.LogEvent);
+ _log = new LoggerConfiguration()
+ .WriteTo.MSSqlServer(_connectionString,
+ sinkOptions: new MSSqlServerSinkOptions
+ {
+ TableName = _tableName,
+ SchemaName = _schemaName,
+ AutoCreateSqlTable = true,
+ AutoCreateSqlDatabase = true
+ },
+ appConfiguration: null,
+ restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Verbose,
+ formatProvider: null,
+ columnOptions: options,
+ columnOptionsSection: null)
+ .CreateLogger();
+ }
+
+ [Benchmark]
+ public void EmitLogEvent()
+ {
+ _log.Information("Hello, {Name}!", "World");
+ }
+
+ [Benchmark]
+ public void IntProperties()
+ {
+ _log.Information("Hello, {A} {B} {C}!", 1, 2, 3);
+ }
+}
diff --git a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Program.cs b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Program.cs
new file mode 100644
index 00000000..63ccef94
--- /dev/null
+++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Program.cs
@@ -0,0 +1,11 @@
+using BenchmarkDotNet.Running;
+
+namespace Serilog.Sinks.MSSqlServer.PerformanceTests;
+
+public static class Program
+{
+ public static void Main(string[] args)
+ {
+ BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
+ }
+}
diff --git a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Serilog.Sinks.MSSqlServer.PerformanceTests.csproj b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Serilog.Sinks.MSSqlServer.PerformanceTests.csproj
new file mode 100644
index 00000000..b37c5d50
--- /dev/null
+++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Serilog.Sinks.MSSqlServer.PerformanceTests.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net6.0
+ true
+ Serilog.Sinks.MSSqlServer.PerformanceTests
+ Exe
+ ../../assets/Serilog.snk
+ true
+ AnyCPU
+ 6.0-recommended
+ True
+
+
+
+
+
+
+
+
+
diff --git a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlBulkBatchWriterBenchmarks.cs b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlBulkBatchWriterBenchmarks.cs
new file mode 100644
index 00000000..c2c7a756
--- /dev/null
+++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlBulkBatchWriterBenchmarks.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Threading.Tasks;
+using BenchmarkDotNet.Attributes;
+using Moq;
+using Serilog.Events;
+using Serilog.Parsing;
+using Serilog.Sinks.MSSqlServer.Output;
+using Serilog.Sinks.MSSqlServer.Platform;
+using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
+
+namespace Serilog.Sinks.MSSqlServer.PerformanceTests.Platform;
+
+[MemoryDiagnoser]
+[MaxIterationCount(16)]
+public class SqlBulkBatchWriterBenchmarks : IDisposable
+{
+ private const string _tableName = "TestTableName";
+ private const string _schemaName = "TestSchemaName";
+ private readonly DataTable _dataTable = new(_tableName);
+ private Mock _sqlConnectionFactoryMock;
+ private Mock _logEventDataGeneratorMock;
+ private Mock _sqlConnectionWrapperMock;
+ private Mock _sqlBulkCopyWrapper;
+ private List _logEvents;
+ private SqlBulkBatchWriter _sut;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ _sqlConnectionFactoryMock = new Mock();
+ _logEventDataGeneratorMock = new Mock();
+ _sqlConnectionWrapperMock = new Mock();
+ _sqlBulkCopyWrapper = new Mock();
+
+ _sqlConnectionFactoryMock.Setup(f => f.Create()).Returns(_sqlConnectionWrapperMock.Object);
+ _sqlConnectionWrapperMock.Setup(c => c.CreateSqlBulkCopy(It.IsAny(), It.IsAny()))
+ .Returns(_sqlBulkCopyWrapper.Object);
+
+ CreateLogEvents();
+
+ _sut = new SqlBulkBatchWriter(_tableName, _schemaName, false, _sqlConnectionFactoryMock.Object,
+ _logEventDataGeneratorMock.Object);
+ }
+
+ [Benchmark]
+ public async Task WriteBatch()
+ {
+ await _sut.WriteBatch(_logEvents, _dataTable);
+ }
+
+ private static LogEvent CreateLogEvent()
+ {
+ return new LogEvent(
+ new DateTimeOffset(2020, 1, 1, 0, 0, 0, 0, TimeSpan.Zero),
+ LogEventLevel.Debug, null, new MessageTemplate(new List()),
+ new List());
+ }
+
+ private void CreateLogEvents()
+ {
+ _logEvents = new List();
+ var eventCount = 500_000;
+ while (eventCount-- > 0)
+ {
+ _logEvents.Add(CreateLogEvent());
+ }
+ }
+
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ _dataTable.Dispose();
+ }
+}
diff --git a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlInsertStatementWriterBenchmarks.cs b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlInsertStatementWriterBenchmarks.cs
new file mode 100644
index 00000000..2c2f76f4
--- /dev/null
+++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlInsertStatementWriterBenchmarks.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Threading.Tasks;
+using BenchmarkDotNet.Attributes;
+using Moq;
+using Serilog.Events;
+using Serilog.Parsing;
+using Serilog.Sinks.MSSqlServer.Output;
+using Serilog.Sinks.MSSqlServer.Platform;
+using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
+
+namespace Serilog.Sinks.MSSqlServer.PerformanceTests.Platform;
+
+[MemoryDiagnoser]
+[MaxIterationCount(16)]
+public class SqlInsertStatementWriterBenchmarks : IDisposable
+{
+ private const string _tableName = "TestTableName";
+ private const string _schemaName = "TestSchemaName";
+ private readonly DataTable _dataTable = new(_tableName);
+ private Mock _sqlConnectionFactoryMock;
+ private Mock _logEventDataGeneratorMock;
+ private Mock _sqlConnectionWrapperMock;
+ private Mock _sqlCommandWrapperMock;
+ private List _logEvents;
+ private SqlInsertStatementWriter _sut;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ _sqlConnectionFactoryMock = new Mock();
+ _logEventDataGeneratorMock = new Mock();
+ _sqlConnectionWrapperMock = new Mock();
+ _sqlCommandWrapperMock = new Mock();
+
+ _sqlConnectionFactoryMock.Setup(f => f.Create()).Returns(_sqlConnectionWrapperMock.Object);
+ _sqlConnectionWrapperMock.Setup(f => f.CreateCommand()).Returns(_sqlCommandWrapperMock.Object);
+
+ CreateLogEvents();
+
+ _sut = new SqlInsertStatementWriter(_tableName, _schemaName, _sqlConnectionFactoryMock.Object,
+ _logEventDataGeneratorMock.Object);
+ }
+
+ [Benchmark]
+ public async Task WriteBatch()
+ {
+ await _sut.WriteBatch(_logEvents, _dataTable);
+ }
+
+ private static LogEvent CreateLogEvent()
+ {
+ return new LogEvent(
+ new DateTimeOffset(2020, 1, 1, 0, 0, 0, 0, TimeSpan.Zero),
+ LogEventLevel.Debug, null, new MessageTemplate(new List()),
+ new List());
+ }
+
+ private void CreateLogEvents()
+ {
+ _logEvents = new List();
+ var eventCount = 200_000;
+ while (eventCount-- > 0)
+ {
+ _logEvents.Add(CreateLogEvent());
+ }
+ }
+
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ _dataTable.Dispose();
+ }
+}
diff --git a/test/Serilog.Sinks.MSSqlServer.Tests/Serilog.Sinks.MSSqlServer.Tests.csproj b/test/Serilog.Sinks.MSSqlServer.Tests/Serilog.Sinks.MSSqlServer.Tests.csproj
index 828477ca..b1e02e28 100644
--- a/test/Serilog.Sinks.MSSqlServer.Tests/Serilog.Sinks.MSSqlServer.Tests.csproj
+++ b/test/Serilog.Sinks.MSSqlServer.Tests/Serilog.Sinks.MSSqlServer.Tests.csproj
@@ -9,7 +9,6 @@
true
Serilog.Sinks.MSSqlServer.Tests
true
- win
true
AnyCPU
6.0-recommended
diff --git a/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Output/AdditionalColumnDataGeneratorTests.cs b/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Output/AdditionalColumnDataGeneratorTests.cs
index 0b08096e..e7143479 100644
--- a/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Output/AdditionalColumnDataGeneratorTests.cs
+++ b/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Output/AdditionalColumnDataGeneratorTests.cs
@@ -8,7 +8,7 @@
using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
using Xunit;
-namespace Serilog.Tests.Output
+namespace Serilog.Sinks.MSSqlServer.Tests.Output
{
[Trait(TestCategory.TraitName, TestCategory.Unit)]
public class AdditionalColumnDataGeneratorTests