From 9f819de696ad1df97923a1f27071cbd5327bae2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Hou=C5=A1ka?= Date: Fri, 9 Jan 2026 18:01:45 +0100 Subject: [PATCH 1/4] Update aspire to 13.1 (examples + code) (#1417) Allows usage of aspire CLI which is very useful for dev in codespaces (for my next PR). --- examples-Aspire/AspireApp1.AppHost/AspireApp1.AppHost.csproj | 4 ++-- .../AspireApp1.AppHostOriginal.csproj | 4 ++-- examples-Aspire/AspireApp1.Tests/AspireApp1.Tests.csproj | 2 +- src/WireMock.Net.Aspire/WireMock.Net.Aspire.csproj | 2 +- .../WireMock.Net.Aspire.TestAppHost.csproj | 4 ++-- .../WireMock.Net.Aspire.Tests.csproj | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples-Aspire/AspireApp1.AppHost/AspireApp1.AppHost.csproj b/examples-Aspire/AspireApp1.AppHost/AspireApp1.AppHost.csproj index cf43ca01..a04440d9 100644 --- a/examples-Aspire/AspireApp1.AppHost/AspireApp1.AppHost.csproj +++ b/examples-Aspire/AspireApp1.AppHost/AspireApp1.AppHost.csproj @@ -1,6 +1,6 @@ - + Exe @@ -18,7 +18,7 @@ - + diff --git a/examples-Aspire/AspireApp1.AppHostOriginal/AspireApp1.AppHostOriginal.csproj b/examples-Aspire/AspireApp1.AppHostOriginal/AspireApp1.AppHostOriginal.csproj index f556db85..ddfe855d 100644 --- a/examples-Aspire/AspireApp1.AppHostOriginal/AspireApp1.AppHostOriginal.csproj +++ b/examples-Aspire/AspireApp1.AppHostOriginal/AspireApp1.AppHostOriginal.csproj @@ -1,6 +1,6 @@ - + Exe @@ -15,7 +15,7 @@ - + diff --git a/examples-Aspire/AspireApp1.Tests/AspireApp1.Tests.csproj b/examples-Aspire/AspireApp1.Tests/AspireApp1.Tests.csproj index a4d0fb2c..94f73378 100644 --- a/examples-Aspire/AspireApp1.Tests/AspireApp1.Tests.csproj +++ b/examples-Aspire/AspireApp1.Tests/AspireApp1.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/WireMock.Net.Aspire/WireMock.Net.Aspire.csproj b/src/WireMock.Net.Aspire/WireMock.Net.Aspire.csproj index 088098d4..379011c0 100644 --- a/src/WireMock.Net.Aspire/WireMock.Net.Aspire.csproj +++ b/src/WireMock.Net.Aspire/WireMock.Net.Aspire.csproj @@ -45,7 +45,7 @@ - + diff --git a/test/WireMock.Net.Aspire.TestAppHost/WireMock.Net.Aspire.TestAppHost.csproj b/test/WireMock.Net.Aspire.TestAppHost/WireMock.Net.Aspire.TestAppHost.csproj index 16d5e97c..96f2bb2f 100644 --- a/test/WireMock.Net.Aspire.TestAppHost/WireMock.Net.Aspire.TestAppHost.csproj +++ b/test/WireMock.Net.Aspire.TestAppHost/WireMock.Net.Aspire.TestAppHost.csproj @@ -1,6 +1,6 @@ - + Exe @@ -19,7 +19,7 @@ - + diff --git a/test/WireMock.Net.Aspire.Tests/WireMock.Net.Aspire.Tests.csproj b/test/WireMock.Net.Aspire.Tests/WireMock.Net.Aspire.Tests.csproj index 51a115cf..c7a0dbd5 100644 --- a/test/WireMock.Net.Aspire.Tests/WireMock.Net.Aspire.Tests.csproj +++ b/test/WireMock.Net.Aspire.Tests/WireMock.Net.Aspire.Tests.csproj @@ -13,7 +13,7 @@ - + all From abe996671ea1c2afdb49f057e7b718748f742874 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Fri, 9 Jan 2026 18:20:12 +0100 Subject: [PATCH 2/4] Add Copilot Setup Steps action (#1419) --- .github/workflows/copilot-setup-steps.yml | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/copilot-setup-steps.yml diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 00000000..530ae749 --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,36 @@ +name: "Copilot Setup Steps" + +# Automatically run the setup steps when they are changed to allow for easy validation, and +# allow manual testing through the repository's "Actions" tab +on: + workflow_dispatch: + push: + paths: + - .github/workflows/copilot-setup-steps.yml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yml + +jobs: + # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. + copilot-setup-steps: + runs-on: ubuntu-latest + + # Set the permissions to the lowest permissions possible needed for your steps. + # Copilot will be given its own token for its operations. + permissions: + # If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete. + contents: read + + # You can define any steps you want, and they will run before the agent starts. + # If you do not check out your code, Copilot will do this for you. + steps: + - name: Install .NET 10.x + uses: actions/setup-dotnet@v5 + with: + dotnet-version: | + 10.x + dotnet-quality: preview + + - name: dotnet --info + run: dotnet --info \ No newline at end of file From 4525c6184795b1038e70ac2800427b3e333c6a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Hou=C5=A1ka?= Date: Sun, 18 Jan 2026 17:22:36 +0100 Subject: [PATCH 3/4] Add OTEL tracing support for Wiremock + automatic OTEL for Aspire integration (#1418) * Update aspire to 13.1 (examples + code) Allows usage of aspire CLI which is very useful for dev in codespaces (for my next PR). * Add OTEL support * Initial PR feedback * PR feedback * PR feedback * PR feedback * Cleanup. * Cleanup * Fix * Fix * Rename stuff around to be more accurate * PR feedback * Update WireMock.Net.OpenTelemetry.csproj Update * PR feedback parser * PR feedback package versions * Status code feedback. * Update preprocessor directives to to Activity Tracing instead of OpenTelemetry. Is more descriptive. * Add tests * Improve tests --------- Co-authored-by: Stef Heyenrath --- WireMock.Net Solution.sln | 450 ++++++++++++++++++ examples-Aspire/AspireApp1.AppHost/Program.cs | 3 +- ...ck.Net.Console.NET6.WithCertificate.csproj | 1 - .../WireMock.Net.OpenTelemetryDemo/Program.cs | 141 ++++++ .../WireMock.Net.OpenTelemetryDemo.csproj | 15 + .../WireMockServerArguments.cs | 30 ++ .../WireMockServerBuilderExtensions.cs | 34 ++ .../ActivityTracing/ActivityTracingOptions.cs | 38 ++ .../ActivityTracingValidator.cs | 34 ++ .../ActivityTracing/WireMockActivitySource.cs | 201 ++++++++ .../WireMockSemanticConventions.cs | 34 ++ .../Owin/IWireMockMiddlewareOptions.cs | 9 + .../Owin/WireMockMiddleware.cs | 47 ++ .../Owin/WireMockMiddlewareOptions.cs | 6 + .../Owin/WireMockMiddlewareOptionsHelper.cs | 22 + .../Server/WireMockServer.cs | 5 - .../Settings/WireMockServerSettingsParser.cs | 16 + .../WireMock.Net.Minimal.csproj | 5 + .../OpenTelemetryOptions.cs | 35 ++ .../OpenTelemetryOptionsParser.cs | 44 ++ src/WireMock.Net.OpenTelemetry/README.md | 160 +++++++ .../WireMock.Net.OpenTelemetry.csproj | 43 ++ .../WireMockOpenTelemetryExtensions.cs | 115 +++++ .../Extensions/DictionaryExtensions.cs | 2 +- .../Properties/AssemblyInfo.cs | 1 + .../Settings/ActivityTracingOptions.cs | 42 ++ .../Settings/SimpleSettingsParser.cs | 17 +- .../Settings/WireMockServerSettings.cs | 11 + src/WireMock.Net.StandAlone/StandAloneApp.cs | 42 ++ .../WireMock.Net.StandAlone.csproj | 10 + src/WireMock.Net/WireMock.Net.csproj | 5 + .../OpenTelemetryOptionsParserTests.cs | 42 ++ .../WireMockOpenTelemetryExtensionsTests.cs | 43 ++ .../WireMockActivitySourceTests.cs | 195 ++++++++ .../Owin/WireMockMiddlewareTests.cs | 92 ++++ .../Settings/SimpleSettingsParserTests.cs | 17 + .../WireMockServerSettingsParserTests.cs | 22 + .../WireMockServer.Settings.cs | 37 ++ 38 files changed, 2057 insertions(+), 9 deletions(-) create mode 100644 examples/WireMock.Net.OpenTelemetryDemo/Program.cs create mode 100644 examples/WireMock.Net.OpenTelemetryDemo/WireMock.Net.OpenTelemetryDemo.csproj create mode 100644 src/WireMock.Net.Minimal/Owin/ActivityTracing/ActivityTracingOptions.cs create mode 100644 src/WireMock.Net.Minimal/Owin/ActivityTracing/ActivityTracingValidator.cs create mode 100644 src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockActivitySource.cs create mode 100644 src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockSemanticConventions.cs create mode 100644 src/WireMock.Net.OpenTelemetry/OpenTelemetryOptions.cs create mode 100644 src/WireMock.Net.OpenTelemetry/OpenTelemetryOptionsParser.cs create mode 100644 src/WireMock.Net.OpenTelemetry/README.md create mode 100644 src/WireMock.Net.OpenTelemetry/WireMock.Net.OpenTelemetry.csproj create mode 100644 src/WireMock.Net.OpenTelemetry/WireMockOpenTelemetryExtensions.cs rename src/{WireMock.Net.Minimal => WireMock.Net.Shared}/Extensions/DictionaryExtensions.cs (99%) create mode 100644 src/WireMock.Net.Shared/Settings/ActivityTracingOptions.cs rename src/{WireMock.Net.Minimal => WireMock.Net.Shared}/Settings/SimpleSettingsParser.cs (94%) create mode 100644 test/WireMock.Net.Tests/OpenTelemetry/OpenTelemetryOptionsParserTests.cs create mode 100644 test/WireMock.Net.Tests/OpenTelemetry/WireMockOpenTelemetryExtensionsTests.cs create mode 100644 test/WireMock.Net.Tests/Owin/ActivityTracing/WireMockActivitySourceTests.cs diff --git a/WireMock.Net Solution.sln b/WireMock.Net Solution.sln index 02034e54..6a3bdd94 100644 --- a/WireMock.Net Solution.sln +++ b/WireMock.Net Solution.sln @@ -148,220 +148,668 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.xUnit.v3", "sr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.NUnit", "src\WireMock.Net.NUnit\WireMock.Net.NUnit.csproj", "{2DBBD70D-8051-441F-92BB-FF9B8B4B4982}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.OpenTelemetry", "src\WireMock.Net.OpenTelemetry\WireMock.Net.OpenTelemetry.csproj", "{C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.OpenTelemetryDemo", "examples\WireMock.Net.OpenTelemetryDemo\WireMock.Net.OpenTelemetryDemo.csproj", "{9957038D-F9C3-CA5D-E8AE-BE188E512635}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D3804228-91F4-4502-9595-39584E5A01AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D3804228-91F4-4502-9595-39584E5A01AD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D3804228-91F4-4502-9595-39584E5A01AD}.Debug|x64.ActiveCfg = Debug|Any CPU + {D3804228-91F4-4502-9595-39584E5A01AD}.Debug|x64.Build.0 = Debug|Any CPU + {D3804228-91F4-4502-9595-39584E5A01AD}.Debug|x86.ActiveCfg = Debug|Any CPU + {D3804228-91F4-4502-9595-39584E5A01AD}.Debug|x86.Build.0 = Debug|Any CPU {D3804228-91F4-4502-9595-39584E5A01AD}.Release|Any CPU.ActiveCfg = Release|Any CPU {D3804228-91F4-4502-9595-39584E5A01AD}.Release|Any CPU.Build.0 = Release|Any CPU + {D3804228-91F4-4502-9595-39584E5A01AD}.Release|x64.ActiveCfg = Release|Any CPU + {D3804228-91F4-4502-9595-39584E5A01AD}.Release|x64.Build.0 = Release|Any CPU + {D3804228-91F4-4502-9595-39584E5A01AD}.Release|x86.ActiveCfg = Release|Any CPU + {D3804228-91F4-4502-9595-39584E5A01AD}.Release|x86.Build.0 = Release|Any CPU {B6269AAC-170A-4346-8B9A-579DED3D9A94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B6269AAC-170A-4346-8B9A-579DED3D9A94}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A94}.Debug|x64.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A94}.Debug|x64.Build.0 = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A94}.Debug|x86.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A94}.Debug|x86.Build.0 = Debug|Any CPU {B6269AAC-170A-4346-8B9A-579DED3D9A94}.Release|Any CPU.ActiveCfg = Release|Any CPU {B6269AAC-170A-4346-8B9A-579DED3D9A94}.Release|Any CPU.Build.0 = Release|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A94}.Release|x64.ActiveCfg = Release|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A94}.Release|x64.Build.0 = Release|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A94}.Release|x86.ActiveCfg = Release|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A94}.Release|x86.Build.0 = Release|Any CPU {B6269AAC-170A-43D6-8B9A-579DED3D9A96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B6269AAC-170A-43D6-8B9A-579DED3D9A96}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6269AAC-170A-43D6-8B9A-579DED3D9A96}.Debug|x64.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-43D6-8B9A-579DED3D9A96}.Debug|x64.Build.0 = Debug|Any CPU + {B6269AAC-170A-43D6-8B9A-579DED3D9A96}.Debug|x86.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-43D6-8B9A-579DED3D9A96}.Debug|x86.Build.0 = Debug|Any CPU {B6269AAC-170A-43D6-8B9A-579DED3D9A96}.Release|Any CPU.ActiveCfg = Release|Any CPU {B6269AAC-170A-43D6-8B9A-579DED3D9A96}.Release|Any CPU.Build.0 = Release|Any CPU + {B6269AAC-170A-43D6-8B9A-579DED3D9A96}.Release|x64.ActiveCfg = Release|Any CPU + {B6269AAC-170A-43D6-8B9A-579DED3D9A96}.Release|x64.Build.0 = Release|Any CPU + {B6269AAC-170A-43D6-8B9A-579DED3D9A96}.Release|x86.ActiveCfg = Release|Any CPU + {B6269AAC-170A-43D6-8B9A-579DED3D9A96}.Release|x86.Build.0 = Release|Any CPU {B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Debug|x64.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Debug|x64.Build.0 = Debug|Any CPU + {B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Debug|x86.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Debug|x86.Build.0 = Debug|Any CPU {B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Release|Any CPU.ActiveCfg = Release|Any CPU {B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Release|Any CPU.Build.0 = Release|Any CPU + {B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Release|x64.ActiveCfg = Release|Any CPU + {B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Release|x64.Build.0 = Release|Any CPU + {B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Release|x86.ActiveCfg = Release|Any CPU + {B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Release|x86.Build.0 = Release|Any CPU {31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Debug|x64.ActiveCfg = Debug|Any CPU + {31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Debug|x64.Build.0 = Debug|Any CPU + {31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Debug|x86.ActiveCfg = Debug|Any CPU + {31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Debug|x86.Build.0 = Debug|Any CPU {31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Release|Any CPU.ActiveCfg = Release|Any CPU {31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Release|Any CPU.Build.0 = Release|Any CPU + {31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Release|x64.ActiveCfg = Release|Any CPU + {31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Release|x64.Build.0 = Release|Any CPU + {31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Release|x86.ActiveCfg = Release|Any CPU + {31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Release|x86.Build.0 = Release|Any CPU {74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Debug|x64.ActiveCfg = Debug|Any CPU + {74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Debug|x64.Build.0 = Debug|Any CPU + {74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Debug|x86.ActiveCfg = Debug|Any CPU + {74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Debug|x86.Build.0 = Debug|Any CPU {74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Release|Any CPU.ActiveCfg = Release|Any CPU {74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Release|Any CPU.Build.0 = Release|Any CPU + {74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Release|x64.ActiveCfg = Release|Any CPU + {74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Release|x64.Build.0 = Release|Any CPU + {74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Release|x86.ActiveCfg = Release|Any CPU + {74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Release|x86.Build.0 = Release|Any CPU {7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x64.ActiveCfg = Debug|Any CPU + {7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x64.Build.0 = Debug|Any CPU + {7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x86.ActiveCfg = Debug|Any CPU + {7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x86.Build.0 = Debug|Any CPU {7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|Any CPU.ActiveCfg = Release|Any CPU {7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|Any CPU.Build.0 = Release|Any CPU + {7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x64.ActiveCfg = Release|Any CPU + {7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x64.Build.0 = Release|Any CPU + {7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x86.ActiveCfg = Release|Any CPU + {7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x86.Build.0 = Release|Any CPU {A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|x64.ActiveCfg = Debug|Any CPU + {A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|x64.Build.0 = Debug|Any CPU + {A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|x86.ActiveCfg = Debug|Any CPU + {A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|x86.Build.0 = Debug|Any CPU {A9D039B9-7509-4CF1-9EFD-87EB82998575}.Release|Any CPU.ActiveCfg = Release|Any CPU {A9D039B9-7509-4CF1-9EFD-87EB82998575}.Release|Any CPU.Build.0 = Release|Any CPU + {A9D039B9-7509-4CF1-9EFD-87EB82998575}.Release|x64.ActiveCfg = Release|Any CPU + {A9D039B9-7509-4CF1-9EFD-87EB82998575}.Release|x64.Build.0 = Release|Any CPU + {A9D039B9-7509-4CF1-9EFD-87EB82998575}.Release|x86.ActiveCfg = Release|Any CPU + {A9D039B9-7509-4CF1-9EFD-87EB82998575}.Release|x86.Build.0 = Release|Any CPU {5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Debug|x64.ActiveCfg = Debug|Any CPU + {5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Debug|x64.Build.0 = Debug|Any CPU + {5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Debug|x86.ActiveCfg = Debug|Any CPU + {5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Debug|x86.Build.0 = Debug|Any CPU {5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Release|Any CPU.ActiveCfg = Release|Any CPU {5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Release|Any CPU.Build.0 = Release|Any CPU + {5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Release|x64.ActiveCfg = Release|Any CPU + {5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Release|x64.Build.0 = Release|Any CPU + {5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Release|x86.ActiveCfg = Release|Any CPU + {5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Release|x86.Build.0 = Release|Any CPU {B6269AAC-170A-4346-8B9A-579DED3D9A95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B6269AAC-170A-4346-8B9A-579DED3D9A95}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A95}.Debug|x64.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A95}.Debug|x64.Build.0 = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A95}.Debug|x86.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A95}.Debug|x86.Build.0 = Debug|Any CPU {B6269AAC-170A-4346-8B9A-579DED3D9A95}.Release|Any CPU.ActiveCfg = Release|Any CPU {B6269AAC-170A-4346-8B9A-579DED3D9A95}.Release|Any CPU.Build.0 = Release|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A95}.Release|x64.ActiveCfg = Release|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A95}.Release|x64.Build.0 = Release|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A95}.Release|x86.ActiveCfg = Release|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A95}.Release|x86.Build.0 = Release|Any CPU {40BF24B5-12E6-4610-9489-138798632E28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {40BF24B5-12E6-4610-9489-138798632E28}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40BF24B5-12E6-4610-9489-138798632E28}.Debug|x64.ActiveCfg = Debug|Any CPU + {40BF24B5-12E6-4610-9489-138798632E28}.Debug|x64.Build.0 = Debug|Any CPU + {40BF24B5-12E6-4610-9489-138798632E28}.Debug|x86.ActiveCfg = Debug|Any CPU + {40BF24B5-12E6-4610-9489-138798632E28}.Debug|x86.Build.0 = Debug|Any CPU {40BF24B5-12E6-4610-9489-138798632E28}.Release|Any CPU.ActiveCfg = Release|Any CPU {40BF24B5-12E6-4610-9489-138798632E28}.Release|Any CPU.Build.0 = Release|Any CPU + {40BF24B5-12E6-4610-9489-138798632E28}.Release|x64.ActiveCfg = Release|Any CPU + {40BF24B5-12E6-4610-9489-138798632E28}.Release|x64.Build.0 = Release|Any CPU + {40BF24B5-12E6-4610-9489-138798632E28}.Release|x86.ActiveCfg = Release|Any CPU + {40BF24B5-12E6-4610-9489-138798632E28}.Release|x86.Build.0 = Release|Any CPU {B6269AAC-170A-4346-8B9A-444DED3D9A44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B6269AAC-170A-4346-8B9A-444DED3D9A44}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A44}.Debug|x64.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A44}.Debug|x64.Build.0 = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A44}.Debug|x86.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A44}.Debug|x86.Build.0 = Debug|Any CPU {B6269AAC-170A-4346-8B9A-444DED3D9A44}.Release|Any CPU.ActiveCfg = Release|Any CPU {B6269AAC-170A-4346-8B9A-444DED3D9A44}.Release|Any CPU.Build.0 = Release|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A44}.Release|x64.ActiveCfg = Release|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A44}.Release|x64.Build.0 = Release|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A44}.Release|x86.ActiveCfg = Release|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A44}.Release|x86.Build.0 = Release|Any CPU {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Debug|x64.ActiveCfg = Debug|Any CPU + {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Debug|x64.Build.0 = Debug|Any CPU + {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Debug|x86.ActiveCfg = Debug|Any CPU + {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Debug|x86.Build.0 = Debug|Any CPU {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Release|Any CPU.ActiveCfg = Release|Any CPU {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Release|Any CPU.Build.0 = Release|Any CPU + {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Release|x64.ActiveCfg = Release|Any CPU + {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Release|x64.Build.0 = Release|Any CPU + {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Release|x86.ActiveCfg = Release|Any CPU + {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Release|x86.Build.0 = Release|Any CPU {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Debug|x64.ActiveCfg = Debug|Any CPU + {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Debug|x64.Build.0 = Debug|Any CPU + {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Debug|x86.ActiveCfg = Debug|Any CPU + {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Debug|x86.Build.0 = Debug|Any CPU {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Release|Any CPU.ActiveCfg = Release|Any CPU {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Release|Any CPU.Build.0 = Release|Any CPU + {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Release|x64.ActiveCfg = Release|Any CPU + {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Release|x64.Build.0 = Release|Any CPU + {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Release|x86.ActiveCfg = Release|Any CPU + {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Release|x86.Build.0 = Release|Any CPU {3F7AA023-6833-4856-A08A-4B5717B592B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3F7AA023-6833-4856-A08A-4B5717B592B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F7AA023-6833-4856-A08A-4B5717B592B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {3F7AA023-6833-4856-A08A-4B5717B592B8}.Debug|x64.Build.0 = Debug|Any CPU + {3F7AA023-6833-4856-A08A-4B5717B592B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {3F7AA023-6833-4856-A08A-4B5717B592B8}.Debug|x86.Build.0 = Debug|Any CPU {3F7AA023-6833-4856-A08A-4B5717B592B8}.Release|Any CPU.ActiveCfg = Release|Any CPU {3F7AA023-6833-4856-A08A-4B5717B592B8}.Release|Any CPU.Build.0 = Release|Any CPU + {3F7AA023-6833-4856-A08A-4B5717B592B8}.Release|x64.ActiveCfg = Release|Any CPU + {3F7AA023-6833-4856-A08A-4B5717B592B8}.Release|x64.Build.0 = Release|Any CPU + {3F7AA023-6833-4856-A08A-4B5717B592B8}.Release|x86.ActiveCfg = Release|Any CPU + {3F7AA023-6833-4856-A08A-4B5717B592B8}.Release|x86.Build.0 = Release|Any CPU {670C7562-C154-442E-A249-7D26849BCD13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {670C7562-C154-442E-A249-7D26849BCD13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {670C7562-C154-442E-A249-7D26849BCD13}.Debug|x64.ActiveCfg = Debug|Any CPU + {670C7562-C154-442E-A249-7D26849BCD13}.Debug|x64.Build.0 = Debug|Any CPU + {670C7562-C154-442E-A249-7D26849BCD13}.Debug|x86.ActiveCfg = Debug|Any CPU + {670C7562-C154-442E-A249-7D26849BCD13}.Debug|x86.Build.0 = Debug|Any CPU {670C7562-C154-442E-A249-7D26849BCD13}.Release|Any CPU.ActiveCfg = Release|Any CPU {670C7562-C154-442E-A249-7D26849BCD13}.Release|Any CPU.Build.0 = Release|Any CPU + {670C7562-C154-442E-A249-7D26849BCD13}.Release|x64.ActiveCfg = Release|Any CPU + {670C7562-C154-442E-A249-7D26849BCD13}.Release|x64.Build.0 = Release|Any CPU + {670C7562-C154-442E-A249-7D26849BCD13}.Release|x86.ActiveCfg = Release|Any CPU + {670C7562-C154-442E-A249-7D26849BCD13}.Release|x86.Build.0 = Release|Any CPU {0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Debug|x64.ActiveCfg = Debug|Any CPU + {0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Debug|x64.Build.0 = Debug|Any CPU + {0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Debug|x86.ActiveCfg = Debug|Any CPU + {0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Debug|x86.Build.0 = Debug|Any CPU {0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Release|Any CPU.ActiveCfg = Release|Any CPU {0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Release|Any CPU.Build.0 = Release|Any CPU + {0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Release|x64.ActiveCfg = Release|Any CPU + {0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Release|x64.Build.0 = Release|Any CPU + {0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Release|x86.ActiveCfg = Release|Any CPU + {0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Release|x86.Build.0 = Release|Any CPU {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Debug|x64.ActiveCfg = Debug|Any CPU + {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Debug|x64.Build.0 = Debug|Any CPU + {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Debug|x86.ActiveCfg = Debug|Any CPU + {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Debug|x86.Build.0 = Debug|Any CPU {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|Any CPU.ActiveCfg = Release|Any CPU {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|Any CPU.Build.0 = Release|Any CPU + {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|x64.ActiveCfg = Release|Any CPU + {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|x64.Build.0 = Release|Any CPU + {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|x86.ActiveCfg = Release|Any CPU + {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|x86.Build.0 = Release|Any CPU {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Debug|x64.Build.0 = Debug|Any CPU + {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Debug|x86.Build.0 = Debug|Any CPU {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Release|Any CPU.ActiveCfg = Release|Any CPU {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Release|x64.ActiveCfg = Release|Any CPU + {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Release|x64.Build.0 = Release|Any CPU + {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Release|x86.ActiveCfg = Release|Any CPU + {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Release|x86.Build.0 = Release|Any CPU {12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Debug|x64.ActiveCfg = Debug|Any CPU + {12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Debug|x64.Build.0 = Debug|Any CPU + {12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Debug|x86.ActiveCfg = Debug|Any CPU + {12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Debug|x86.Build.0 = Debug|Any CPU {12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Release|Any CPU.ActiveCfg = Release|Any CPU {12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Release|Any CPU.Build.0 = Release|Any CPU + {12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Release|x64.ActiveCfg = Release|Any CPU + {12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Release|x64.Build.0 = Release|Any CPU + {12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Release|x86.ActiveCfg = Release|Any CPU + {12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Release|x86.Build.0 = Release|Any CPU {56A38798-C48B-4A4A-B805-071E05C02CE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {56A38798-C48B-4A4A-B805-071E05C02CE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {56A38798-C48B-4A4A-B805-071E05C02CE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {56A38798-C48B-4A4A-B805-071E05C02CE1}.Debug|x64.Build.0 = Debug|Any CPU + {56A38798-C48B-4A4A-B805-071E05C02CE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {56A38798-C48B-4A4A-B805-071E05C02CE1}.Debug|x86.Build.0 = Debug|Any CPU {56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|Any CPU.ActiveCfg = Release|Any CPU {56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|Any CPU.Build.0 = Release|Any CPU + {56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|x64.ActiveCfg = Release|Any CPU + {56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|x64.Build.0 = Release|Any CPU + {56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|x86.ActiveCfg = Release|Any CPU + {56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|x86.Build.0 = Release|Any CPU {07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|x64.ActiveCfg = Debug|Any CPU + {07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|x64.Build.0 = Debug|Any CPU + {07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|x86.ActiveCfg = Debug|Any CPU + {07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|x86.Build.0 = Debug|Any CPU {07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|Any CPU.ActiveCfg = Release|Any CPU {07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|Any CPU.Build.0 = Release|Any CPU + {07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|x64.ActiveCfg = Release|Any CPU + {07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|x64.Build.0 = Release|Any CPU + {07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|x86.ActiveCfg = Release|Any CPU + {07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|x86.Build.0 = Release|Any CPU {1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Debug|x64.ActiveCfg = Debug|Any CPU + {1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Debug|x64.Build.0 = Debug|Any CPU + {1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Debug|x86.ActiveCfg = Debug|Any CPU + {1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Debug|x86.Build.0 = Debug|Any CPU {1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Release|Any CPU.ActiveCfg = Release|Any CPU {1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Release|Any CPU.Build.0 = Release|Any CPU + {1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Release|x64.ActiveCfg = Release|Any CPU + {1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Release|x64.Build.0 = Release|Any CPU + {1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Release|x86.ActiveCfg = Release|Any CPU + {1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Release|x86.Build.0 = Release|Any CPU {7FC0B409-2682-40EE-B3B9-3930D6769D01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7FC0B409-2682-40EE-B3B9-3930D6769D01}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7FC0B409-2682-40EE-B3B9-3930D6769D01}.Debug|x64.ActiveCfg = Debug|Any CPU + {7FC0B409-2682-40EE-B3B9-3930D6769D01}.Debug|x64.Build.0 = Debug|Any CPU + {7FC0B409-2682-40EE-B3B9-3930D6769D01}.Debug|x86.ActiveCfg = Debug|Any CPU + {7FC0B409-2682-40EE-B3B9-3930D6769D01}.Debug|x86.Build.0 = Debug|Any CPU {7FC0B409-2682-40EE-B3B9-3930D6769D01}.Release|Any CPU.ActiveCfg = Release|Any CPU {7FC0B409-2682-40EE-B3B9-3930D6769D01}.Release|Any CPU.Build.0 = Release|Any CPU + {7FC0B409-2682-40EE-B3B9-3930D6769D01}.Release|x64.ActiveCfg = Release|Any CPU + {7FC0B409-2682-40EE-B3B9-3930D6769D01}.Release|x64.Build.0 = Release|Any CPU + {7FC0B409-2682-40EE-B3B9-3930D6769D01}.Release|x86.ActiveCfg = Release|Any CPU + {7FC0B409-2682-40EE-B3B9-3930D6769D01}.Release|x86.Build.0 = Release|Any CPU {B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Debug|x64.ActiveCfg = Debug|Any CPU + {B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Debug|x64.Build.0 = Debug|Any CPU + {B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Debug|x86.ActiveCfg = Debug|Any CPU + {B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Debug|x86.Build.0 = Debug|Any CPU {B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Release|Any CPU.ActiveCfg = Release|Any CPU {B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Release|Any CPU.Build.0 = Release|Any CPU + {B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Release|x64.ActiveCfg = Release|Any CPU + {B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Release|x64.Build.0 = Release|Any CPU + {B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Release|x86.ActiveCfg = Release|Any CPU + {B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Release|x86.Build.0 = Release|Any CPU {CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Debug|x64.ActiveCfg = Debug|Any CPU + {CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Debug|x64.Build.0 = Debug|Any CPU + {CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Debug|x86.ActiveCfg = Debug|Any CPU + {CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Debug|x86.Build.0 = Debug|Any CPU {CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Release|Any CPU.ActiveCfg = Release|Any CPU {CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Release|Any CPU.Build.0 = Release|Any CPU + {CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Release|x64.ActiveCfg = Release|Any CPU + {CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Release|x64.Build.0 = Release|Any CPU + {CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Release|x86.ActiveCfg = Release|Any CPU + {CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Release|x86.Build.0 = Release|Any CPU {42113E6B-DC43-4E80-9967-1E4233568E87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {42113E6B-DC43-4E80-9967-1E4233568E87}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42113E6B-DC43-4E80-9967-1E4233568E87}.Debug|x64.ActiveCfg = Debug|Any CPU + {42113E6B-DC43-4E80-9967-1E4233568E87}.Debug|x64.Build.0 = Debug|Any CPU + {42113E6B-DC43-4E80-9967-1E4233568E87}.Debug|x86.ActiveCfg = Debug|Any CPU + {42113E6B-DC43-4E80-9967-1E4233568E87}.Debug|x86.Build.0 = Debug|Any CPU {42113E6B-DC43-4E80-9967-1E4233568E87}.Release|Any CPU.ActiveCfg = Release|Any CPU {42113E6B-DC43-4E80-9967-1E4233568E87}.Release|Any CPU.Build.0 = Release|Any CPU + {42113E6B-DC43-4E80-9967-1E4233568E87}.Release|x64.ActiveCfg = Release|Any CPU + {42113E6B-DC43-4E80-9967-1E4233568E87}.Release|x64.Build.0 = Release|Any CPU + {42113E6B-DC43-4E80-9967-1E4233568E87}.Release|x86.ActiveCfg = Release|Any CPU + {42113E6B-DC43-4E80-9967-1E4233568E87}.Release|x86.Build.0 = Release|Any CPU {84624E1F-DF07-4315-89B0-51776BE99E13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {84624E1F-DF07-4315-89B0-51776BE99E13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {84624E1F-DF07-4315-89B0-51776BE99E13}.Debug|x64.ActiveCfg = Debug|Any CPU + {84624E1F-DF07-4315-89B0-51776BE99E13}.Debug|x64.Build.0 = Debug|Any CPU + {84624E1F-DF07-4315-89B0-51776BE99E13}.Debug|x86.ActiveCfg = Debug|Any CPU + {84624E1F-DF07-4315-89B0-51776BE99E13}.Debug|x86.Build.0 = Debug|Any CPU {84624E1F-DF07-4315-89B0-51776BE99E13}.Release|Any CPU.ActiveCfg = Release|Any CPU {84624E1F-DF07-4315-89B0-51776BE99E13}.Release|Any CPU.Build.0 = Release|Any CPU + {84624E1F-DF07-4315-89B0-51776BE99E13}.Release|x64.ActiveCfg = Release|Any CPU + {84624E1F-DF07-4315-89B0-51776BE99E13}.Release|x64.Build.0 = Release|Any CPU + {84624E1F-DF07-4315-89B0-51776BE99E13}.Release|x86.ActiveCfg = Release|Any CPU + {84624E1F-DF07-4315-89B0-51776BE99E13}.Release|x86.Build.0 = Release|Any CPU {A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Debug|x64.ActiveCfg = Debug|Any CPU + {A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Debug|x64.Build.0 = Debug|Any CPU + {A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Debug|x86.ActiveCfg = Debug|Any CPU + {A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Debug|x86.Build.0 = Debug|Any CPU {A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Release|Any CPU.ActiveCfg = Release|Any CPU {A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Release|Any CPU.Build.0 = Release|Any CPU + {A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Release|x64.ActiveCfg = Release|Any CPU + {A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Release|x64.Build.0 = Release|Any CPU + {A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Release|x86.ActiveCfg = Release|Any CPU + {A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Release|x86.Build.0 = Release|Any CPU {7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Debug|x64.ActiveCfg = Debug|Any CPU + {7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Debug|x64.Build.0 = Debug|Any CPU + {7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Debug|x86.ActiveCfg = Debug|Any CPU + {7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Debug|x86.Build.0 = Debug|Any CPU {7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Release|Any CPU.ActiveCfg = Release|Any CPU {7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Release|Any CPU.Build.0 = Release|Any CPU + {7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Release|x64.ActiveCfg = Release|Any CPU + {7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Release|x64.Build.0 = Release|Any CPU + {7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Release|x86.ActiveCfg = Release|Any CPU + {7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Release|x86.Build.0 = Release|Any CPU {CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Debug|x64.ActiveCfg = Debug|Any CPU + {CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Debug|x64.Build.0 = Debug|Any CPU + {CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Debug|x86.ActiveCfg = Debug|Any CPU + {CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Debug|x86.Build.0 = Debug|Any CPU {CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Release|Any CPU.ActiveCfg = Release|Any CPU {CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Release|Any CPU.Build.0 = Release|Any CPU + {CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Release|x64.ActiveCfg = Release|Any CPU + {CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Release|x64.Build.0 = Release|Any CPU + {CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Release|x86.ActiveCfg = Release|Any CPU + {CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Release|x86.Build.0 = Release|Any CPU {F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Debug|x64.ActiveCfg = Debug|Any CPU + {F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Debug|x64.Build.0 = Debug|Any CPU + {F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Debug|x86.ActiveCfg = Debug|Any CPU + {F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Debug|x86.Build.0 = Debug|Any CPU {F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Release|Any CPU.ActiveCfg = Release|Any CPU {F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Release|Any CPU.Build.0 = Release|Any CPU + {F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Release|x64.ActiveCfg = Release|Any CPU + {F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Release|x64.Build.0 = Release|Any CPU + {F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Release|x86.ActiveCfg = Release|Any CPU + {F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Release|x86.Build.0 = Release|Any CPU {C9210DA3-F390-4598-8512-349A473FE9C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C9210DA3-F390-4598-8512-349A473FE9C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C9210DA3-F390-4598-8512-349A473FE9C9}.Debug|x64.ActiveCfg = Debug|Any CPU + {C9210DA3-F390-4598-8512-349A473FE9C9}.Debug|x64.Build.0 = Debug|Any CPU + {C9210DA3-F390-4598-8512-349A473FE9C9}.Debug|x86.ActiveCfg = Debug|Any CPU + {C9210DA3-F390-4598-8512-349A473FE9C9}.Debug|x86.Build.0 = Debug|Any CPU {C9210DA3-F390-4598-8512-349A473FE9C9}.Release|Any CPU.ActiveCfg = Release|Any CPU {C9210DA3-F390-4598-8512-349A473FE9C9}.Release|Any CPU.Build.0 = Release|Any CPU + {C9210DA3-F390-4598-8512-349A473FE9C9}.Release|x64.ActiveCfg = Release|Any CPU + {C9210DA3-F390-4598-8512-349A473FE9C9}.Release|x64.Build.0 = Release|Any CPU + {C9210DA3-F390-4598-8512-349A473FE9C9}.Release|x86.ActiveCfg = Release|Any CPU + {C9210DA3-F390-4598-8512-349A473FE9C9}.Release|x86.Build.0 = Release|Any CPU {91024A93-848F-4A02-AF53-5EBE5834E23C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91024A93-848F-4A02-AF53-5EBE5834E23C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91024A93-848F-4A02-AF53-5EBE5834E23C}.Debug|x64.ActiveCfg = Debug|Any CPU + {91024A93-848F-4A02-AF53-5EBE5834E23C}.Debug|x64.Build.0 = Debug|Any CPU + {91024A93-848F-4A02-AF53-5EBE5834E23C}.Debug|x86.ActiveCfg = Debug|Any CPU + {91024A93-848F-4A02-AF53-5EBE5834E23C}.Debug|x86.Build.0 = Debug|Any CPU {91024A93-848F-4A02-AF53-5EBE5834E23C}.Release|Any CPU.ActiveCfg = Release|Any CPU {91024A93-848F-4A02-AF53-5EBE5834E23C}.Release|Any CPU.Build.0 = Release|Any CPU + {91024A93-848F-4A02-AF53-5EBE5834E23C}.Release|x64.ActiveCfg = Release|Any CPU + {91024A93-848F-4A02-AF53-5EBE5834E23C}.Release|x64.Build.0 = Release|Any CPU + {91024A93-848F-4A02-AF53-5EBE5834E23C}.Release|x86.ActiveCfg = Release|Any CPU + {91024A93-848F-4A02-AF53-5EBE5834E23C}.Release|x86.Build.0 = Release|Any CPU {4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Debug|x64.ActiveCfg = Debug|Any CPU + {4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Debug|x64.Build.0 = Debug|Any CPU + {4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Debug|x86.ActiveCfg = Debug|Any CPU + {4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Debug|x86.Build.0 = Debug|Any CPU {4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Release|Any CPU.ActiveCfg = Release|Any CPU {4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Release|Any CPU.Build.0 = Release|Any CPU + {4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Release|x64.ActiveCfg = Release|Any CPU + {4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Release|x64.Build.0 = Release|Any CPU + {4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Release|x86.ActiveCfg = Release|Any CPU + {4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Release|x86.Build.0 = Release|Any CPU {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Debug|x64.ActiveCfg = Debug|Any CPU + {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Debug|x64.Build.0 = Debug|Any CPU + {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Debug|x86.ActiveCfg = Debug|Any CPU + {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Debug|x86.Build.0 = Debug|Any CPU {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Release|Any CPU.ActiveCfg = Release|Any CPU {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Release|Any CPU.Build.0 = Release|Any CPU + {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Release|x64.ActiveCfg = Release|Any CPU + {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Release|x64.Build.0 = Release|Any CPU + {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Release|x86.ActiveCfg = Release|Any CPU + {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Release|x86.Build.0 = Release|Any CPU {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Debug|x64.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Debug|x64.Build.0 = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Debug|x86.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Debug|x86.Build.0 = Debug|Any CPU {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Release|Any CPU.ActiveCfg = Release|Any CPU {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Release|Any CPU.Build.0 = Release|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Release|x64.ActiveCfg = Release|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Release|x64.Build.0 = Release|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Release|x86.ActiveCfg = Release|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Release|x86.Build.0 = Release|Any CPU {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Debug|x64.ActiveCfg = Debug|Any CPU + {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Debug|x64.Build.0 = Debug|Any CPU + {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Debug|x86.ActiveCfg = Debug|Any CPU + {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Debug|x86.Build.0 = Debug|Any CPU {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Release|Any CPU.ActiveCfg = Release|Any CPU {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Release|Any CPU.Build.0 = Release|Any CPU + {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Release|x64.ActiveCfg = Release|Any CPU + {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Release|x64.Build.0 = Release|Any CPU + {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Release|x86.ActiveCfg = Release|Any CPU + {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Release|x86.Build.0 = Release|Any CPU {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Debug|x64.ActiveCfg = Debug|Any CPU + {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Debug|x64.Build.0 = Debug|Any CPU + {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Debug|x86.ActiveCfg = Debug|Any CPU + {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Debug|x86.Build.0 = Debug|Any CPU {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Release|Any CPU.Build.0 = Release|Any CPU + {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Release|x64.ActiveCfg = Release|Any CPU + {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Release|x64.Build.0 = Release|Any CPU + {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Release|x86.ActiveCfg = Release|Any CPU + {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Release|x86.Build.0 = Release|Any CPU {7753670F-7C7F-44BF-8BC7-08325588E60C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7753670F-7C7F-44BF-8BC7-08325588E60C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7753670F-7C7F-44BF-8BC7-08325588E60C}.Debug|x64.ActiveCfg = Debug|Any CPU + {7753670F-7C7F-44BF-8BC7-08325588E60C}.Debug|x64.Build.0 = Debug|Any CPU + {7753670F-7C7F-44BF-8BC7-08325588E60C}.Debug|x86.ActiveCfg = Debug|Any CPU + {7753670F-7C7F-44BF-8BC7-08325588E60C}.Debug|x86.Build.0 = Debug|Any CPU {7753670F-7C7F-44BF-8BC7-08325588E60C}.Release|Any CPU.ActiveCfg = Release|Any CPU {7753670F-7C7F-44BF-8BC7-08325588E60C}.Release|Any CPU.Build.0 = Release|Any CPU + {7753670F-7C7F-44BF-8BC7-08325588E60C}.Release|x64.ActiveCfg = Release|Any CPU + {7753670F-7C7F-44BF-8BC7-08325588E60C}.Release|x64.Build.0 = Release|Any CPU + {7753670F-7C7F-44BF-8BC7-08325588E60C}.Release|x86.ActiveCfg = Release|Any CPU + {7753670F-7C7F-44BF-8BC7-08325588E60C}.Release|x86.Build.0 = Release|Any CPU {E5B03EEF-822C-4295-952B-4479AD30082B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E5B03EEF-822C-4295-952B-4479AD30082B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5B03EEF-822C-4295-952B-4479AD30082B}.Debug|x64.ActiveCfg = Debug|Any CPU + {E5B03EEF-822C-4295-952B-4479AD30082B}.Debug|x64.Build.0 = Debug|Any CPU + {E5B03EEF-822C-4295-952B-4479AD30082B}.Debug|x86.ActiveCfg = Debug|Any CPU + {E5B03EEF-822C-4295-952B-4479AD30082B}.Debug|x86.Build.0 = Debug|Any CPU {E5B03EEF-822C-4295-952B-4479AD30082B}.Release|Any CPU.ActiveCfg = Release|Any CPU {E5B03EEF-822C-4295-952B-4479AD30082B}.Release|Any CPU.Build.0 = Release|Any CPU + {E5B03EEF-822C-4295-952B-4479AD30082B}.Release|x64.ActiveCfg = Release|Any CPU + {E5B03EEF-822C-4295-952B-4479AD30082B}.Release|x64.Build.0 = Release|Any CPU + {E5B03EEF-822C-4295-952B-4479AD30082B}.Release|x86.ActiveCfg = Release|Any CPU + {E5B03EEF-822C-4295-952B-4479AD30082B}.Release|x86.Build.0 = Release|Any CPU {F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Debug|x64.ActiveCfg = Debug|Any CPU + {F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Debug|x64.Build.0 = Debug|Any CPU + {F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Debug|x86.ActiveCfg = Debug|Any CPU + {F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Debug|x86.Build.0 = Debug|Any CPU {F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Release|Any CPU.ActiveCfg = Release|Any CPU {F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Release|Any CPU.Build.0 = Release|Any CPU + {F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Release|x64.ActiveCfg = Release|Any CPU + {F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Release|x64.Build.0 = Release|Any CPU + {F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Release|x86.ActiveCfg = Release|Any CPU + {F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Release|x86.Build.0 = Release|Any CPU {D3804228-91F4-4502-9595-39584E5A0177}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D3804228-91F4-4502-9595-39584E5A0177}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D3804228-91F4-4502-9595-39584E5A0177}.Debug|x64.ActiveCfg = Debug|Any CPU + {D3804228-91F4-4502-9595-39584E5A0177}.Debug|x64.Build.0 = Debug|Any CPU + {D3804228-91F4-4502-9595-39584E5A0177}.Debug|x86.ActiveCfg = Debug|Any CPU + {D3804228-91F4-4502-9595-39584E5A0177}.Debug|x86.Build.0 = Debug|Any CPU {D3804228-91F4-4502-9595-39584E5A0177}.Release|Any CPU.ActiveCfg = Release|Any CPU {D3804228-91F4-4502-9595-39584E5A0177}.Release|Any CPU.Build.0 = Release|Any CPU + {D3804228-91F4-4502-9595-39584E5A0177}.Release|x64.ActiveCfg = Release|Any CPU + {D3804228-91F4-4502-9595-39584E5A0177}.Release|x64.Build.0 = Release|Any CPU + {D3804228-91F4-4502-9595-39584E5A0177}.Release|x86.ActiveCfg = Release|Any CPU + {D3804228-91F4-4502-9595-39584E5A0177}.Release|x86.Build.0 = Release|Any CPU {BFEF8990-65B3-4274-310F-7355F0B84035}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BFEF8990-65B3-4274-310F-7355F0B84035}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BFEF8990-65B3-4274-310F-7355F0B84035}.Debug|x64.ActiveCfg = Debug|Any CPU + {BFEF8990-65B3-4274-310F-7355F0B84035}.Debug|x64.Build.0 = Debug|Any CPU + {BFEF8990-65B3-4274-310F-7355F0B84035}.Debug|x86.ActiveCfg = Debug|Any CPU + {BFEF8990-65B3-4274-310F-7355F0B84035}.Debug|x86.Build.0 = Debug|Any CPU {BFEF8990-65B3-4274-310F-7355F0B84035}.Release|Any CPU.ActiveCfg = Release|Any CPU {BFEF8990-65B3-4274-310F-7355F0B84035}.Release|Any CPU.Build.0 = Release|Any CPU + {BFEF8990-65B3-4274-310F-7355F0B84035}.Release|x64.ActiveCfg = Release|Any CPU + {BFEF8990-65B3-4274-310F-7355F0B84035}.Release|x64.Build.0 = Release|Any CPU + {BFEF8990-65B3-4274-310F-7355F0B84035}.Release|x86.ActiveCfg = Release|Any CPU + {BFEF8990-65B3-4274-310F-7355F0B84035}.Release|x86.Build.0 = Release|Any CPU {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Debug|x64.ActiveCfg = Debug|Any CPU + {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Debug|x64.Build.0 = Debug|Any CPU + {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Debug|x86.ActiveCfg = Debug|Any CPU + {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Debug|x86.Build.0 = Debug|Any CPU {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Release|Any CPU.Build.0 = Release|Any CPU + {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Release|x64.ActiveCfg = Release|Any CPU + {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Release|x64.Build.0 = Release|Any CPU + {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Release|x86.ActiveCfg = Release|Any CPU + {1F80A6E6-D146-4E40-9EA8-49DB8494239F}.Release|x86.Build.0 = Release|Any CPU {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Debug|x64.ActiveCfg = Debug|Any CPU + {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Debug|x64.Build.0 = Debug|Any CPU + {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Debug|x86.ActiveCfg = Debug|Any CPU + {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Debug|x86.Build.0 = Debug|Any CPU {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Release|Any CPU.ActiveCfg = Release|Any CPU {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Release|Any CPU.Build.0 = Release|Any CPU + {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Release|x64.ActiveCfg = Release|Any CPU + {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Release|x64.Build.0 = Release|Any CPU + {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Release|x86.ActiveCfg = Release|Any CPU + {BBA332C6-28A9-42E7-9C4D-A0816E52A198}.Release|x86.Build.0 = Release|Any CPU {B6269AAC-170A-4346-8B9A-444DED3D9A45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B6269AAC-170A-4346-8B9A-444DED3D9A45}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A45}.Debug|x64.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A45}.Debug|x64.Build.0 = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A45}.Debug|x86.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A45}.Debug|x86.Build.0 = Debug|Any CPU {B6269AAC-170A-4346-8B9A-444DED3D9A45}.Release|Any CPU.ActiveCfg = Release|Any CPU {B6269AAC-170A-4346-8B9A-444DED3D9A45}.Release|Any CPU.Build.0 = Release|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A45}.Release|x64.ActiveCfg = Release|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A45}.Release|x64.Build.0 = Release|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A45}.Release|x86.ActiveCfg = Release|Any CPU + {B6269AAC-170A-4346-8B9A-444DED3D9A45}.Release|x86.Build.0 = Release|Any CPU {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Debug|x64.ActiveCfg = Debug|Any CPU + {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Debug|x64.Build.0 = Debug|Any CPU + {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Debug|x86.ActiveCfg = Debug|Any CPU + {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Debug|x86.Build.0 = Debug|Any CPU {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Release|Any CPU.ActiveCfg = Release|Any CPU {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Release|Any CPU.Build.0 = Release|Any CPU + {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Release|x64.ActiveCfg = Release|Any CPU + {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Release|x64.Build.0 = Release|Any CPU + {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Release|x86.ActiveCfg = Release|Any CPU + {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Release|x86.Build.0 = Release|Any CPU {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Debug|x64.ActiveCfg = Debug|Any CPU + {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Debug|x64.Build.0 = Debug|Any CPU + {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Debug|x86.ActiveCfg = Debug|Any CPU + {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Debug|x86.Build.0 = Debug|Any CPU {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Release|Any CPU.ActiveCfg = Release|Any CPU {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Release|Any CPU.Build.0 = Release|Any CPU + {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Release|x64.ActiveCfg = Release|Any CPU + {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Release|x64.Build.0 = Release|Any CPU + {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Release|x86.ActiveCfg = Release|Any CPU + {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Release|x86.Build.0 = Release|Any CPU {B47413AA-55D3-49A7-896A-17ADBFF72407}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B47413AA-55D3-49A7-896A-17ADBFF72407}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B47413AA-55D3-49A7-896A-17ADBFF72407}.Debug|x64.ActiveCfg = Debug|Any CPU + {B47413AA-55D3-49A7-896A-17ADBFF72407}.Debug|x64.Build.0 = Debug|Any CPU + {B47413AA-55D3-49A7-896A-17ADBFF72407}.Debug|x86.ActiveCfg = Debug|Any CPU + {B47413AA-55D3-49A7-896A-17ADBFF72407}.Debug|x86.Build.0 = Debug|Any CPU {B47413AA-55D3-49A7-896A-17ADBFF72407}.Release|Any CPU.ActiveCfg = Release|Any CPU {B47413AA-55D3-49A7-896A-17ADBFF72407}.Release|Any CPU.Build.0 = Release|Any CPU + {B47413AA-55D3-49A7-896A-17ADBFF72407}.Release|x64.ActiveCfg = Release|Any CPU + {B47413AA-55D3-49A7-896A-17ADBFF72407}.Release|x64.Build.0 = Release|Any CPU + {B47413AA-55D3-49A7-896A-17ADBFF72407}.Release|x86.ActiveCfg = Release|Any CPU + {B47413AA-55D3-49A7-896A-17ADBFF72407}.Release|x86.Build.0 = Release|Any CPU {4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Debug|x64.ActiveCfg = Debug|Any CPU + {4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Debug|x64.Build.0 = Debug|Any CPU + {4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Debug|x86.ActiveCfg = Debug|Any CPU + {4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Debug|x86.Build.0 = Debug|Any CPU {4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Release|Any CPU.ActiveCfg = Release|Any CPU {4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Release|Any CPU.Build.0 = Release|Any CPU + {4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Release|x64.ActiveCfg = Release|Any CPU + {4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Release|x64.Build.0 = Release|Any CPU + {4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Release|x86.ActiveCfg = Release|Any CPU + {4F46BD02-BEBC-4B2D-B857-4169AD1FB067}.Release|x86.Build.0 = Release|Any CPU {2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Debug|x64.ActiveCfg = Debug|Any CPU + {2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Debug|x64.Build.0 = Debug|Any CPU + {2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Debug|x86.ActiveCfg = Debug|Any CPU + {2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Debug|x86.Build.0 = Debug|Any CPU {2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Release|Any CPU.ActiveCfg = Release|Any CPU {2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Release|Any CPU.Build.0 = Release|Any CPU + {2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Release|x64.ActiveCfg = Release|Any CPU + {2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Release|x64.Build.0 = Release|Any CPU + {2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Release|x86.ActiveCfg = Release|Any CPU + {2DBBD70D-8051-441F-92BB-FF9B8B4B4982}.Release|x86.Build.0 = Release|Any CPU + {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F}.Debug|x64.ActiveCfg = Debug|Any CPU + {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F}.Debug|x64.Build.0 = Debug|Any CPU + {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F}.Debug|x86.ActiveCfg = Debug|Any CPU + {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F}.Debug|x86.Build.0 = Debug|Any CPU + {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F}.Release|Any CPU.Build.0 = Release|Any CPU + {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F}.Release|x64.ActiveCfg = Release|Any CPU + {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F}.Release|x64.Build.0 = Release|Any CPU + {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F}.Release|x86.ActiveCfg = Release|Any CPU + {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F}.Release|x86.Build.0 = Release|Any CPU + {9957038D-F9C3-CA5D-E8AE-BE188E512635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9957038D-F9C3-CA5D-E8AE-BE188E512635}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9957038D-F9C3-CA5D-E8AE-BE188E512635}.Debug|x64.ActiveCfg = Debug|Any CPU + {9957038D-F9C3-CA5D-E8AE-BE188E512635}.Debug|x64.Build.0 = Debug|Any CPU + {9957038D-F9C3-CA5D-E8AE-BE188E512635}.Debug|x86.ActiveCfg = Debug|Any CPU + {9957038D-F9C3-CA5D-E8AE-BE188E512635}.Debug|x86.Build.0 = Debug|Any CPU + {9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|Any CPU.Build.0 = Release|Any CPU + {9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x64.ActiveCfg = Release|Any CPU + {9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x64.Build.0 = Release|Any CPU + {9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x86.ActiveCfg = Release|Any CPU + {9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -421,6 +869,8 @@ Global {B47413AA-55D3-49A7-896A-17ADBFF72407} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} {4F46BD02-BEBC-4B2D-B857-4169AD1FB067} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} {2DBBD70D-8051-441F-92BB-FF9B8B4B4982} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} + {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} + {9957038D-F9C3-CA5D-E8AE-BE188E512635} = {985E0ADB-D4B4-473A-AA40-567E279B7946} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458} diff --git a/examples-Aspire/AspireApp1.AppHost/Program.cs b/examples-Aspire/AspireApp1.AppHost/Program.cs index 3ed78657..7a851900 100644 --- a/examples-Aspire/AspireApp1.AppHost/Program.cs +++ b/examples-Aspire/AspireApp1.AppHost/Program.cs @@ -24,7 +24,8 @@ IResourceBuilder apiService2 = builder .AsHttp2Service() .WithMappingsPath(mappingsPath) .WithWatchStaticMappings() - .WithApiMappingBuilder(WeatherForecastApiMock.BuildAsync); + .WithApiMappingBuilder(WeatherForecastApiMock.BuildAsync) + .WithOpenTelemetry(); // Enable OpenTelemetry tracing for Aspire dashboard //var apiServiceUsedForDocs = builder // .AddWireMock("apiservice1", WireMockServerArguments.DefaultPort) diff --git a/examples/WireMock.Net.Console.NET6.WithCertificate/WireMock.Net.Console.NET6.WithCertificate.csproj b/examples/WireMock.Net.Console.NET6.WithCertificate/WireMock.Net.Console.NET6.WithCertificate.csproj index 1415e9ac..de4e20c1 100644 --- a/examples/WireMock.Net.Console.NET6.WithCertificate/WireMock.Net.Console.NET6.WithCertificate.csproj +++ b/examples/WireMock.Net.Console.NET6.WithCertificate/WireMock.Net.Console.NET6.WithCertificate.csproj @@ -7,7 +7,6 @@ - diff --git a/examples/WireMock.Net.OpenTelemetryDemo/Program.cs b/examples/WireMock.Net.OpenTelemetryDemo/Program.cs new file mode 100644 index 00000000..d12df50d --- /dev/null +++ b/examples/WireMock.Net.OpenTelemetryDemo/Program.cs @@ -0,0 +1,141 @@ +// Copyright © WireMock.Net +// OpenTelemetry Tracing Demo for WireMock.Net +// This demo uses the Console Exporter to visualize traces in the terminal. + +using OpenTelemetry; +using OpenTelemetry.Trace; +using WireMock.Server; +using WireMock.Settings; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; +using WireMock.OpenTelemetry; + +Console.WriteLine("=== WireMock.Net OpenTelemetry Tracing Demo ===\n"); + +// WireMock.Net creates Activity objects using System.Diagnostics.Activity (built into .NET). +// These activities are automatically created when ActivityTracingEnabled is set to true. +// +// To export these traces, you have two options: +// +// Option 1: Configure your own TracerProvider (shown below) +// - Full control over exporters (Console, OTLP, Jaeger, etc.) +// - Add additional instrumentation (HttpClient, database, etc.) +// - Recommended for most applications +// +// Option 2: Use WireMock.Net.OpenTelemetry package +// - Reference the WireMock.Net.OpenTelemetry NuGet package +// - Use services.AddWireMockOpenTelemetry(openTelemetryOptions) +// - Adds WireMock + ASP.NET Core instrumentation and OTLP exporter +// - Good for quick setup with all-in-one configuration + +// Option 1: Custom TracerProvider with Console exporter for this demo +using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddWireMockInstrumentation(new OpenTelemetryOptions() { ExcludeAdminRequests = true }) + .AddHttpClientInstrumentation() // HTTP client traces (for our test requests) + .AddConsoleExporter() // Export traces to console for demo purposes + .Build(); + +Console.WriteLine("Console Exporter configured to visualize:"); +Console.WriteLine(" - WireMock.Net traces (wiremock.* tags)"); +Console.WriteLine(" - ASP.NET Core server traces"); +Console.WriteLine(" - HTTP client traces\n"); + +// Start WireMock server with OpenTelemetry enabled (ActivityTracingOptions != null enables tracing) +var server = WireMockServer.Start(new WireMockServerSettings +{ + StartAdminInterface = true, + ActivityTracingOptions = new ActivityTracingOptions + { + ExcludeAdminRequests = true + } +}); + +Console.WriteLine($"WireMock server started at: {string.Join(", ", server.Urls)}\n"); + +// Configure some mock mappings +server + .Given(Request.Create() + .WithPath("/api/hello") + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(200) + .WithBody("Hello from WireMock!")); + +server + .Given(Request.Create() + .WithPath("/api/user/*") + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(200) + .WithHeader("Content-Type", "application/json") + .WithBody(@"{""name"": ""John Doe"", ""email"": ""john@example.com""}")); + +server + .Given(Request.Create() + .WithPath("/api/error") + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(500) + .WithBody("Internal Server Error")); + +Console.WriteLine("Mock mappings configured:"); +Console.WriteLine(" GET /api/hello -> 200 OK"); +Console.WriteLine(" GET /api/user/* -> 200 OK (JSON)"); +Console.WriteLine(" GET /api/error -> 500 Error"); +Console.WriteLine(); + +// Make some test requests to generate traces +using var httpClient = server.CreateClient(); + +Console.WriteLine("Making test requests to generate traces...\n"); +Console.WriteLine("─────────────────────────────────────────────────────────────────"); + +// Request 1: Successful request +Console.WriteLine("\n>>> Request 1: GET /api/hello"); +var response1 = await httpClient.GetAsync("/api/hello"); +Console.WriteLine($"<<< Response: {(int)response1.StatusCode} {response1.StatusCode}"); +Console.WriteLine($" Body: {await response1.Content.ReadAsStringAsync()}"); + +await Task.Delay(500); // Small delay to let trace export complete + +// Request 2: Another successful request with path parameter +Console.WriteLine("\n>>> Request 2: GET /api/user/123"); +var response2 = await httpClient.GetAsync("/api/user/123"); +Console.WriteLine($"<<< Response: {(int)response2.StatusCode} {response2.StatusCode}"); +Console.WriteLine($" Body: {await response2.Content.ReadAsStringAsync()}"); + +await Task.Delay(500); + +// Request 3: Error response +Console.WriteLine("\n>>> Request 3: GET /api/error"); +var response3 = await httpClient.GetAsync("/api/error"); +Console.WriteLine($"<<< Response: {(int)response3.StatusCode} {response3.StatusCode}"); +Console.WriteLine($" Body: {await response3.Content.ReadAsStringAsync()}"); + +await Task.Delay(500); + +// Request 4: No matching mapping (404) +Console.WriteLine("\n>>> Request 4: GET /api/notfound"); +var response4 = await httpClient.GetAsync("/api/notfound"); +Console.WriteLine($"<<< Response: {(int)response4.StatusCode} {response4.StatusCode}"); + +await Task.Delay(500); + +// Request 5: Admin API request (should be excluded from tracing) +Console.WriteLine("\n>>> Request 5: GET /__admin/health"); +var response5 = await httpClient.GetAsync("/__admin/health"); +Console.WriteLine($"<<< Admin Health Status: {response5.StatusCode}"); + +Console.WriteLine("\n─────────────────────────────────────────────────────────────────"); +Console.WriteLine("\nTraces above show OpenTelemetry activities from WireMock.Net!"); +Console.WriteLine("Look for 'Activity.TraceId', 'Activity.SpanId', and custom tags like:"); +Console.WriteLine(" - http.request.method"); +Console.WriteLine(" - url.path"); +Console.WriteLine(" - http.response.status_code"); +Console.WriteLine(" - wiremock.mapping.matched"); +Console.WriteLine(" - wiremock.mapping.guid"); +Console.WriteLine(); + +// Cleanup +server.Stop(); +Console.WriteLine("WireMock server stopped. Demo complete!"); diff --git a/examples/WireMock.Net.OpenTelemetryDemo/WireMock.Net.OpenTelemetryDemo.csproj b/examples/WireMock.Net.OpenTelemetryDemo/WireMock.Net.OpenTelemetryDemo.csproj new file mode 100644 index 00000000..e85ad669 --- /dev/null +++ b/examples/WireMock.Net.OpenTelemetryDemo/WireMock.Net.OpenTelemetryDemo.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + diff --git a/src/WireMock.Net.Aspire/WireMockServerArguments.cs b/src/WireMock.Net.Aspire/WireMockServerArguments.cs index 3a28aefa..1ca0d200 100644 --- a/src/WireMock.Net.Aspire/WireMockServerArguments.cs +++ b/src/WireMock.Net.Aspire/WireMockServerArguments.cs @@ -79,6 +79,22 @@ public class WireMockServerArguments /// public Dictionary ProtoDefinitions { get; set; } = []; + /// + /// Gets or sets a value indicating whether OpenTelemetry tracing is enabled. + /// When enabled, WireMock.Net will emit distributed traces for request processing. + /// Default value is false. + /// + public bool OpenTelemetryEnabled { get; set; } + + /// + /// Gets or sets the OTLP exporter endpoint URL. + /// When set, traces will be exported to this endpoint using the OTLP protocol. + /// Example: "http://localhost:4317" for gRPC or "http://localhost:4318" for HTTP. + /// If not set, the OTLP exporter will use the OTEL_EXPORTER_OTLP_ENDPOINT environment variable, + /// or fall back to the default endpoint (http://localhost:4317 for gRPC). + /// + public string? OpenTelemetryOtlpExporterEndpoint { get; set; } + /// /// Add an additional Urls on which WireMock should listen. /// @@ -138,6 +154,20 @@ public class WireMockServerArguments Add(args, "--WatchStaticMappingsInSubdirectories", "true"); } + if (OpenTelemetryEnabled) + { + // Enable activity tracing (creates System.Diagnostics.Activity objects) + Add(args, "--ActivityTracingEnabled", "true"); + + // Enable OpenTelemetry exporter + Add(args, "--OpenTelemetryEnabled", "true"); + + if (!string.IsNullOrEmpty(OpenTelemetryOtlpExporterEndpoint)) + { + Add(args, "--OpenTelemetryOtlpExporterEndpoint", OpenTelemetryOtlpExporterEndpoint); + } + } + if (AdditionalUrls.Count > 0) { Add(args, "--Urls", $"http://*:{HttpContainerPort} {string.Join(' ', AdditionalUrls)}"); diff --git a/src/WireMock.Net.Aspire/WireMockServerBuilderExtensions.cs b/src/WireMock.Net.Aspire/WireMockServerBuilderExtensions.cs index 07c5dc79..70a1527c 100644 --- a/src/WireMock.Net.Aspire/WireMockServerBuilderExtensions.cs +++ b/src/WireMock.Net.Aspire/WireMockServerBuilderExtensions.cs @@ -287,6 +287,40 @@ public static class WireMockServerBuilderExtensions return wiremock; } + /// + /// Configures OpenTelemetry distributed tracing for the WireMock.Net server. + /// This enables automatic trace export to the Aspire dashboard. + /// + /// The . + /// A reference to the . + /// + /// When enabled, WireMock.Net will emit distributed traces for each request processed, + /// including information about: + /// + /// HTTP method, URL, and status code + /// Mapping match results and scores + /// Request processing duration + /// + /// The traces will automatically appear in the Aspire dashboard. + /// + public static IResourceBuilder WithOpenTelemetry(this IResourceBuilder wiremock) + { + Guard.NotNull(wiremock); + + // Enable OpenTelemetry in WireMock server arguments + wiremock.Resource.Arguments.OpenTelemetryEnabled = true; + + // Use Aspire's standard WithOtlpExporter to configure OTEL environment variables for the container + // This sets OTEL_EXPORTER_OTLP_ENDPOINT which the OTLP exporter reads automatically + var containerBuilder = wiremock as IResourceBuilder; + if (containerBuilder != null) + { + containerBuilder.WithOtlpExporter(); + } + + return wiremock; + } + private static Task OnRunOpenInspectorCommandAsync(IResourceBuilder builder) { WireMockInspector.Inspect(builder.Resource.GetEndpoint().Url); diff --git a/src/WireMock.Net.Minimal/Owin/ActivityTracing/ActivityTracingOptions.cs b/src/WireMock.Net.Minimal/Owin/ActivityTracing/ActivityTracingOptions.cs new file mode 100644 index 00000000..e7767f9e --- /dev/null +++ b/src/WireMock.Net.Minimal/Owin/ActivityTracing/ActivityTracingOptions.cs @@ -0,0 +1,38 @@ +// Copyright © WireMock.Net + +#if ACTIVITY_TRACING_SUPPORTED + +namespace WireMock.Owin.ActivityTracing; + +/// +/// Options for controlling activity tracing in WireMock.Net middleware. +/// These options control the creation of System.Diagnostics.Activity objects +/// but do not require any OpenTelemetry exporter dependencies. +/// +public class ActivityTracingOptions +{ + /// + /// Gets or sets a value indicating whether to exclude admin interface requests from tracing. + /// Default is true. + /// + public bool ExcludeAdminRequests { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to record request body in trace attributes. + /// Default is false due to potential PII concerns. + /// + public bool RecordRequestBody { get; set; } + + /// + /// Gets or sets a value indicating whether to record response body in trace attributes. + /// Default is false due to potential PII concerns. + /// + public bool RecordResponseBody { get; set; } + + /// + /// Gets or sets a value indicating whether to record mapping match details in trace attributes. + /// Default is true. + /// + public bool RecordMatchDetails { get; set; } = true; +} +#endif diff --git a/src/WireMock.Net.Minimal/Owin/ActivityTracing/ActivityTracingValidator.cs b/src/WireMock.Net.Minimal/Owin/ActivityTracing/ActivityTracingValidator.cs new file mode 100644 index 00000000..bac61387 --- /dev/null +++ b/src/WireMock.Net.Minimal/Owin/ActivityTracing/ActivityTracingValidator.cs @@ -0,0 +1,34 @@ +// Copyright © WireMock.Net + +#if !ACTIVITY_TRACING_SUPPORTED +using System; +#endif +using WireMock.Settings; + +namespace WireMock.Owin.ActivityTracing; + +/// +/// Validator for Activity Tracing configuration. +/// +internal static class ActivityTracingValidator +{ + /// + /// Validates that Activity Tracing is supported on the current framework. + /// Throws an exception if ActivityTracingOptions is configured on an unsupported framework. + /// + /// The WireMock server settings to validate. + /// + /// Thrown when ActivityTracingOptions is configured but the current framework does not support System.Diagnostics.Activity. + /// + public static void ValidateActivityApiPresence(WireMockServerSettings settings) + { +#if !ACTIVITY_TRACING_SUPPORTED + if (settings.ActivityTracingOptions is not null) + { + throw new InvalidOperationException( + "Activity Tracing is not supported on this target framework. " + + "It requires .NET 5.0 or higher which includes System.Diagnostics.Activity support."); + } +#endif + } +} diff --git a/src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockActivitySource.cs b/src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockActivitySource.cs new file mode 100644 index 00000000..d7db92c4 --- /dev/null +++ b/src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockActivitySource.cs @@ -0,0 +1,201 @@ +// Copyright © WireMock.Net + +#if ACTIVITY_TRACING_SUPPORTED +using System; +using System.Collections.Generic; +using System.Diagnostics; +using WireMock.Logging; + +namespace WireMock.Owin.ActivityTracing; + +/// +/// Provides an ActivitySource for WireMock.Net distributed tracing. +/// +public static class WireMockActivitySource +{ + /// + /// The name of the ActivitySource used by WireMock.Net. + /// + internal const string SourceName = "WireMock.Net"; + + /// + /// The ActivitySource instance used for creating tracing activities. + /// + public static readonly ActivitySource Source = new(SourceName, GetVersion()); + + private static string GetVersion() + { + return typeof(WireMockActivitySource).Assembly.GetName().Version?.ToString() ?? "1.0.0"; + } + + /// + /// Starts a new activity for a WireMock request. + /// + /// The HTTP method of the request. + /// The path of the request. + /// The started activity, or null if tracing is not enabled. + internal static Activity? StartRequestActivity(string requestMethod, string requestPath) + { + if (!Source.HasListeners()) + { + return null; + } + + var activity = Source.StartActivity( + $"WireMock {requestMethod} {requestPath}", + ActivityKind.Server + ); + + return activity; + } + + /// + /// Enriches an activity with request information. + /// + internal static void EnrichWithRequest(Activity? activity, IRequestMessage request, ActivityTracingOptions? options = null) + { + if (activity == null) + { + return; + } + + activity.SetTag(WireMockSemanticConventions.HttpMethod, request.Method); + activity.SetTag(WireMockSemanticConventions.HttpUrl, request.Url); + activity.SetTag(WireMockSemanticConventions.HttpPath, request.Path); + activity.SetTag(WireMockSemanticConventions.HttpHost, request.Host); + + if (request.ClientIP != null) + { + activity.SetTag(WireMockSemanticConventions.ClientAddress, request.ClientIP); + } + + // Record request body if enabled + if (options?.RecordRequestBody == true && request.Body != null) + { + activity.SetTag(WireMockSemanticConventions.RequestBody, request.Body); + } + } + + /// + /// Enriches an activity with response information. + /// + internal static void EnrichWithResponse(Activity? activity, IResponseMessage? response, ActivityTracingOptions? options = null) + { + if (activity == null || response == null) + { + return; + } + + // StatusCode can be int, HttpStatusCode, or string + var statusCode = response.StatusCode; + int? statusCodeInt = statusCode switch + { + int i => i, + System.Net.HttpStatusCode hsc => (int)hsc, + string s when int.TryParse(s, out var parsed) => parsed, + _ => null + }; + + if (statusCodeInt.HasValue) + { + activity.SetTag(WireMockSemanticConventions.HttpStatusCode, statusCodeInt.Value); + activity.SetTag("otel.status_description", $"HTTP {statusCodeInt.Value}"); + + // Set status based on HTTP status code (using standard otel.status_code tag) + if (statusCodeInt.Value >= 400) + { + activity.SetTag("otel.status_code", "ERROR"); + } + else + { + activity.SetTag("otel.status_code", "OK"); + } + } + + // Record response body if enabled + if (options?.RecordResponseBody == true && response.BodyData?.BodyAsString != null) + { + activity.SetTag(WireMockSemanticConventions.ResponseBody, response.BodyData.BodyAsString); + } + } + + /// + /// Enriches an activity with mapping match information. + /// + internal static void EnrichWithMappingMatch( + Activity? activity, + Guid? mappingGuid, + string? mappingTitle, + bool isPerfectMatch, + double? matchScore) + { + if (activity == null) + { + return; + } + + activity.SetTag(WireMockSemanticConventions.MappingMatched, isPerfectMatch); + + if (mappingGuid.HasValue) + { + activity.SetTag(WireMockSemanticConventions.MappingGuid, mappingGuid.Value.ToString()); + } + + if (!string.IsNullOrEmpty(mappingTitle)) + { + activity.SetTag(WireMockSemanticConventions.MappingTitle, mappingTitle); + } + + if (matchScore.HasValue) + { + activity.SetTag(WireMockSemanticConventions.MatchScore, matchScore.Value); + } + } + + /// + /// Enriches an activity with log entry information (includes response and mapping match info). + /// + internal static void EnrichWithLogEntry(Activity? activity, ILogEntry logEntry, ActivityTracingOptions? options = null) + { + if (activity == null) + { + return; + } + + // Enrich with response + EnrichWithResponse(activity, logEntry.ResponseMessage, options); + + // Enrich with mapping match (if enabled) + if (options?.RecordMatchDetails != false) + { + EnrichWithMappingMatch( + activity, + logEntry.MappingGuid, + logEntry.MappingTitle, + logEntry.RequestMatchResult?.IsPerfectMatch ?? false, + logEntry.RequestMatchResult?.TotalScore); + } + + // Set request GUID + activity.SetTag(WireMockSemanticConventions.RequestGuid, logEntry.Guid.ToString()); + } + + /// + /// Records an exception on the activity. + /// + internal static void RecordException(Activity? activity, Exception exception) + { + if (activity == null) + { + return; + } + + // Use standard OpenTelemetry exception semantic conventions + activity.SetTag("otel.status_code", "ERROR"); + activity.SetTag("otel.status_description", exception.Message); + activity.SetTag("exception.type", exception.GetType().FullName); + activity.SetTag("exception.message", exception.Message); + activity.SetTag("exception.stacktrace", exception.ToString()); + } +} +#endif diff --git a/src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockSemanticConventions.cs b/src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockSemanticConventions.cs new file mode 100644 index 00000000..66360cba --- /dev/null +++ b/src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockSemanticConventions.cs @@ -0,0 +1,34 @@ +// Copyright © WireMock.Net + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WireMock.Owin.ActivityTracing; + +/// +/// Semantic convention constants for WireMock.Net tracing attributes. +/// +internal static class WireMockSemanticConventions +{ + // Standard HTTP semantic conventions (OpenTelemetry) + public const string HttpMethod = "http.request.method"; + public const string HttpUrl = "url.full"; + public const string HttpPath = "url.path"; + public const string HttpHost = "server.address"; + public const string HttpStatusCode = "http.response.status_code"; + public const string ClientAddress = "client.address"; + + // WireMock-specific attributes + public const string MappingMatched = "wiremock.mapping.matched"; + public const string MappingGuid = "wiremock.mapping.guid"; + public const string MappingTitle = "wiremock.mapping.title"; + public const string MatchScore = "wiremock.match.score"; + public const string PartialMappingGuid = "wiremock.partial_mapping.guid"; + public const string PartialMappingTitle = "wiremock.partial_mapping.title"; + public const string RequestGuid = "wiremock.request.guid"; + public const string RequestBody = "wiremock.request.body"; + public const string ResponseBody = "wiremock.response.body"; +} diff --git a/src/WireMock.Net.Minimal/Owin/IWireMockMiddlewareOptions.cs b/src/WireMock.Net.Minimal/Owin/IWireMockMiddlewareOptions.cs index d6f4c7ee..9a7395b7 100644 --- a/src/WireMock.Net.Minimal/Owin/IWireMockMiddlewareOptions.cs +++ b/src/WireMock.Net.Minimal/Owin/IWireMockMiddlewareOptions.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using WireMock.Handlers; using WireMock.Logging; using WireMock.Matchers; +using WireMock.Owin.ActivityTracing; using WireMock.Types; using WireMock.Util; using System.Security.Cryptography.X509Certificates; @@ -90,4 +91,12 @@ internal interface IWireMockMiddlewareOptions QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; } public bool ProxyAll { get; set; } + +#if ACTIVITY_TRACING_SUPPORTED + /// + /// Gets or sets the activity tracing options. + /// When set, System.Diagnostics.Activity objects are created for request tracing. + /// + ActivityTracingOptions? ActivityTracingOptions { get; set; } +#endif } \ No newline at end of file diff --git a/src/WireMock.Net.Minimal/Owin/WireMockMiddleware.cs b/src/WireMock.Net.Minimal/Owin/WireMockMiddleware.cs index a67e6b70..790a352e 100644 --- a/src/WireMock.Net.Minimal/Owin/WireMockMiddleware.cs +++ b/src/WireMock.Net.Minimal/Owin/WireMockMiddleware.cs @@ -16,6 +16,10 @@ using System.Collections.Generic; using WireMock.Constants; using WireMock.Exceptions; using WireMock.Util; +#if ACTIVITY_TRACING_SUPPORTED +using System.Diagnostics; +using WireMock.Owin.ActivityTracing; +#endif #if !USE_ASPNETCORE using IContext = Microsoft.Owin.IOwinContext; using OwinMiddleware = Microsoft.Owin.OwinMiddleware; @@ -97,6 +101,40 @@ namespace WireMock.Owin { var request = await _requestMapper.MapAsync(ctx.Request, _options).ConfigureAwait(false); +#if ACTIVITY_TRACING_SUPPORTED + // Start activity if ActivityTracingOptions is configured + var tracingEnabled = _options.ActivityTracingOptions is not null; + var excludeAdmin = _options.ActivityTracingOptions?.ExcludeAdminRequests ?? true; + Activity? activity = null; + + // Check if we should trace this request (optionally exclude admin requests) + var shouldTrace = tracingEnabled && !(excludeAdmin && request.Path.StartsWith("/__admin/")); + + if (shouldTrace) + { + activity = WireMockActivitySource.StartRequestActivity(request.Method, request.Path); + WireMockActivitySource.EnrichWithRequest(activity, request, _options.ActivityTracingOptions); + } + + try + { + await InvokeInternalCoreAsync(ctx, request, activity).ConfigureAwait(false); + } + finally + { + activity?.Dispose(); + } +#else + await InvokeInternalCoreAsync(ctx, request).ConfigureAwait(false); +#endif + } + +#if ACTIVITY_TRACING_SUPPORTED + private async Task InvokeInternalCoreAsync(IContext ctx, RequestMessage request, Activity? activity) +#else + private async Task InvokeInternalCoreAsync(IContext ctx, RequestMessage request) +#endif + { var logRequest = false; IResponseMessage? response = null; (MappingMatcherResult? Match, MappingMatcherResult? Partial) result = (null, null); @@ -193,6 +231,10 @@ namespace WireMock.Owin { _options.Logger.Error($"Providing a Response for Mapping '{result.Match?.Mapping.Guid}' failed. HttpStatusCode set to 500. Exception: {ex}"); response = ResponseMessageBuilder.Create(500, ex.Message); + +#if ACTIVITY_TRACING_SUPPORTED + WireMockActivitySource.RecordException(activity, ex); +#endif } finally { @@ -211,6 +253,11 @@ namespace WireMock.Owin PartialMatchResult = result.Partial?.RequestMatchResult }; +#if ACTIVITY_TRACING_SUPPORTED + // Enrich activity with response and mapping info + WireMockActivitySource.EnrichWithLogEntry(activity, log, _options.ActivityTracingOptions); +#endif + LogRequest(log, logRequest); try diff --git a/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptions.cs b/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptions.cs index 6f675459..5c24198f 100644 --- a/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptions.cs +++ b/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptions.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using WireMock.Handlers; using WireMock.Logging; using WireMock.Matchers; +using WireMock.Owin.ActivityTracing; using WireMock.Types; using WireMock.Util; using System.Security.Cryptography.X509Certificates; @@ -106,4 +107,9 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions /// public bool ProxyAll { get; set; } + +#if ACTIVITY_TRACING_SUPPORTED + /// + public ActivityTracingOptions? ActivityTracingOptions { get; set; } +#endif } \ No newline at end of file diff --git a/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptionsHelper.cs b/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptionsHelper.cs index a5dbd144..824173e3 100644 --- a/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptionsHelper.cs +++ b/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptionsHelper.cs @@ -2,6 +2,7 @@ using System; using Stef.Validation; +using WireMock.Owin.ActivityTracing; using WireMock.Settings; namespace WireMock.Owin; @@ -34,6 +35,27 @@ internal static class WireMockMiddlewareOptionsHelper options.RequestLogExpirationDuration = settings.RequestLogExpirationDuration; options.SaveUnmatchedRequests = settings.SaveUnmatchedRequests; + // Validate and configure activity tracing + ActivityTracingValidator.ValidateActivityApiPresence(settings); +#if ACTIVITY_TRACING_SUPPORTED + if (settings.ActivityTracingOptions is not null) + { + options.ActivityTracingOptions = new Owin.ActivityTracing.ActivityTracingOptions + { + ExcludeAdminRequests = settings.ActivityTracingOptions.ExcludeAdminRequests, + RecordRequestBody = settings.ActivityTracingOptions.RecordRequestBody, + RecordResponseBody = settings.ActivityTracingOptions.RecordResponseBody, + RecordMatchDetails = settings.ActivityTracingOptions.RecordMatchDetails + }; + } +#endif +#if USE_ASPNETCORE + options.AdditionalServiceRegistration = settings.AdditionalServiceRegistration; + options.CorsPolicyOptions = settings.CorsPolicyOptions; + options.ClientCertificateMode = settings.ClientCertificateMode; + options.AcceptAnyClientCertificate = settings.AcceptAnyClientCertificate; +#endif + if (settings.CustomCertificateDefined) { options.X509StoreName = settings.CertificateSettings!.X509StoreName; diff --git a/src/WireMock.Net.Minimal/Server/WireMockServer.cs b/src/WireMock.Net.Minimal/Server/WireMockServer.cs index b7d46738..f8bed8f4 100644 --- a/src/WireMock.Net.Minimal/Server/WireMockServer.cs +++ b/src/WireMock.Net.Minimal/Server/WireMockServer.cs @@ -412,11 +412,6 @@ public partial class WireMockServer : IWireMockServer ); #if USE_ASPNETCORE - _options.AdditionalServiceRegistration = _settings.AdditionalServiceRegistration; - _options.CorsPolicyOptions = _settings.CorsPolicyOptions; - _options.ClientCertificateMode = _settings.ClientCertificateMode; - _options.AcceptAnyClientCertificate = _settings.AcceptAnyClientCertificate; - _httpServer = new AspNetCoreSelfHost(_options, urlOptions); #else _httpServer = new OwinSelfHost(_options, urlOptions); diff --git a/src/WireMock.Net.Minimal/Settings/WireMockServerSettingsParser.cs b/src/WireMock.Net.Minimal/Settings/WireMockServerSettingsParser.cs index 0dba1c21..6cd639a5 100644 --- a/src/WireMock.Net.Minimal/Settings/WireMockServerSettingsParser.cs +++ b/src/WireMock.Net.Minimal/Settings/WireMockServerSettingsParser.cs @@ -85,6 +85,7 @@ public static class WireMockServerSettingsParser ParseProxyAndRecordSettings(settings, parser); ParseCertificateSettings(settings, parser); ParseHandlebarsSettings(settings, parser); + ParseActivityTracingSettings(settings, parser); return true; } @@ -226,4 +227,19 @@ public static class WireMockServerSettingsParser }; } } + + private static void ParseActivityTracingSettings(WireMockServerSettings settings, SimpleSettingsParser parser) + { + // Only create ActivityTracingOptions if tracing is enabled + if (parser.GetBoolValue("ActivityTracingEnabled") || parser.GetBoolValue("ActivityTracingOptions__Enabled")) + { + settings.ActivityTracingOptions = new ActivityTracingOptions + { + ExcludeAdminRequests = parser.GetBoolWithDefault("ActivityTracingExcludeAdminRequests", "ActivityTracingOptions__ExcludeAdminRequests", defaultValue: true), + RecordRequestBody = parser.GetBoolValue("ActivityTracingRecordRequestBody") || parser.GetBoolValue("ActivityTracingOptions__RecordRequestBody"), + RecordResponseBody = parser.GetBoolValue("ActivityTracingRecordResponseBody") || parser.GetBoolValue("ActivityTracingOptions__RecordResponseBody"), + RecordMatchDetails = parser.GetBoolWithDefault("ActivityTracingRecordMatchDetails", "ActivityTracingOptions__RecordMatchDetails", defaultValue: true) + }; + } + } } \ No newline at end of file diff --git a/src/WireMock.Net.Minimal/WireMock.Net.Minimal.csproj b/src/WireMock.Net.Minimal/WireMock.Net.Minimal.csproj index 6d245c53..75f2aa20 100644 --- a/src/WireMock.Net.Minimal/WireMock.Net.Minimal.csproj +++ b/src/WireMock.Net.Minimal/WireMock.Net.Minimal.csproj @@ -52,6 +52,11 @@ $(DefineConstants);TRAILINGHEADERS + + + $(DefineConstants);ACTIVITY_TRACING_SUPPORTED + + diff --git a/src/WireMock.Net.OpenTelemetry/OpenTelemetryOptions.cs b/src/WireMock.Net.OpenTelemetry/OpenTelemetryOptions.cs new file mode 100644 index 00000000..044b5397 --- /dev/null +++ b/src/WireMock.Net.OpenTelemetry/OpenTelemetryOptions.cs @@ -0,0 +1,35 @@ +// Copyright © WireMock.Net + +using JetBrains.Annotations; + +namespace WireMock.OpenTelemetry; + +/// +/// OpenTelemetry exporter configuration options for WireMock.Net. +/// These options control the export of traces to an OTLP endpoint. +/// For controlling what data is recorded in traces, configure ActivityTracingOptions in WireMockServerSettings. +/// +public class OpenTelemetryOptions +{ + /// + /// Gets or sets a value indicating whether to exclude admin interface requests from ASP.NET Core instrumentation. + /// Default is true. + /// + /// + /// This controls the ASP.NET Core HTTP server instrumentation filter. + /// To also exclude admin requests from WireMock's own activity tracing, + /// set ActivityTracingOptions.ExcludeAdminRequests in WireMockServerSettings. + /// + [PublicAPI] + public bool ExcludeAdminRequests { get; set; } = true; + + /// + /// Gets or sets the OTLP exporter endpoint URL. + /// When set, traces will be exported to this endpoint using the OTLP protocol. + /// Example: "http://localhost:4317" for gRPC or "http://localhost:4318" for HTTP. + /// If not set, the OTLP exporter will use the OTEL_EXPORTER_OTLP_ENDPOINT environment variable, + /// or fall back to the default endpoint (http://localhost:4317 for gRPC). + /// + [PublicAPI] + public string? OtlpExporterEndpoint { get; set; } +} diff --git a/src/WireMock.Net.OpenTelemetry/OpenTelemetryOptionsParser.cs b/src/WireMock.Net.OpenTelemetry/OpenTelemetryOptionsParser.cs new file mode 100644 index 00000000..8468923b --- /dev/null +++ b/src/WireMock.Net.OpenTelemetry/OpenTelemetryOptionsParser.cs @@ -0,0 +1,44 @@ +// Copyright © WireMock.Net + +using System.Collections; +using Stef.Validation; +using WireMock.Settings; + +namespace WireMock.OpenTelemetry; + +/// +/// A static helper class to parse commandline arguments into OpenTelemetryOptions. +/// +public static class OpenTelemetryOptionsParser +{ + private const string Prefix = "OpenTelemetry"; + + /// + /// Parse commandline arguments into OpenTelemetryOptions. + /// + /// The commandline arguments + /// The environment settings (optional) + /// The parsed options, or null if OpenTelemetry is not enabled + /// Always returns true. + public static bool TryParseArguments(string[] args, IDictionary? environment, out OpenTelemetryOptions? options) + { + Guard.HasNoNulls(args); + + var parser = new SimpleSettingsParser(); + parser.Parse(args, environment); + + if (!parser.GetBoolValue($"{Prefix}Enabled")) + { + options = null; + return true; + } + + options = new OpenTelemetryOptions + { + ExcludeAdminRequests = parser.GetBoolValue($"{Prefix}ExcludeAdminRequests", defaultValue: true), + OtlpExporterEndpoint = parser.GetStringValue($"{Prefix}OtlpExporterEndpoint") + }; + + return true; + } +} diff --git a/src/WireMock.Net.OpenTelemetry/README.md b/src/WireMock.Net.OpenTelemetry/README.md new file mode 100644 index 00000000..7e972032 --- /dev/null +++ b/src/WireMock.Net.OpenTelemetry/README.md @@ -0,0 +1,160 @@ +# WireMock.Net.OpenTelemetry + +OpenTelemetry tracing support for WireMock.Net. This package provides instrumentation and OTLP (OpenTelemetry Protocol) exporting capabilities. + +## Overview + +WireMock.Net automatically creates `System.Diagnostics.Activity` objects for request tracing when `ActivityTracingOptions` is configured (not null). These activities use the built-in .NET distributed tracing infrastructure and are available without any additional dependencies. + +This package provides: +- **WireMock.Net instrumentation** - Adds the WireMock.Net ActivitySource to the tracing pipeline +- **ASP.NET Core instrumentation** - Standard HTTP server tracing with request filtering +- **OTLP exporter** - Sends traces to an OpenTelemetry collector + +## Installation + +```bash +dotnet add package WireMock.Net.OpenTelemetry +``` + +## Usage + +### Option 1: Using AdditionalServiceRegistration (Recommended) + +```csharp +using WireMock.OpenTelemetry; +using WireMock.Server; +using WireMock.Settings; + +var openTelemetryOptions = new OpenTelemetryOptions +{ + ExcludeAdminRequests = true, + OtlpExporterEndpoint = "http://localhost:4317" // Your OTEL collector +}; + +var settings = new WireMockServerSettings +{ + // Setting ActivityTracingOptions (not null) enables activity creation in middleware + ActivityTracingOptions = new ActivityTracingOptions + { + ExcludeAdminRequests = true, + RecordRequestBody = false, // PII concern + RecordResponseBody = false, // PII concern + RecordMatchDetails = true + }, + AdditionalServiceRegistration = services => + { + services.AddWireMockOpenTelemetry(openTelemetryOptions); + } +}; + +var server = WireMockServer.Start(settings); +``` + +### Option 2: Custom TracerProvider Configuration + +For more control over the tracing configuration: + +```csharp +using OpenTelemetry; +using OpenTelemetry.Trace; +using WireMock.OpenTelemetry; + +var openTelemetryOptions = new OpenTelemetryOptions(); + +// Configure your own TracerProvider +using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddWireMockInstrumentation(openTelemetryOptions) // Adds WireMock.Net source + .AddOtlpExporter(options => + { + options.Endpoint = new Uri("http://localhost:4317"); + }) + .Build(); +``` + +## Extension Methods + +### `AddWireMockOpenTelemetry` + +Adds full OpenTelemetry tracing to the service collection with instrumentation and OTLP exporter: + +```csharp +services.AddWireMockOpenTelemetry(openTelemetryOptions); +``` + +This configures: +- The WireMock.Net ActivitySource +- ASP.NET Core instrumentation +- OTLP exporter (using the endpoint from `OpenTelemetryOptions.OtlpExporterEndpoint` or the `OTEL_EXPORTER_OTLP_ENDPOINT` environment variable) + +### `AddWireMockInstrumentation` + +Adds WireMock instrumentation to an existing TracerProviderBuilder: + +```csharp +tracerProvider.AddWireMockInstrumentation(openTelemetryOptions); +``` + +## Configuration + +### OpenTelemetryOptions (Exporter configuration) + +| Property | Description | Default | +|----------|-------------|---------| +| `ExcludeAdminRequests` | Exclude `/__admin/*` from ASP.NET Core instrumentation | `true` | +| `OtlpExporterEndpoint` | OTLP collector endpoint URL | Uses `OTEL_EXPORTER_OTLP_ENDPOINT` env var | + +### ActivityTracingOptions (Trace content configuration) + +Configured in `WireMockServerSettings.ActivityTracingOptions`: + +| Property | Description | Default | +|----------|-------------|---------| +| `ExcludeAdminRequests` | Exclude `/__admin/*` from WireMock activity creation | `true` | +| `RecordRequestBody` | Include request body in trace attributes | `false` | +| `RecordResponseBody` | Include response body in trace attributes | `false` | +| `RecordMatchDetails` | Include mapping match details in trace attributes | `true` | + +## Trace Attributes + +WireMock.Net traces include these semantic conventions: + +**Standard HTTP attributes:** +- `http.request.method` +- `url.full` +- `url.path` +- `server.address` +- `http.response.status_code` +- `client.address` + +**WireMock-specific attributes:** +- `wiremock.mapping.matched` - Whether a mapping was found +- `wiremock.mapping.guid` - GUID of the matched mapping +- `wiremock.mapping.title` - Title of the matched mapping +- `wiremock.match.score` - Match score +- `wiremock.request.guid` - GUID of the request + +## CLI Arguments + +When using WireMock.Net.StandAlone or Docker images, activity tracing and OpenTelemetry can be configured via command-line arguments: + +**Activity Tracing (what gets recorded):** +```bash +--ActivityTracingEnabled true +--ActivityTracingExcludeAdminRequests true +--ActivityTracingRecordRequestBody false +--ActivityTracingRecordResponseBody false +--ActivityTracingRecordMatchDetails true +``` + +**OpenTelemetry Export (where traces are sent):** +```bash +--OpenTelemetryEnabled true +--OpenTelemetryOtlpExporterEndpoint http://localhost:4317 +--OpenTelemetryExcludeAdminRequests true +``` + +## Requirements + +- .NET 6.0 or later +- WireMock.Net 1.6.0 or later diff --git a/src/WireMock.Net.OpenTelemetry/WireMock.Net.OpenTelemetry.csproj b/src/WireMock.Net.OpenTelemetry/WireMock.Net.OpenTelemetry.csproj new file mode 100644 index 00000000..c21960e4 --- /dev/null +++ b/src/WireMock.Net.OpenTelemetry/WireMock.Net.OpenTelemetry.csproj @@ -0,0 +1,43 @@ + + + + OpenTelemetry exporter support for WireMock.Net + WireMock.Net.OpenTelemetry + Petr Houška + net6.0;net8.0 + true + wiremock;opentelemetry;otel;tracing;telemetry + WireMock + {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F} + true + true + true + true + ../WireMock.Net/WireMock.Net.ruleset + true + ../WireMock.Net/WireMock.Net.snk + true + MIT + + + + true + + + + + + + + + + + + + + + + + + + diff --git a/src/WireMock.Net.OpenTelemetry/WireMockOpenTelemetryExtensions.cs b/src/WireMock.Net.OpenTelemetry/WireMockOpenTelemetryExtensions.cs new file mode 100644 index 00000000..8626a8a2 --- /dev/null +++ b/src/WireMock.Net.OpenTelemetry/WireMockOpenTelemetryExtensions.cs @@ -0,0 +1,115 @@ +// Copyright © WireMock.Net + +using System; +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +namespace WireMock.OpenTelemetry; + +/// +/// Extension methods for configuring OpenTelemetry tracing for WireMock.Net. +/// +public static class WireMockOpenTelemetryExtensions +{ + private const string ServiceName = "WireMock.Net"; + private const string WireMockActivitySourceName = "WireMock.Net"; + + /// + /// Adds OpenTelemetry tracing to the WireMock server with instrumentation and OTLP exporter. + /// This configures: + /// - WireMock.Net ActivitySource instrumentation (custom WireMock traces with mapping details) + /// - ASP.NET Core instrumentation (standard HTTP server traces) + /// - OTLP exporter to send traces to a collector + /// + /// The service collection. + /// The OpenTelemetry options containing exporter configuration. + /// The service collection for chaining. + public static IServiceCollection AddWireMockOpenTelemetry( + this IServiceCollection services, + OpenTelemetryOptions? options) + { + if (options is null) + { + return services; + } + + services.AddOpenTelemetry() + .ConfigureResource(resource => + { + resource.AddService( + serviceName: ServiceName, + serviceVersion: typeof(WireMockOpenTelemetryExtensions).Assembly.GetName().Version?.ToString() ?? "unknown" + ); + }) + .WithTracing(tracing => + { + // Add WireMock-specific traces + tracing.AddSource(WireMockActivitySourceName); + + // Add ASP.NET Core instrumentation for standard HTTP server traces + tracing.AddAspNetCoreInstrumentation(aspNetOptions => + { + // Filter out admin requests if configured + if (options.ExcludeAdminRequests) + { + aspNetOptions.Filter = context => + { + var path = context.Request.Path.Value ?? string.Empty; + return !path.StartsWith("/__admin", StringComparison.OrdinalIgnoreCase); + }; + } + }); + + // Add OTLP exporter - automatically reads OTEL_EXPORTER_OTLP_ENDPOINT from environment + // If explicit endpoint is specified in options, use that instead + var otlpEndpoint = options.OtlpExporterEndpoint; + if (!string.IsNullOrEmpty(otlpEndpoint)) + { + tracing.AddOtlpExporter(exporterOptions => + { + exporterOptions.Endpoint = new Uri(otlpEndpoint); + }); + } + else + { + // Use default - reads from OTEL_EXPORTER_OTLP_ENDPOINT env var + tracing.AddOtlpExporter(); + } + }); + + return services; + } + + /// + /// Configures OpenTelemetry tracing builder with WireMock.Net ActivitySource and ASP.NET Core instrumentation. + /// Use this method when you want more control over the TracerProvider configuration. + /// + /// The TracerProviderBuilder to configure. + /// The OpenTelemetry options (optional). + /// The TracerProviderBuilder for chaining. + public static TracerProviderBuilder AddWireMockInstrumentation( + this TracerProviderBuilder tracing, + OpenTelemetryOptions? options = null) + { + // Add WireMock-specific traces + tracing.AddSource(WireMockActivitySourceName); + + // Add ASP.NET Core instrumentation for standard HTTP server traces + tracing.AddAspNetCoreInstrumentation(aspNetOptions => + { + // Filter out admin requests if configured + if (options?.ExcludeAdminRequests == true) + { + aspNetOptions.Filter = context => + { + var path = context.Request.Path.Value ?? string.Empty; + return !path.StartsWith("/__admin", StringComparison.OrdinalIgnoreCase); + }; + } + }); + + return tracing; + } +} diff --git a/src/WireMock.Net.Minimal/Extensions/DictionaryExtensions.cs b/src/WireMock.Net.Shared/Extensions/DictionaryExtensions.cs similarity index 99% rename from src/WireMock.Net.Minimal/Extensions/DictionaryExtensions.cs rename to src/WireMock.Net.Shared/Extensions/DictionaryExtensions.cs index b80c6105..95310ff0 100644 --- a/src/WireMock.Net.Minimal/Extensions/DictionaryExtensions.cs +++ b/src/WireMock.Net.Shared/Extensions/DictionaryExtensions.cs @@ -28,4 +28,4 @@ internal static class DictionaryExtensions value = default; return false; } -} \ No newline at end of file +} diff --git a/src/WireMock.Net.Shared/Properties/AssemblyInfo.cs b/src/WireMock.Net.Shared/Properties/AssemblyInfo.cs index 2b4ab3ad..9bb112fd 100644 --- a/src/WireMock.Net.Shared/Properties/AssemblyInfo.cs +++ b/src/WireMock.Net.Shared/Properties/AssemblyInfo.cs @@ -7,6 +7,7 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("WireMock.Net.GraphQL, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")] [assembly: InternalsVisibleTo("WireMock.Net.ProtoBuf, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")] [assembly: InternalsVisibleTo("WireMock.Net.Matchers.CSharpCode, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")] +[assembly: InternalsVisibleTo("WireMock.Net.OpenTelemetry, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")] // [assembly: InternalsVisibleTo("WireMock.Net.StandAlone, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")] [assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")] diff --git a/src/WireMock.Net.Shared/Settings/ActivityTracingOptions.cs b/src/WireMock.Net.Shared/Settings/ActivityTracingOptions.cs new file mode 100644 index 00000000..f022940b --- /dev/null +++ b/src/WireMock.Net.Shared/Settings/ActivityTracingOptions.cs @@ -0,0 +1,42 @@ +// Copyright © WireMock.Net + +using JetBrains.Annotations; + +namespace WireMock.Settings; + +/// +/// Options for controlling activity tracing in WireMock.Net. +/// These options control the creation of System.Diagnostics.Activity objects +/// but do not require any OpenTelemetry exporter dependencies. +/// +/// +/// To export traces to an OpenTelemetry collector, install the WireMock.Net.OpenTelemetry package +/// and configure the exporter using the provided extension methods. +/// +[PublicAPI] +public class ActivityTracingOptions +{ + /// + /// Gets or sets a value indicating whether to exclude admin interface requests from activity tracing. + /// Default is true. + /// + public bool ExcludeAdminRequests { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to record request body in trace attributes. + /// Default is false due to potential PII concerns. + /// + public bool RecordRequestBody { get; set; } + + /// + /// Gets or sets a value indicating whether to record response body in trace attributes. + /// Default is false due to potential PII concerns. + /// + public bool RecordResponseBody { get; set; } + + /// + /// Gets or sets a value indicating whether to record mapping match details in trace attributes. + /// Default is true. + /// + public bool RecordMatchDetails { get; set; } = true; +} diff --git a/src/WireMock.Net.Minimal/Settings/SimpleSettingsParser.cs b/src/WireMock.Net.Shared/Settings/SimpleSettingsParser.cs similarity index 94% rename from src/WireMock.Net.Minimal/Settings/SimpleSettingsParser.cs rename to src/WireMock.Net.Shared/Settings/SimpleSettingsParser.cs index a02e06d2..322d687f 100644 --- a/src/WireMock.Net.Minimal/Settings/SimpleSettingsParser.cs +++ b/src/WireMock.Net.Shared/Settings/SimpleSettingsParser.cs @@ -104,6 +104,21 @@ internal class SimpleSettingsParser }, defaultValue); } + public bool GetBoolWithDefault(string key1, string key2, bool defaultValue) + { + if (Contains(key1)) + { + return GetBoolValue(key1); + } + + if (Contains(key2)) + { + return GetBoolValue(key2); + } + + return defaultValue; + } + public bool GetBoolSwitchValue(string name) { return Contains(name); @@ -184,4 +199,4 @@ internal class SimpleSettingsParser var value = GetValue(name, values => values.FirstOrDefault()); return string.IsNullOrWhiteSpace(value) ? default : JsonUtils.DeserializeObject(value); } -} \ No newline at end of file +} diff --git a/src/WireMock.Net.Shared/Settings/WireMockServerSettings.cs b/src/WireMock.Net.Shared/Settings/WireMockServerSettings.cs index 86f99f38..811bc7f6 100644 --- a/src/WireMock.Net.Shared/Settings/WireMockServerSettings.cs +++ b/src/WireMock.Net.Shared/Settings/WireMockServerSettings.cs @@ -338,4 +338,15 @@ public class WireMockServerSettings /// [PublicAPI] public HandlebarsSettings? HandlebarsSettings { get; set; } + + /// + /// Gets or sets the activity tracing options. + /// When set (not null), WireMock.Net will create System.Diagnostics.Activity objects for request processing. + /// + /// + /// To export traces to an OpenTelemetry collector, install the WireMock.Net.OpenTelemetry package + /// and configure the exporter using the provided extension methods. + /// + [PublicAPI] + public ActivityTracingOptions? ActivityTracingOptions { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net.StandAlone/StandAloneApp.cs b/src/WireMock.Net.StandAlone/StandAloneApp.cs index 54889d80..a17d0b11 100644 --- a/src/WireMock.Net.StandAlone/StandAloneApp.cs +++ b/src/WireMock.Net.StandAlone/StandAloneApp.cs @@ -10,6 +10,9 @@ using WireMock.Exceptions; using WireMock.Logging; using WireMock.Server; using WireMock.Settings; +#if OPENTELEMETRY_SUPPORTED +using WireMock.OpenTelemetry; +#endif namespace WireMock.Net.StandAlone; @@ -37,6 +40,39 @@ public static class StandAloneApp return server; } +#if OPENTELEMETRY_SUPPORTED + /// + /// Start WireMock.Net standalone Server based on the WireMockServerSettings with OpenTelemetry tracing. + /// + /// The WireMockServerSettings + /// The OpenTelemetry options for exporting traces. + [PublicAPI] + public static WireMockServer Start(WireMockServerSettings settings, OpenTelemetryOptions? openTelemetryOptions) + { + Guard.NotNull(settings); + + // Wire up OpenTelemetry OTLP exporter if options are provided + if (openTelemetryOptions is not null) + { + // Enable activity tracing in settings so middleware creates activities + // Only set ExcludeAdminRequests if not already configured + settings.ActivityTracingOptions ??= new ActivityTracingOptions + { + ExcludeAdminRequests = openTelemetryOptions.ExcludeAdminRequests + }; + + var existingRegistration = settings.AdditionalServiceRegistration; + settings.AdditionalServiceRegistration = services => + { + existingRegistration?.Invoke(services); + services.AddWireMockOpenTelemetry(openTelemetryOptions); + }; + } + + return Start(settings); + } +#endif + /// /// Start WireMock.Net standalone Server based on the commandline arguments. /// @@ -71,7 +107,13 @@ public static class StandAloneApp settings.Logger?.Info("Version [{0}]", Version); settings.Logger?.Debug("Server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'"))); +#if OPENTELEMETRY_SUPPORTED + // Parse OpenTelemetry options separately using the OTEL project parser + OpenTelemetryOptionsParser.TryParseArguments(args, Environment.GetEnvironmentVariables(), out var openTelemetryOptions); + server = Start(settings, openTelemetryOptions); +#else server = Start(settings); +#endif return true; } diff --git a/src/WireMock.Net.StandAlone/WireMock.Net.StandAlone.csproj b/src/WireMock.Net.StandAlone/WireMock.Net.StandAlone.csproj index c1715a86..8dbdd8dc 100644 --- a/src/WireMock.Net.StandAlone/WireMock.Net.StandAlone.csproj +++ b/src/WireMock.Net.StandAlone/WireMock.Net.StandAlone.csproj @@ -35,6 +35,11 @@ USE_ASPNETCORE;NET46 + + + $(DefineConstants);OPENTELEMETRY_SUPPORTED + + all @@ -42,4 +47,9 @@ + + + + + \ No newline at end of file diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index f07348a0..dd897284 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -37,4 +37,9 @@ + + + + + \ No newline at end of file diff --git a/test/WireMock.Net.Tests/OpenTelemetry/OpenTelemetryOptionsParserTests.cs b/test/WireMock.Net.Tests/OpenTelemetry/OpenTelemetryOptionsParserTests.cs new file mode 100644 index 00000000..43b7cdcd --- /dev/null +++ b/test/WireMock.Net.Tests/OpenTelemetry/OpenTelemetryOptionsParserTests.cs @@ -0,0 +1,42 @@ +// Copyright © WireMock.Net + +#if NET6_0_OR_GREATER +using System; +using FluentAssertions; +using WireMock.OpenTelemetry; +using Xunit; + +namespace WireMock.Net.Tests.OpenTelemetry; + +public class OpenTelemetryOptionsParserTests +{ + [Fact] + public void TryParseArguments_Enabled_ShouldReturnOptions() + { + // Act + var result = OpenTelemetryOptionsParser.TryParseArguments(new[] + { + "--OpenTelemetryEnabled", "true", + "--OpenTelemetryExcludeAdminRequests", "false", + "--OpenTelemetryOtlpExporterEndpoint", "http://localhost:4317" + }, null, out var options); + + // Assert + result.Should().BeTrue(); + options.Should().NotBeNull(); + options!.ExcludeAdminRequests.Should().BeFalse(); + options.OtlpExporterEndpoint.Should().Be("http://localhost:4317"); + } + + [Fact] + public void TryParseArguments_NotEnabled_ShouldReturnNull() + { + // Act + var result = OpenTelemetryOptionsParser.TryParseArguments(Array.Empty(), null, out var options); + + // Assert + result.Should().BeTrue(); + options.Should().BeNull(); + } +} +#endif diff --git a/test/WireMock.Net.Tests/OpenTelemetry/WireMockOpenTelemetryExtensionsTests.cs b/test/WireMock.Net.Tests/OpenTelemetry/WireMockOpenTelemetryExtensionsTests.cs new file mode 100644 index 00000000..b754294d --- /dev/null +++ b/test/WireMock.Net.Tests/OpenTelemetry/WireMockOpenTelemetryExtensionsTests.cs @@ -0,0 +1,43 @@ +// Copyright © WireMock.Net + +#if NET6_0_OR_GREATER +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using WireMock.OpenTelemetry; +using Xunit; + +namespace WireMock.Net.Tests.OpenTelemetry; + +public class WireMockOpenTelemetryExtensionsTests +{ + [Fact] + public void AddWireMockOpenTelemetry_WithNullOptions_ShouldNotAddServices() + { + // Arrange + var services = new ServiceCollection(); + var initialCount = services.Count; + + // Act + var result = services.AddWireMockOpenTelemetry(null); + + // Assert + result.Should().BeSameAs(services); + services.Count.Should().Be(initialCount); + } + + [Fact] + public void AddWireMockOpenTelemetry_WithOptions_ShouldAddServices() + { + // Arrange + var services = new ServiceCollection(); + var initialCount = services.Count; + + // Act + var result = services.AddWireMockOpenTelemetry(new OpenTelemetryOptions()); + + // Assert + result.Should().BeSameAs(services); + services.Count.Should().BeGreaterThan(initialCount); + } +} +#endif diff --git a/test/WireMock.Net.Tests/Owin/ActivityTracing/WireMockActivitySourceTests.cs b/test/WireMock.Net.Tests/Owin/ActivityTracing/WireMockActivitySourceTests.cs new file mode 100644 index 00000000..044ef395 --- /dev/null +++ b/test/WireMock.Net.Tests/Owin/ActivityTracing/WireMockActivitySourceTests.cs @@ -0,0 +1,195 @@ +// Copyright © WireMock.Net + +#if NET6_0_OR_GREATER +using System; +using System.Diagnostics; +using System.Linq; +using FluentAssertions; +using Moq; +using WireMock.Logging; +using WireMock.Matchers.Request; +using WireMock.Models; +using WireMock.Owin.ActivityTracing; +using WireMock.Util; +using Xunit; + +namespace WireMock.Net.Tests.Owin.ActivityTracing; + +public class WireMockActivitySourceTests +{ + [Fact] + public void EnrichWithRequest_ShouldSetRequestTagsAndBody_WhenEnabled() + { + // Arrange + using var activity = new Activity("test").Start(); + var request = new RequestMessage( + new UrlDetails("http://localhost/api/orders"), + "POST", + "127.0.0.1", + new BodyData { BodyAsString = "payload" }); + + var options = new ActivityTracingOptions + { + RecordRequestBody = true + }; + + // Act + WireMockActivitySource.EnrichWithRequest(activity, request, options); + + // Assert + activity.GetTagItem(WireMockSemanticConventions.HttpMethod).Should().Be("POST"); + activity.GetTagItem(WireMockSemanticConventions.HttpUrl).Should().Be("http://localhost/api/orders"); + activity.GetTagItem(WireMockSemanticConventions.HttpPath).Should().Be("/api/orders"); + activity.GetTagItem(WireMockSemanticConventions.HttpHost).Should().Be("localhost"); + activity.GetTagItem(WireMockSemanticConventions.ClientAddress).Should().Be("127.0.0.1"); + activity.GetTagItem(WireMockSemanticConventions.RequestBody).Should().Be("payload"); + } + + [Fact] + public void EnrichWithResponse_ShouldSetStatusAndBody_WhenEnabled() + { + // Arrange + using var activity = new Activity("test").Start(); + var response = new ResponseMessage + { + StatusCode = 200, + BodyData = new BodyData { BodyAsString = "ok" } + }; + + var options = new ActivityTracingOptions + { + RecordResponseBody = true + }; + + // Act + WireMockActivitySource.EnrichWithResponse(activity, response, options); + + // Assert + activity.GetTagItem(WireMockSemanticConventions.HttpStatusCode).Should().Be(200); + activity.GetTagItem("otel.status_code").Should().Be("OK"); + activity.GetTagItem(WireMockSemanticConventions.ResponseBody).Should().Be("ok"); + } + + [Fact] + public void EnrichWithResponse_ShouldSetErrorStatus_ForNonSuccess() + { + // Arrange + using var activity = new Activity("test").Start(); + var response = new ResponseMessage + { + StatusCode = 500 + }; + + // Act + WireMockActivitySource.EnrichWithResponse(activity, response, new ActivityTracingOptions()); + + // Assert + activity.GetTagItem(WireMockSemanticConventions.HttpStatusCode).Should().Be(500); + activity.GetTagItem("otel.status_code").Should().Be("ERROR"); + } + + [Fact] + public void EnrichWithRequest_ShouldNotRecordBody_WhenDisabled() + { + // Arrange + using var activity = new Activity("test").Start(); + var request = new RequestMessage( + new UrlDetails("http://localhost/api/orders"), + "POST", + "127.0.0.1", + new BodyData { BodyAsString = "payload" }); + + var options = new ActivityTracingOptions + { + RecordRequestBody = false + }; + + // Act + WireMockActivitySource.EnrichWithRequest(activity, request, options); + + // Assert + activity.GetTagItem(WireMockSemanticConventions.RequestBody).Should().BeNull(); + } + + [Fact] + public void EnrichWithResponse_ShouldNotRecordBody_WhenDisabled() + { + // Arrange + using var activity = new Activity("test").Start(); + var response = new ResponseMessage + { + StatusCode = 200, + BodyData = new BodyData { BodyAsString = "ok" } + }; + + var options = new ActivityTracingOptions + { + RecordResponseBody = false + }; + + // Act + WireMockActivitySource.EnrichWithResponse(activity, response, options); + + // Assert + activity.GetTagItem(WireMockSemanticConventions.ResponseBody).Should().BeNull(); + } + + [Fact] + public void EnrichWithLogEntry_ShouldSkipMatchDetails_WhenDisabled() + { + // Arrange + using var activity = new Activity("test").Start(); + var request = new RequestMessage( + new UrlDetails("http://localhost/api/orders"), + "GET", + "127.0.0.1"); + var response = new ResponseMessage { StatusCode = 200 }; + + var matchResult = new Mock(); + matchResult.SetupGet(r => r.IsPerfectMatch).Returns(true); + matchResult.SetupGet(r => r.TotalScore).Returns(1.0); + + var logEntry = new LogEntry + { + Guid = Guid.NewGuid(), + RequestMessage = request, + ResponseMessage = response, + RequestMatchResult = matchResult.Object, + MappingGuid = Guid.NewGuid(), + MappingTitle = "test-mapping" + }; + + var options = new ActivityTracingOptions + { + RecordMatchDetails = false + }; + + // Act + WireMockActivitySource.EnrichWithLogEntry(activity, logEntry, options); + + // Assert + activity.GetTagItem(WireMockSemanticConventions.RequestGuid).Should().Be(logEntry.Guid.ToString()); + activity.Tags.Should().NotContain(tag => tag.Key == WireMockSemanticConventions.MappingGuid); + activity.Tags.Should().NotContain(tag => tag.Key == WireMockSemanticConventions.MappingTitle); + activity.Tags.Should().NotContain(tag => tag.Key == WireMockSemanticConventions.MatchScore); + } + + [Fact] + public void RecordException_ShouldSetExceptionTags() + { + // Arrange + using var activity = new Activity("test").Start(); + var exception = new InvalidOperationException("boom"); + + // Act + WireMockActivitySource.RecordException(activity, exception); + + // Assert + activity.GetTagItem("otel.status_code").Should().Be("ERROR"); + activity.GetTagItem("otel.status_description").Should().Be("boom"); + activity.GetTagItem("exception.type").Should().Be(typeof(InvalidOperationException).FullName); + activity.GetTagItem("exception.message").Should().Be("boom"); + activity.GetTagItem("exception.stacktrace").Should().NotBeNull(); + } +} +#endif diff --git a/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs b/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs index cb8954de..81dcd6f5 100644 --- a/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs +++ b/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs @@ -13,6 +13,9 @@ using WireMock.Util; using WireMock.Logging; using WireMock.Matchers; using System.Collections.Generic; +#if NET6_0_OR_GREATER +using System.Diagnostics; +#endif using WireMock.Admin.Mappings; using WireMock.Admin.Requests; using WireMock.Settings; @@ -21,6 +24,9 @@ using WireMock.Handlers; using WireMock.Matchers.Request; using WireMock.ResponseBuilders; using WireMock.RequestBuilders; +#if NET6_0_OR_GREATER +using WireMock.Owin.ActivityTracing; +#endif #if NET452 using Microsoft.Owin; using IContext = Microsoft.Owin.IOwinContext; @@ -289,4 +295,90 @@ public class WireMockMiddlewareTests _mappings.Should().HaveCount(1); } + +#if NET6_0_OR_GREATER + [Fact] + public async Task WireMockMiddleware_Invoke_AdminPath_WithExcludeAdminRequests_ShouldNotStartActivity() + { + // Arrange + var request = new RequestMessage(new UrlDetails("http://localhost/__admin/health"), "GET", "::1"); + _requestMapperMock.Setup(m => m.MapAsync(It.IsAny(), It.IsAny())).ReturnsAsync(request); + + _optionsMock.SetupGet(o => o.ActivityTracingOptions).Returns(new WireMock.Owin.ActivityTracing.ActivityTracingOptions + { + ExcludeAdminRequests = true + }); + + var activityStarted = false; + using var listener = new ActivityListener + { + ShouldListenTo = source => source.Name == WireMockActivitySource.SourceName, + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = _ => activityStarted = true + }; + + ActivitySource.AddActivityListener(listener); + + // Act + await _sut.Invoke(_contextMock.Object).ConfigureAwait(false); + + // Assert + activityStarted.Should().BeFalse(); + } + + [Fact] + public async Task WireMockMiddleware_Invoke_NonAdminPath_WithTracingEnabled_ShouldStartActivity() + { + // Arrange + var request = new RequestMessage(new UrlDetails("http://localhost/api/orders"), "GET", "::1"); + _requestMapperMock.Setup(m => m.MapAsync(It.IsAny(), It.IsAny())).ReturnsAsync(request); + + _optionsMock.SetupGet(o => o.ActivityTracingOptions).Returns(new WireMock.Owin.ActivityTracing.ActivityTracingOptions + { + ExcludeAdminRequests = true + }); + + var activityStarted = false; + using var listener = new ActivityListener + { + ShouldListenTo = source => source.Name == WireMockActivitySource.SourceName, + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = _ => activityStarted = true + }; + + ActivitySource.AddActivityListener(listener); + + // Act + await _sut.Invoke(_contextMock.Object).ConfigureAwait(false); + + // Assert + activityStarted.Should().BeTrue(); + } + + [Fact] + public async Task WireMockMiddleware_Invoke_NonAdminPath_WithoutTracingOptions_ShouldNotStartActivity() + { + // Arrange + var request = new RequestMessage(new UrlDetails("http://localhost/api/orders"), "GET", "::1"); + _requestMapperMock.Setup(m => m.MapAsync(It.IsAny(), It.IsAny())).ReturnsAsync(request); + + _optionsMock.SetupGet(o => o.ActivityTracingOptions).Returns((WireMock.Owin.ActivityTracing.ActivityTracingOptions?)null); + + var activityStarted = false; + using var listener = new ActivityListener + { + ShouldListenTo = source => source.Name == WireMockActivitySource.SourceName, + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = _ => activityStarted = true + }; + + ActivitySource.AddActivityListener(listener); + + // Act + await _sut.Invoke(_contextMock.Object).ConfigureAwait(false); + + // Assert + activityStarted.Should().BeFalse(); + } +#endif } \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Settings/SimpleSettingsParserTests.cs b/test/WireMock.Net.Tests/Settings/SimpleSettingsParserTests.cs index cbbd7f08..88c56ae0 100644 --- a/test/WireMock.Net.Tests/Settings/SimpleSettingsParserTests.cs +++ b/test/WireMock.Net.Tests/Settings/SimpleSettingsParserTests.cs @@ -112,6 +112,23 @@ public class SimpleSettingsParserTests Check.That(value3).IsEqualTo(true); } + [Fact] + public void SimpleCommandLineParser_Parse_GetBoolWithDefault() + { + // Assign + _parser.Parse(new[] { "--test1", "true", "--test2", "false" }); + + // Act + bool value1 = _parser.GetBoolWithDefault("test1", "test1_fallback", defaultValue: false); + bool value2 = _parser.GetBoolWithDefault("missing", "test2", defaultValue: true); + bool value3 = _parser.GetBoolWithDefault("missing1", "missing2", defaultValue: true); + + // Assert + Check.That(value1).IsEqualTo(true); + Check.That(value2).IsEqualTo(false); + Check.That(value3).IsEqualTo(true); + } + [Fact] public void SimpleCommandLineParser_Parse_Environment_GetBoolValue() { diff --git a/test/WireMock.Net.Tests/Settings/WireMockServerSettingsParserTests.cs b/test/WireMock.Net.Tests/Settings/WireMockServerSettingsParserTests.cs index 56d4a2d1..274c1468 100644 --- a/test/WireMock.Net.Tests/Settings/WireMockServerSettingsParserTests.cs +++ b/test/WireMock.Net.Tests/Settings/WireMockServerSettingsParserTests.cs @@ -34,4 +34,26 @@ public class WireMockServerSettingsParserTests settings.Should().NotBeNull(); settings!.AdminPath.Should().Be("/__admin"); } + + [Fact] + public void TryParseArguments_With_ActivityTracingEnabled_ShouldParseOptions() + { + // Act + var result = WireMockServerSettingsParser.TryParseArguments(new[] + { + "--ActivityTracingEnabled", "true", + "--ActivityTracingExcludeAdminRequests", "false", + "--ActivityTracingRecordRequestBody", "true", + "--ActivityTracingRecordResponseBody", "true" + }, null, out var settings); + + // Assert + result.Should().BeTrue(); + settings.Should().NotBeNull(); + settings!.ActivityTracingOptions.Should().NotBeNull(); + settings.ActivityTracingOptions!.ExcludeAdminRequests.Should().BeFalse(); + settings.ActivityTracingOptions.RecordRequestBody.Should().BeTrue(); + settings.ActivityTracingOptions.RecordResponseBody.Should().BeTrue(); + settings.ActivityTracingOptions.RecordMatchDetails.Should().BeTrue(); + } } \ No newline at end of file diff --git a/test/WireMock.Net.Tests/WireMockServer.Settings.cs b/test/WireMock.Net.Tests/WireMockServer.Settings.cs index 91defc85..7a82e8a9 100644 --- a/test/WireMock.Net.Tests/WireMockServer.Settings.cs +++ b/test/WireMock.Net.Tests/WireMockServer.Settings.cs @@ -194,4 +194,41 @@ public class WireMockServerSettingsTests var options = server.GetPrivateFieldValue("_options"); Check.That(options.RequestLogExpirationDuration).IsEqualTo(1); } + +#if NET6_0_OR_GREATER + [Fact] + public void WireMockServer_WireMockServerSettings_ActivityTracingOptions_AreMappedToMiddlewareOptions() + { + // Assign and Act + var server = WireMockServer.Start(new WireMockServerSettings + { + ActivityTracingOptions = new ActivityTracingOptions + { + ExcludeAdminRequests = false, + RecordRequestBody = true, + RecordResponseBody = true, + RecordMatchDetails = false + } + }); + + // Assert + var options = server.GetPrivateFieldValue("_options"); + options.ActivityTracingOptions.Should().NotBeNull(); + options.ActivityTracingOptions!.ExcludeAdminRequests.Should().BeFalse(); + options.ActivityTracingOptions.RecordRequestBody.Should().BeTrue(); + options.ActivityTracingOptions.RecordResponseBody.Should().BeTrue(); + options.ActivityTracingOptions.RecordMatchDetails.Should().BeFalse(); + } + + [Fact] + public void WireMockServer_WireMockServerSettings_Without_ActivityTracingOptions_ShouldNotSetMiddlewareOptions() + { + // Assign and Act + var server = WireMockServer.Start(new WireMockServerSettings()); + + // Assert + var options = server.GetPrivateFieldValue("_options"); + options.ActivityTracingOptions.Should().BeNull(); + } +#endif } \ No newline at end of file From 4b602dd777cc45b70fa62681d8b97f16cdca6305 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 18 Jan 2026 17:56:07 +0100 Subject: [PATCH 4/4] Small updates to WireMock.Net.OpenTelemetry --- README.md | 1 + .../AspireApp1.AppHostOriginal/Program.cs | 2 +- .../WireMock.Net.OpenTelemetryDemo/Program.cs | 7 +++-- .../WireMock.Net.OpenTelemetryDemo.csproj | 28 +++++++++++-------- .../ActivityTracing/WireMockActivitySource.cs | 3 +- .../WireMockSemanticConventions.cs | 8 +----- .../WireMock.Net.OpenTelemetry.csproj | 4 +-- .../WireMockOpenTelemetryExtensions.cs | 3 +- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index bd4a2c27..29b4f48e 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w |   **WireMock.Net.MimePart** | [![NuGet Badge WireMock.Net.MimePart](https://img.shields.io/nuget/v/WireMock.Net.MimePart)](https://www.nuget.org/packages/WireMock.Net.MimePart) | [![MyGet Badge WireMock.Net.MimePart](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.MimePart?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.MimePart) |   **WireMock.Net.GraphQL** | [![NuGet Badge WireMock.Net.GraphQL](https://img.shields.io/nuget/v/WireMock.Net.GraphQL)](https://www.nuget.org/packages/WireMock.Net.GraphQL) | [![MyGet Badge WireMock.Net.GraphQL](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.GraphQL?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.GraphQL) |   **WireMock.Net.ProtoBuf** | [![NuGet Badge WireMock.Net.ProtoBuf](https://img.shields.io/nuget/v/WireMock.Net.ProtoBuf)](https://www.nuget.org/packages/WireMock.Net.ProtoBuf) | [![MyGet Badge WireMock.Net.ProtoBuf](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.ProtoBuf?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.ProtoBuf) +|   **WireMock.Net.OpenTelemetry** | [![NuGet Badge WireMock.Net.OpenTelemetry](https://img.shields.io/nuget/v/WireMock.Net.OpenTelemetry)](https://www.nuget.org/packages/WireMock.Net.ProtoBuf) | [![MyGet Badge WireMock.Net.OpenTelemetry](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.OpenTelemetry?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.OpenTelemetry) | | | | |   **WireMock.Net.RestClient** | [![NuGet Badge WireMock.Net.RestClient](https://img.shields.io/nuget/v/WireMock.Net.RestClient)](https://www.nuget.org/packages/WireMock.Net.RestClient) | [![MyGet Badge WireMock.Net.RestClient](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.RestClient?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.RestClient) |   **WireMock.Org.RestClient** | [![NuGet Badge WireMock.Org.RestClient](https://img.shields.io/nuget/v/WireMock.Org.RestClient)](https://www.nuget.org/packages/WireMock.Org.RestClient) | [![MyGet Badge WireMock.Org.RestClient](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Org.RestClient?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Org.RestClient) diff --git a/examples-Aspire/AspireApp1.AppHostOriginal/Program.cs b/examples-Aspire/AspireApp1.AppHostOriginal/Program.cs index db90f730..d8288c47 100644 --- a/examples-Aspire/AspireApp1.AppHostOriginal/Program.cs +++ b/examples-Aspire/AspireApp1.AppHostOriginal/Program.cs @@ -6,4 +6,4 @@ builder.AddProject("webfrontend") .WithExternalHttpEndpoints() .WithReference(apiService); -builder.Build().Run(); \ No newline at end of file +await builder.Build().RunAsync(); \ No newline at end of file diff --git a/examples/WireMock.Net.OpenTelemetryDemo/Program.cs b/examples/WireMock.Net.OpenTelemetryDemo/Program.cs index d12df50d..e7664ba5 100644 --- a/examples/WireMock.Net.OpenTelemetryDemo/Program.cs +++ b/examples/WireMock.Net.OpenTelemetryDemo/Program.cs @@ -4,11 +4,11 @@ using OpenTelemetry; using OpenTelemetry.Trace; +using WireMock.OpenTelemetry; using WireMock.Server; using WireMock.Settings; using WireMock.RequestBuilders; using WireMock.ResponseBuilders; -using WireMock.OpenTelemetry; Console.WriteLine("=== WireMock.Net OpenTelemetry Tracing Demo ===\n"); @@ -31,8 +31,9 @@ Console.WriteLine("=== WireMock.Net OpenTelemetry Tracing Demo ===\n"); // Option 1: Custom TracerProvider with Console exporter for this demo using var tracerProvider = Sdk.CreateTracerProviderBuilder() .AddWireMockInstrumentation(new OpenTelemetryOptions() { ExcludeAdminRequests = true }) - .AddHttpClientInstrumentation() // HTTP client traces (for our test requests) - .AddConsoleExporter() // Export traces to console for demo purposes + .AddHttpClientInstrumentation() // HTTP client traces (for our test requests) + .AddConsoleExporter() // Export traces to console for demo purposes + .AddOtlpExporter() // Export to real OTLP collector (e.g. Jaeger, Tempo, etc.) .Build(); Console.WriteLine("Console Exporter configured to visualize:"); diff --git a/examples/WireMock.Net.OpenTelemetryDemo/WireMock.Net.OpenTelemetryDemo.csproj b/examples/WireMock.Net.OpenTelemetryDemo/WireMock.Net.OpenTelemetryDemo.csproj index e85ad669..5608d759 100644 --- a/examples/WireMock.Net.OpenTelemetryDemo/WireMock.Net.OpenTelemetryDemo.csproj +++ b/examples/WireMock.Net.OpenTelemetryDemo/WireMock.Net.OpenTelemetryDemo.csproj @@ -1,15 +1,21 @@ - - Exe - net8.0 - enable - enable - + + Exe + net8.0 + enable + enable + + + + + + + + + + + + - - - - - diff --git a/src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockActivitySource.cs b/src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockActivitySource.cs index d7db92c4..3524d0ad 100644 --- a/src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockActivitySource.cs +++ b/src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockActivitySource.cs @@ -2,7 +2,6 @@ #if ACTIVITY_TRACING_SUPPORTED using System; -using System.Collections.Generic; using System.Diagnostics; using WireMock.Logging; @@ -198,4 +197,4 @@ public static class WireMockActivitySource activity.SetTag("exception.stacktrace", exception.ToString()); } } -#endif +#endif \ No newline at end of file diff --git a/src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockSemanticConventions.cs b/src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockSemanticConventions.cs index 66360cba..57de76e6 100644 --- a/src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockSemanticConventions.cs +++ b/src/WireMock.Net.Minimal/Owin/ActivityTracing/WireMockSemanticConventions.cs @@ -1,11 +1,5 @@ // Copyright © WireMock.Net -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace WireMock.Owin.ActivityTracing; /// @@ -31,4 +25,4 @@ internal static class WireMockSemanticConventions public const string RequestGuid = "wiremock.request.guid"; public const string RequestBody = "wiremock.request.body"; public const string ResponseBody = "wiremock.response.body"; -} +} \ No newline at end of file diff --git a/src/WireMock.Net.OpenTelemetry/WireMock.Net.OpenTelemetry.csproj b/src/WireMock.Net.OpenTelemetry/WireMock.Net.OpenTelemetry.csproj index c21960e4..f12c9b86 100644 --- a/src/WireMock.Net.OpenTelemetry/WireMock.Net.OpenTelemetry.csproj +++ b/src/WireMock.Net.OpenTelemetry/WireMock.Net.OpenTelemetry.csproj @@ -1,13 +1,14 @@ + OpenTelemetry exporter support for WireMock.Net WireMock.Net.OpenTelemetry Petr Houška net6.0;net8.0 true + WireMock.OpenTelemetry wiremock;opentelemetry;otel;tracing;telemetry - WireMock {C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F} true true @@ -26,7 +27,6 @@ - diff --git a/src/WireMock.Net.OpenTelemetry/WireMockOpenTelemetryExtensions.cs b/src/WireMock.Net.OpenTelemetry/WireMockOpenTelemetryExtensions.cs index 8626a8a2..d0b8f95a 100644 --- a/src/WireMock.Net.OpenTelemetry/WireMockOpenTelemetryExtensions.cs +++ b/src/WireMock.Net.OpenTelemetry/WireMockOpenTelemetryExtensions.cs @@ -2,7 +2,6 @@ using System; using Microsoft.Extensions.DependencyInjection; -using OpenTelemetry; using OpenTelemetry.Resources; using OpenTelemetry.Trace; @@ -112,4 +111,4 @@ public static class WireMockOpenTelemetryExtensions return tracing; } -} +} \ No newline at end of file