From d520ee8c11185d300786b177d0aff81222ce2dcd Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Fri, 20 Sep 2024 11:15:25 +0200 Subject: [PATCH 01/15] Bumped patch version after release --- src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj b/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj index 8e82e129..87e33b58 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 From da081250d7ccce143cf4e225c345b4093da60589 Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:40:51 +0200 Subject: [PATCH 02/15] Fixed issue #540 in README.md Level column defaults to nvarchar(max) instead of nvarchar(128). --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 180117e8..85b2edd1 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ CREATE TABLE [Logs] ( [Id] int IDENTITY(1,1) NOT NULL, [Message] nvarchar(max) NULL, [MessageTemplate] nvarchar(max) NULL, - [Level] nvarchar(128) NULL, + [Level] nvarchar(max) NULL, [TimeStamp] datetime NOT 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 From 5385cef6e8eae4ed488b50a8051d7a93377dbf26 Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:42:17 +0200 Subject: [PATCH 03/15] Editorconfig cleanup Removed an obsolete match pattern for *.sh. We do not have shell sripts anymore since we use PowerShell 7 for any build scripts. --- .editorconfig | 4 ---- 1 file changed, 4 deletions(-) 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 From 17b9e1c56fd2358b391b7b0ad7b9614aa79fad76 Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:23:21 +0200 Subject: [PATCH 04/15] Fixed issue #541 in README.md The Timestamp column is nullable by default but the README.md stated the opposite. This was fixed now. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 85b2edd1..3fc1eecd 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ CREATE TABLE [Logs] ( [Message] nvarchar(max) NULL, [MessageTemplate] nvarchar(max) NULL, [Level] nvarchar(max) NULL, - [TimeStamp] datetime NOT NULL, + [TimeStamp] datetime NULL, [Exception] nvarchar(max) NULL, [Properties] nvarchar(max) NULL From 579b06b9762ad4b3b66368b45dddaa91ce2a238b Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Mon, 30 Sep 2024 19:42:47 +0200 Subject: [PATCH 05/15] Added Benchmark.NET Performance Tests * New project in tests/Serilog.Sinks.MSSqlServer.PerformanceTests * Integrated into release.yml workflow (attach CSV report as file to GitHub release) * Added new perftest.yml workflow without trigger to manually run perf tests * Minor changes in .gitignore, etc. --- .github/workflows/perftests.yml | 19 +++++++ .github/workflows/release.yml | 20 ++++---- .gitignore | 4 +- Build.ps1 | 11 ---- Directory.Packages.props | 51 ++++++++++--------- RunPerfTests.ps1 | 21 ++++++++ serilog-sinks-mssqlserver.sln | 9 ++++ .../Program.cs | 17 +++++++ ....Sinks.MSSqlServer.PerformanceTests.csproj | 21 ++++++++ .../SinkBenchmarks.cs | 46 +++++++++++++++++ 10 files changed, 173 insertions(+), 46 deletions(-) create mode 100644 .github/workflows/perftests.yml create mode 100644 RunPerfTests.ps1 create mode 100644 test/Serilog.Sinks.MSSqlServer.PerformanceTests/Program.cs create mode 100644 test/Serilog.Sinks.MSSqlServer.PerformanceTests/Serilog.Sinks.MSSqlServer.PerformanceTests.csproj create mode 100644 test/Serilog.Sinks.MSSqlServer.PerformanceTests/SinkBenchmarks.cs diff --git a/.github/workflows/perftests.yml b/.github/workflows/perftests.yml new file mode 100644 index 00000000..04759b15 --- /dev/null +++ b/.github/workflows/perftests.yml @@ -0,0 +1,19 @@ +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 + shell: pwsh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3573faf2..5d2d337d 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 + shell: pwsh + - name: Get last commit message id: last_commit if: success() && github.ref == 'refs/heads/main' @@ -47,29 +51,27 @@ 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.SinkBenchmarks-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 } - # Ersetze Backslashes durch Forward Slashes für GitHub CLI-Kompatibilität $nupkgFilePath = $nupkgFile.FullName -replace '\\', '/' $snupkgFilePath = $snupkgFile.FullName -replace '\\', '/' + $perfReportSinkFilePath = $perfReportSinkFile.FullName -replace '\\', '/' - # Ausgabe der Dateipfade zu Debugging-Zwecken - Write-Host "Uploading files: $nupkgFilePath, $snupkgFilePath" + Write-Host "Uploading files: $nupkgFilePath, $snupkgFilePath $perfReportPipelineFilePath" - # 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 shell: pwsh - name: Publish to nuget.org 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..97e64429 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 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/RunPerfTests.ps1 b/RunPerfTests.ps1 new file mode 100644 index 00000000..cd7f92f6 --- /dev/null +++ b/RunPerfTests.ps1 @@ -0,0 +1,21 @@ +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 + +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/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Program.cs b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Program.cs new file mode 100644 index 00000000..421f2d58 --- /dev/null +++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Program.cs @@ -0,0 +1,17 @@ +using BenchmarkDotNet.Running; + +namespace Serilog.Sinks.MSSqlServer.PerformanceTests; + +/// +/// Wrappers that make it easy to run benchmark suites through the dotnet test runner. +/// +/// +/// dotnet test -c Release --filter "FullyQualifiedName=Serilog.Sinks.MSSqlServer.PerformanceTests.Harness.Pipeline" +/// +public class Program +{ + public static void Main() + { + BenchmarkRunner.Run(); + } +} 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..f032870a --- /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 + win + AnyCPU + 6.0-recommended + True + + + + + + + + diff --git a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/SinkBenchmarks.cs b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/SinkBenchmarks.cs new file mode 100644 index 00000000..cb3a3829 --- /dev/null +++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/SinkBenchmarks.cs @@ -0,0 +1,46 @@ +using BenchmarkDotNet.Attributes; + +namespace Serilog.Sinks.MSSqlServer.PerformanceTests; + +[MemoryDiagnoser] +public class SinkBenchmarks +{ + private const string _connectionString = @"Data Source=(localdb)\MSSQLLocalDB;Database=LogTest;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); + } +} From af6459e824d792742a2765f35d6bf1e03cafae84 Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Tue, 1 Oct 2024 10:41:08 +0200 Subject: [PATCH 06/15] Create perf test results artifact --- .github/workflows/perftests.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/perftests.yml b/.github/workflows/perftests.yml index 04759b15..45069345 100644 --- a/.github/workflows/perftests.yml +++ b/.github/workflows/perftests.yml @@ -17,3 +17,9 @@ jobs: - name: Run performance tests run: ./RunPerfTests.ps1 shell: pwsh + + - name: Upload perf test results artifact + uses: actions/upload-artifact@v4 + with: + name: perftestresults + path: artifacts\perftests From 5f108a1c648c7f3fa4a76c7899273f4487b3cb0c Mon Sep 17 00:00:00 2001 From: david-brink-talogy Date: Wed, 2 Oct 2024 10:50:30 -0400 Subject: [PATCH 07/15] Remove runtime identifiers, fixes #580 --- src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj | 2 -- .../Serilog.Sinks.MSSqlServer.PerformanceTests.csproj | 3 +-- .../Serilog.Sinks.MSSqlServer.Tests.csproj | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj b/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj index 87e33b58..d961c41a 100644 --- a/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj +++ b/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj @@ -25,7 +25,6 @@ true true snupkg - win false false false @@ -40,7 +39,6 @@ - diff --git a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Serilog.Sinks.MSSqlServer.PerformanceTests.csproj b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Serilog.Sinks.MSSqlServer.PerformanceTests.csproj index f032870a..37f712c8 100644 --- a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Serilog.Sinks.MSSqlServer.PerformanceTests.csproj +++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Serilog.Sinks.MSSqlServer.PerformanceTests.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -7,7 +7,6 @@ Exe ../../assets/Serilog.snk true - win AnyCPU 6.0-recommended True 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 From f7797461d567e5ea4b120318e3e44c3ad965e71b Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Thu, 3 Oct 2024 10:18:18 +0200 Subject: [PATCH 08/15] Minor cleanup in release.yml Removed unnecessary env parameter in nuget publish step. --- .github/workflows/release.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5d2d337d..3e727adc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -75,8 +75,6 @@ jobs: 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 From 25f4573caac7275550764b6cc9ca1d86ac388a4e Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Thu, 3 Oct 2024 10:28:06 +0200 Subject: [PATCH 09/15] Enhanced performance tests * Support filters in RunPerfTests.ps1 * Run only a subset (QuickBenchmarks) in release.yml (renamed existing sink benchmarks) * Added more complex sink benchmarks (ExtendedBenchmarks) * Added unit test-like benchmarks for SqlBulkBatchWriter. Run them with a max of 20 iterations because otherwise they take too long. --- .github/workflows/release.yml | 12 ++-- RunPerfTests.ps1 | 13 +++- .../Serilog.Sinks.MSSqlServer.csproj | 1 + .../Misc/AuditSinkExtendedBenchmarks.cs | 67 +++++++++++++++++++ .../Misc/AuditSinkQuickBenchmarks.cs | 46 +++++++++++++ .../Misc/SinkExtendedBenchmarks.cs | 67 +++++++++++++++++++ .../SinkQuickBenchmarks.cs} | 6 +- .../Program.cs | 12 +--- ....Sinks.MSSqlServer.PerformanceTests.csproj | 1 + .../Platform/SqlBulkBatchWriterBenchmarks.cs | 64 ++++++++++++++++++ 10 files changed, 271 insertions(+), 18 deletions(-) create mode 100644 test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/AuditSinkExtendedBenchmarks.cs create mode 100644 test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/AuditSinkQuickBenchmarks.cs create mode 100644 test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/SinkExtendedBenchmarks.cs rename test/Serilog.Sinks.MSSqlServer.PerformanceTests/{SinkBenchmarks.cs => Misc/SinkQuickBenchmarks.cs} (87%) create mode 100644 test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlBulkBatchWriterBenchmarks.cs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3e727adc..abebc5f3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,7 +36,7 @@ jobs: shell: pwsh - name: Run performance tests - run: ./RunPerfTests.ps1 + run: ./RunPerfTests.ps1 -Filter "*QuickBenchmarks*" shell: pwsh - name: Get last commit message @@ -55,23 +55,27 @@ jobs: $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.SinkBenchmarks-report.csv" ` + $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 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 } $nupkgFilePath = $nupkgFile.FullName -replace '\\', '/' $snupkgFilePath = $snupkgFile.FullName -replace '\\', '/' $perfReportSinkFilePath = $perfReportSinkFile.FullName -replace '\\', '/' + $perfReportAuditSinkFilePath = $perfReportAuditSinkFile.FullName -replace '\\', '/' - Write-Host "Uploading files: $nupkgFilePath, $snupkgFilePath $perfReportPipelineFilePath" + Write-Host "Uploading files: $nupkgFilePath, $snupkgFilePath $perfReportSinkFilePath $perfReportAuditSinkFilePath" gh release create v${{ env.VERSION }} ` --title "v${{ env.VERSION }}" ` --notes "$(Get-Content last_commit_message.txt)" ` - $nupkgFilePath $snupkgFilePath $perfReportSinkFilePath + $nupkgFilePath $snupkgFilePath $perfReportSinkFilePath $perfReportAuditSinkFilePath shell: pwsh - name: Publish to nuget.org diff --git a/RunPerfTests.ps1 b/RunPerfTests.ps1 index cd7f92f6..fd3e4b15 100644 --- a/RunPerfTests.ps1 +++ b/RunPerfTests.ps1 @@ -1,4 +1,13 @@ -Push-Location $PSScriptRoot +[CmdletBinding()] +param ( + [Parameter(Mandatory = $false)] + [string] + $Filter = "*" +) + +echo "perf: Performance tests started with Filter = $Filter" + +Push-Location $PSScriptRoot $artifactsPath = "$PSScriptRoot\artifacts\perftests" @@ -13,7 +22,7 @@ $perfTestProjectPath = "$PSScriptRoot/test/Serilog.Sinks.MSSqlServer.Performance Push-Location "$perfTestProjectPath" echo "perf: Running performance test project in $perfTestProjectPath" -& dotnet run -c Release +& dotnet run -c Release -- -f $Filter cp ".\BenchmarkDotNet.Artifacts\results\*.*" "$artifactsPath\" Pop-Location diff --git a/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj b/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj index d961c41a..a98903cb 100644 --- a/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj +++ b/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj @@ -74,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/SinkBenchmarks.cs b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/SinkQuickBenchmarks.cs similarity index 87% rename from test/Serilog.Sinks.MSSqlServer.PerformanceTests/SinkBenchmarks.cs rename to test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/SinkQuickBenchmarks.cs index cb3a3829..60c843ba 100644 --- a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/SinkBenchmarks.cs +++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/SinkQuickBenchmarks.cs @@ -1,11 +1,11 @@ using BenchmarkDotNet.Attributes; -namespace Serilog.Sinks.MSSqlServer.PerformanceTests; +namespace Serilog.Sinks.MSSqlServer.PerformanceTests.Misc; [MemoryDiagnoser] -public class SinkBenchmarks +public class SinkQuickBenchmarks { - private const string _connectionString = @"Data Source=(localdb)\MSSQLLocalDB;Database=LogTest;Integrated Security=SSPI;Encrypt=False;"; + 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!; diff --git a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Program.cs b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Program.cs index 421f2d58..63ccef94 100644 --- a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Program.cs +++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Program.cs @@ -2,16 +2,10 @@ namespace Serilog.Sinks.MSSqlServer.PerformanceTests; -/// -/// Wrappers that make it easy to run benchmark suites through the dotnet test runner. -/// -/// -/// dotnet test -c Release --filter "FullyQualifiedName=Serilog.Sinks.MSSqlServer.PerformanceTests.Harness.Pipeline" -/// -public class Program +public static class Program { - public static void Main() + public static void Main(string[] args) { - BenchmarkRunner.Run(); + 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 index 37f712c8..b37c5d50 100644 --- a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Serilog.Sinks.MSSqlServer.PerformanceTests.csproj +++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Serilog.Sinks.MSSqlServer.PerformanceTests.csproj @@ -15,6 +15,7 @@ + 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..97735971 --- /dev/null +++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlBulkBatchWriterBenchmarks.cs @@ -0,0 +1,64 @@ +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(20)] +public class SqlBulkBatchWriterBenchmarks +{ + private const string _tableName = "TestTableName"; + private const string _schemaName = "TestSchemaName"; + private Mock _sqlConnectionFactoryMock; + private Mock _logEventDataGeneratorMock; + private Mock _sqlConnectionWrapperMock; + private Mock _sqlBulkCopyWrapper; + 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); + + _sut = new SqlBulkBatchWriter(_tableName, _schemaName, false, _sqlConnectionFactoryMock.Object, + _logEventDataGeneratorMock.Object); + } + + [Benchmark] + public async Task WriteBatch() + { + var logEvents = CreateLogEvents(); + using var dataTable = new DataTable(_tableName); + await _sut.WriteBatch(logEvents, dataTable); + } + + private static List CreateLogEvents() + { + var logEvents = new List { CreateLogEvent(), CreateLogEvent() }; + return logEvents; + } + + 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()); + } +} From af9de4a817bb13fb65cffcc986a3e09bc8fc5d6f Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Thu, 3 Oct 2024 10:30:42 +0200 Subject: [PATCH 10/15] Use GitHub secret to filter in perftests.yml The GitHub secret PERF_TESTS_FILTER will control which benchmarks are executed in perftests.yml. Using a plain GitHub repo variable did not work for some reason. --- .github/workflows/perftests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/perftests.yml b/.github/workflows/perftests.yml index 45069345..d8bc1d45 100644 --- a/.github/workflows/perftests.yml +++ b/.github/workflows/perftests.yml @@ -15,7 +15,7 @@ jobs: shell: pwsh - name: Run performance tests - run: ./RunPerfTests.ps1 + run: ./RunPerfTests.ps1 -Filter ${{ secrets.PERF_TESTS_FILTER }} shell: pwsh - name: Upload perf test results artifact From 48bc2679cee99077ac34fc906412ce470de4fcff Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Thu, 3 Oct 2024 13:40:03 +0200 Subject: [PATCH 11/15] Build perf tests project during PR validation --- Build.ps1 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Build.ps1 b/Build.ps1 index 97e64429..9f020c6d 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -48,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 From e07ce8effb5f1096d62848118763214fbd5b097f Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Mon, 7 Oct 2024 09:38:34 +0200 Subject: [PATCH 12/15] Fixed namespace of unit test --- .../MSSqlServer/Output/AdditionalColumnDataGeneratorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 4506de75db5079059a13d4306812cd63155be174 Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Mon, 7 Oct 2024 09:39:15 +0200 Subject: [PATCH 13/15] Added SqlInsertStatementWriter benchmark --- .../SqlInsertStatementWriterBenchmarks.cs | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlInsertStatementWriterBenchmarks.cs 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(); + } +} From 89a53f70cbb6082a7e39386a01cd6543ae26e79b Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Mon, 7 Oct 2024 09:40:17 +0200 Subject: [PATCH 14/15] Improved SqlBulkBatchWriter benchmark --- .../Platform/SqlBulkBatchWriterBenchmarks.cs | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlBulkBatchWriterBenchmarks.cs b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlBulkBatchWriterBenchmarks.cs index 97735971..c2c7a756 100644 --- a/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlBulkBatchWriterBenchmarks.cs +++ b/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlBulkBatchWriterBenchmarks.cs @@ -13,15 +13,17 @@ namespace Serilog.Sinks.MSSqlServer.PerformanceTests.Platform; [MemoryDiagnoser] -[MaxIterationCount(20)] -public class SqlBulkBatchWriterBenchmarks +[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] @@ -36,6 +38,8 @@ public void Setup() _sqlConnectionWrapperMock.Setup(c => c.CreateSqlBulkCopy(It.IsAny(), It.IsAny())) .Returns(_sqlBulkCopyWrapper.Object); + CreateLogEvents(); + _sut = new SqlBulkBatchWriter(_tableName, _schemaName, false, _sqlConnectionFactoryMock.Object, _logEventDataGeneratorMock.Object); } @@ -43,15 +47,7 @@ public void Setup() [Benchmark] public async Task WriteBatch() { - var logEvents = CreateLogEvents(); - using var dataTable = new DataTable(_tableName); - await _sut.WriteBatch(logEvents, dataTable); - } - - private static List CreateLogEvents() - { - var logEvents = new List { CreateLogEvent(), CreateLogEvent() }; - return logEvents; + await _sut.WriteBatch(_logEvents, _dataTable); } private static LogEvent CreateLogEvent() @@ -61,4 +57,20 @@ private static LogEvent CreateLogEvent() 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(); + } } From c097a65b032f699d2da9f8f161fc64bd1c1b1998 Mon Sep 17 00:00:00 2001 From: Christian Kadluba <10721825+ckadluba@users.noreply.github.com> Date: Fri, 18 Oct 2024 22:55:58 +0200 Subject: [PATCH 15/15] Updated CHANGES.md --- CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) 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