├── .editorconfig
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE.md
└── workflows
│ ├── perftests.yml
│ ├── pr-analysis-codeql.yml
│ ├── pr-analysis-devskim.yml
│ ├── pr-validation.yml
│ └── release.yml
├── .gitignore
├── Build.ps1
├── CHANGES.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DEVELOPMENT.md
├── Directory.Build.props
├── Directory.Packages.props
├── LICENSE
├── README.md
├── RunPerfTests.ps1
├── SECURITY.md
├── assets
└── Serilog.snk
├── sample
├── AppConfigDemo
│ ├── App.config
│ ├── AppConfigDemo.csproj
│ └── Program.cs
├── CombinedConfigDemo
│ ├── CombinedConfigDemo.csproj
│ ├── Program.cs
│ └── appsettings.json
├── CustomLogEventFormatterDemo
│ ├── CustomLogEventFormatterDemo.csproj
│ ├── FlatLogEventFormatter.cs
│ └── Program.cs
├── NetStandardDemo
│ ├── NetStandardDemoApp
│ │ ├── NetStandardDemoApp.csproj
│ │ └── Program.cs
│ └── NetStandardDemoLib
│ │ ├── Initializer.cs
│ │ └── NetStandardDemoLib.csproj
└── WorkerServiceDemo
│ ├── CustomLogEventFormatter.cs
│ ├── Program.cs
│ ├── Properties
│ └── launchSettings.json
│ ├── Structured.cs
│ ├── Worker.cs
│ ├── WorkerServiceDemo.csproj
│ ├── appsettings.Development.json
│ └── appsettings.json
├── serilog-sinks-mssqlserver.sln
├── src
└── Serilog.Sinks.MSSqlServer
│ ├── Configuration
│ ├── Extensions
│ │ ├── Hybrid
│ │ │ └── LoggerConfigurationMSSqlServerExtensions.cs
│ │ └── Microsoft.Extensions.Configuration
│ │ │ └── LoggerConfigurationMSSqlServerExtensions.cs
│ ├── Factories
│ │ ├── IMSSqlServerAuditSinkFactory.cs
│ │ ├── IMSSqlServerSinkFactory.cs
│ │ ├── IPeriodicBatchingSinkFactory.cs
│ │ ├── MSSqlServerAuditSinkFactory.cs
│ │ ├── MSSqlServerSinkFactory.cs
│ │ └── PeriodicBatchingSinkFactory.cs
│ └── Implementations
│ │ ├── Microsoft.Extensions.Configuration
│ │ ├── ApplyMicrosoftExtensionsConfiguration.cs
│ │ ├── IApplyMicrosoftExtensionsConfiguration.cs
│ │ ├── IMicrosoftExtensionsColumnOptionsProvider.cs
│ │ ├── IMicrosoftExtensionsConnectionStringProvider.cs
│ │ ├── IMicrosoftExtensionsSinkOptionsProvider.cs
│ │ ├── MicrosoftExtensionsColumnOptionsProvider.cs
│ │ ├── MicrosoftExtensionsConnectionStringProvider.cs
│ │ └── MicrosoftExtensionsSinkOptionsProvider.cs
│ │ ├── SetProperty.cs
│ │ └── System.Configuration
│ │ ├── ApplySystemConfiguration.cs
│ │ ├── ColumnCollection.cs
│ │ ├── ColumnConfig.cs
│ │ ├── IApplySystemConfiguration.cs
│ │ ├── ISystemConfigurationColumnOptionsProvider.cs
│ │ ├── ISystemConfigurationConnectionStringProvider.cs
│ │ ├── ISystemConfigurationSinkOptionsProvider.cs
│ │ ├── MSSqlServerConfigurationSection.cs
│ │ ├── SetPropertyValueOrigin.cs
│ │ ├── StandardColumnCollection.cs
│ │ ├── StandardColumnConfig.cs
│ │ ├── StandardColumnConfigException.cs
│ │ ├── StandardColumnConfigId.cs
│ │ ├── StandardColumnConfigLevel.cs
│ │ ├── StandardColumnConfigLogEvent.cs
│ │ ├── StandardColumnConfigMessage.cs
│ │ ├── StandardColumnConfigMessageTemplate.cs
│ │ ├── StandardColumnConfigProperties.cs
│ │ ├── StandardColumnConfigSpanId.cs
│ │ ├── StandardColumnConfigTimeStamp.cs
│ │ ├── StandardColumnConfigTraceId.cs
│ │ ├── SystemConfigurationColumnOptionsProvider.cs
│ │ ├── SystemConfigurationConnectionStringProvider.cs
│ │ ├── SystemConfigurationSinkOptionsProvider.cs
│ │ └── ValueConfigElement.cs
│ ├── Extensions
│ └── StringExtensions.cs
│ ├── GlobalSuppressions.cs
│ ├── Images
│ └── serilog-sink-nuget.png
│ ├── Serilog.Sinks.MSSqlServer.csproj
│ └── Sinks
│ └── MSSqlServer
│ ├── ColumnOptions
│ ├── ColumnOptions.cs
│ ├── ExceptionColumnOptions.cs
│ ├── FinalizeConfigurationForSinkConstructor.cs
│ ├── IdColumnOptions.cs
│ ├── LevelColumnOptions.cs
│ ├── LogEventColumnOptions.cs
│ ├── MessageColumnOptions.cs
│ ├── MessageTemplateColumnOptions.cs
│ ├── PropertiesColumnOptions.cs
│ ├── SpanIdColumnOptions.cs
│ ├── TimeStampColumnOptions.cs
│ └── TraceIdColumnOptions.cs
│ ├── Dependencies
│ ├── SinkDependencies.cs
│ └── SinkDependenciesFactory.cs
│ ├── MSSqlServerAuditSink.cs
│ ├── MSSqlServerSink.cs
│ ├── MSSqlServerSinkOptions.cs
│ ├── Options
│ └── SinkOptions.cs
│ ├── Output
│ ├── AdditionalColumnDataGenerator.cs
│ ├── ColumnHierarchicalPropertyValueResolver.cs
│ ├── ColumnSimplePropertyValueResolver.cs
│ ├── IAdditionalColumnDataGenerator.cs
│ ├── IColumnHierarchicalPropertyValueResolver.cs
│ ├── IColumnSimplePropertyValueResolver.cs
│ ├── ILogEventDataGenerator.cs
│ ├── IStandardColumnDataGenerator.cs
│ ├── IXmlPropertyFormatter.cs
│ ├── JsonLogEventFormatter.cs
│ ├── LogEventDataGenerator.cs
│ ├── StandardColumnDataGenerator.cs
│ └── XmlPropertyFormatter.cs
│ ├── Platform
│ ├── DataTableCreator.cs
│ ├── IDataTableCreator.cs
│ ├── ISqlBulkBatchWriter.cs
│ ├── ISqlCommandExecutor.cs
│ ├── ISqlCommandFactory.cs
│ ├── ISqlConnectionFactory.cs
│ ├── ISqlCreateDatabaseWriter.cs
│ ├── ISqlCreateTableWriter.cs
│ ├── ISqlLogEventWriter.cs
│ ├── ISqlWriter.cs
│ ├── SqlBulkBatchWriter.cs
│ ├── SqlClient
│ │ ├── ISqlBulkCopyWrapper.cs
│ │ ├── ISqlCommandWrapper.cs
│ │ ├── ISqlConnectionStringBuilderWrapper.cs
│ │ ├── ISqlConnectionWrapper.cs
│ │ ├── SqlBulkCopyWrapper.cs
│ │ ├── SqlCommandWrapper.cs
│ │ ├── SqlConnectionStringBuilderWrapper.cs
│ │ └── SqlConnectionWrapper.cs
│ ├── SqlCommandExecutor.cs
│ ├── SqlCommandFactory.cs
│ ├── SqlConnectionFactory.cs
│ ├── SqlCreateDatabaseWriter.cs
│ ├── SqlCreateTableWriter.cs
│ ├── SqlDatabaseCreator.cs
│ ├── SqlInsertStatementWriter.cs
│ └── SqlTableCreator.cs
│ ├── SqlColumn.cs
│ ├── SqlDataTypes.cs
│ └── StandardColumn.cs
└── test
├── Serilog.Sinks.MSSqlServer.PerformanceTests
├── Misc
│ ├── AuditSinkExtendedBenchmarks.cs
│ ├── AuditSinkQuickBenchmarks.cs
│ ├── SinkExtendedBenchmarks.cs
│ └── SinkQuickBenchmarks.cs
├── Program.cs
├── Serilog.Sinks.MSSqlServer.PerformanceTests.csproj
└── Sinks
│ └── MSSqlServer
│ └── Platform
│ ├── SqlBulkBatchWriterBenchmarks.cs
│ └── SqlInsertStatementWriterBenchmarks.cs
└── Serilog.Sinks.MSSqlServer.Tests
├── App.config
├── Configuration
├── Extensions
│ └── Hybrid
│ │ ├── ConfigurationExtensionsTests.cs
│ │ └── LoggerConfigurationMSSqlServerExtensionsTests.cs
├── Factories
│ ├── MSSqlServerAuditSinkFactoryTests.cs
│ ├── MSSqlServerSinkFactoryTests.cs
│ └── PeriodicBatchingSinkFactoryTests.cs
└── Implementations
│ ├── Microsoft.Extensions.Configuration
│ ├── ApplyMicrosoftExtensionsConfigurationTests.cs
│ ├── MicrosoftExtensionsColumnOptionsProviderTests.cs
│ ├── MicrosoftExtensionsConnectionStringProviderTests.cs
│ └── MicrosoftExtensionsSinkOptionsProviderTests.cs
│ └── System.Configuration
│ ├── ApplySystemConfigurationTests.cs
│ ├── StandardColumnConfigSpanIdTests.cs
│ ├── StandardColumnConfigTraceIdTests.cs
│ ├── SystemConfigurationColumnOptionsProviderTests.cs
│ └── SystemConfigurationSinkOptionsProviderTests.cs
├── Extensions
└── StringExtensionsTests.cs
├── GlobalSuppressions.cs
├── Misc
├── AdditionalPropertiesTests.cs
├── CustomStandardColumnNamesTests.cs
├── IndexingFeaturesTests.cs
├── LevelAsEnumTests.cs
├── MiscFeaturesTests.cs
├── OpenTelemetryColumnsTests.cs
├── PropertiesColumnFilteringTests.cs
├── SqlBulkCopyTests.cs
├── SqlTypesTests.cs
├── StructuredSubType.cs
├── StructuredType.cs
├── TimeStampTests.cs
├── TransactionTests.cs
└── TriggersOnLogTableTests.cs
├── Serilog.Sinks.MSSqlServer.Tests.csproj
├── Sinks
└── MSSqlServer
│ ├── ColumnOptions
│ ├── ColumnOptionsTests.cs
│ ├── SpanIdColumnOptionsTests.cs
│ ├── TimeStampColumnOptionsTests.cs
│ └── TraceIdColumnOptionsTests.cs
│ ├── Dependencies
│ └── SinkDependenciesFactoryTests.cs
│ ├── MSSqlServerAuditSinkTests.cs
│ ├── MSSqlServerSinkOptionsTests.cs
│ ├── MSSqlServerSinkTests.cs
│ ├── Options
│ └── SinkOptionsTests.cs
│ ├── Output
│ ├── AdditionalColumnDataGeneratorTests.cs
│ ├── ColumnHierarchicalPropertyValueResolverTests.cs
│ ├── ColumnSimplePropertyValueResolverTests.cs
│ ├── JsonLogEventFormatterTests.cs
│ ├── LogEventDataGeneratorTests.cs
│ ├── StandardColumnDataGeneratorTests.cs
│ └── XmlPropertyFormatterTests.cs
│ ├── Platform
│ ├── DataTableCreatorTests.cs
│ ├── SqlBulkBatchWriterTests.cs
│ ├── SqlClient
│ │ ├── SqlBulkCopyWrapperTests.cs
│ │ ├── SqlCommandWrapperTests.cs
│ │ ├── SqlConnectionStringBuilderWrapperTests.cs
│ │ └── SqlConnectionWrapperTests.cs
│ ├── SqlCommandExecutorTests.cs
│ ├── SqlConnectionFactoryTests.cs
│ ├── SqlCreateDatabaseWriterTests.cs
│ ├── SqlCreateTableWriterTests.cs
│ ├── SqlInsertStatementWriterTests.cs
│ └── TestableSqlCommandExecutor.cs
│ └── SqlColumnTests.cs
└── TestUtils
├── DapperQueryTemplates.cs
├── DatabaseFixture.cs
├── DatabaseTestsBase.cs
├── Filename.cs
├── PatientSecureFixture.cs
├── TestCategory.cs
└── TestLogEventHelper.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 |
3 | * text=auto
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Bug Report / Support Request Template
2 | --------------------------------------
3 | If you are opening a feature request, you can ignore this template. Bug reports and requests for assistance usually require the same basic information described below. This will help us more quickly reproduce and investigate the problem you're reporting. (If you are using Serilog.Sinks.MSSqlServerCore, that package is deprecated, please switch to Serilog.Sinks.MSSqlServer before reporting an issue.)
4 |
5 | >> Please clearly describe what the SQL Sink is doing incorrectly:
6 |
7 | >> Please clearly describe the expected behavior:
8 |
9 | >> List the names and versions of all Serilog packages used in the project:
10 |
11 | - Serilog:
12 | - Serilog.Sinks.MSSqlServer:
13 | - (configuration, etc.)
14 |
15 | >> Target framework and operating system:
16 |
17 | [ ] .NET 9
18 | [ ] .NET 8
19 | [ ] .NET Framework 4.8
20 | [ ] .NET Framework 4.7
21 | [ ] .NET Framework 4.6
22 | OS:
23 |
24 | >> Provide a *simple* reproduction of your Serilog configuration code:
25 |
26 | >> Provide a *simple* reproduction of your Serilog configuration file, if any:
27 |
28 | >> Provide a *simple* reproduction of your application code:
29 |
--------------------------------------------------------------------------------
/.github/workflows/perftests.yml:
--------------------------------------------------------------------------------
1 | name: Performance Tests
2 |
3 | on:
4 | # Allows you to run this workflow manually from the Actions tab
5 | workflow_dispatch:
6 |
7 | jobs:
8 | build-and-perftest:
9 | runs-on: windows-latest # Build on Windows to ensure .NET Framework targets
10 | steps:
11 | - uses: actions/checkout@v4
12 |
13 | - name: Run build
14 | run: ./Build.ps1 -SkipTests -SkipSamples
15 | shell: pwsh
16 |
17 | - name: Run performance tests
18 | run: ./RunPerfTests.ps1 -Filter ${{ secrets.PERF_TESTS_FILTER }}
19 | shell: pwsh
20 |
21 | - name: Upload perf test results artifact
22 | uses: actions/upload-artifact@v4
23 | with:
24 | name: perftestresults
25 | path: artifacts\perftests
26 |
--------------------------------------------------------------------------------
/.github/workflows/pr-analysis-codeql.yml:
--------------------------------------------------------------------------------
1 | name: PR Analysis Code QL
2 |
3 | on:
4 | pull_request:
5 | branches: [ dev, main ]
6 |
7 | # Allows you to run this workflow manually from the Actions tab
8 | workflow_dispatch:
9 |
10 | jobs:
11 | build-and-codeql:
12 | runs-on: windows-latest
13 | permissions:
14 | actions: read
15 | contents: read
16 | security-events: write
17 |
18 | steps:
19 | - name: Checkout repository
20 | uses: actions/checkout@v4
21 |
22 | - name: Initialize CodeQL
23 | uses: github/codeql-action/init@v3
24 | with:
25 | languages: 'csharp'
26 |
27 | - name: Run build
28 | run: ./Build.ps1 -SkipTests
29 | shell: pwsh
30 |
31 | - name: Perform CodeQL Analysis
32 | uses: github/codeql-action/analyze@v3
33 | with:
34 | category: "/language:csharp"
35 |
--------------------------------------------------------------------------------
/.github/workflows/pr-analysis-devskim.yml:
--------------------------------------------------------------------------------
1 | name: PR Analysis DevSkim
2 |
3 | on:
4 | pull_request:
5 | branches: [ dev, main ]
6 |
7 | # Allows you to run this workflow manually from the Actions tab
8 | workflow_dispatch:
9 |
10 | jobs:
11 | devskim:
12 | runs-on: ubuntu-latest
13 | permissions:
14 | actions: read
15 | contents: read
16 | security-events: write
17 |
18 | steps:
19 | - name: Checkout code
20 | uses: actions/checkout@v4
21 |
22 | - name: Run DevSkim scanner
23 | uses: microsoft/DevSkim-Action@v1
24 |
25 | - name: Upload DevSkim scan results to GitHub Security tab
26 | uses: github/codeql-action/upload-sarif@v3
27 | with:
28 | sarif_file: devskim-results.sarif
29 |
--------------------------------------------------------------------------------
/.github/workflows/pr-validation.yml:
--------------------------------------------------------------------------------
1 | name: PR Validation
2 |
3 | on:
4 | pull_request:
5 | branches: [ dev, main ]
6 |
7 | # Run every biweekly to discover failures due to environment changes
8 | schedule:
9 | - cron: '0 0 1,15 * *'
10 |
11 | # Allows you to run this workflow manually from the Actions tab
12 | workflow_dispatch:
13 |
14 | jobs:
15 | build-and-test:
16 | runs-on: windows-latest # SQL Server LocalDB used in tests requires Windows
17 | steps:
18 | - uses: actions/checkout@v4
19 |
20 | - name: Run build and tests
21 | run: ./Build.ps1
22 | shell: pwsh
23 |
24 | - name: Upload binaries artifact for InferSharp job
25 | uses: actions/upload-artifact@v4
26 | with:
27 | name: bin
28 | path: src\Serilog.Sinks.MSSqlServer\bin\Release\net8.0
29 |
30 | - name: Upload testresults artifact with code coverage file
31 | uses: actions/upload-artifact@v4
32 | with:
33 | name: testresults
34 | path: test\Serilog.Sinks.MSSqlServer.Tests\TestResults
35 |
36 | infersharp:
37 | runs-on: ubuntu-latest # Container action used by Infer# requires Linux
38 | needs: build-and-test
39 | permissions:
40 | actions: read
41 | contents: read
42 | security-events: write
43 | steps:
44 | - name: Download binaries artifact
45 | uses: actions/download-artifact@v4
46 | with:
47 | name: bin
48 | path: bin
49 |
50 | - name: Run Infer#
51 | uses: microsoft/infersharpaction@v1.5
52 | with:
53 | binary-path: bin
54 |
55 | - name: Upload SARIF output to GitHub Security Center
56 | uses: github/codeql-action/upload-sarif@v3
57 | with:
58 | sarif_file: infer-out/report.sarif
59 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches: [ dev, main ]
6 |
7 | # Allows you to run this workflow manually from the Actions tab
8 | workflow_dispatch:
9 |
10 | jobs:
11 | build-perftest-and-release:
12 | runs-on: windows-latest # Build on Windows to ensure .NET Framework targets
13 | steps:
14 | - uses: actions/checkout@v4
15 | with:
16 | fetch-depth: 0
17 |
18 | - name: Read version from csproj
19 | if: github.ref == 'refs/heads/main'
20 | run: |
21 | # Extract the version from the .csproj file using PowerShell XML parsing
22 | [xml]$csproj = Get-Content 'src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj'
23 | $version = $csproj.Project.PropertyGroup.VersionPrefix
24 | echo "VERSION=$version" >> $env:GITHUB_ENV
25 |
26 | # Check if the tag already exists in git
27 | $tagExists = git tag -l "v$version"
28 | if ($tagExists) {
29 | Write-Host "Tag v$version already exists"
30 | exit 1
31 | }
32 | shell: pwsh
33 |
34 | - name: Run build
35 | run: ./Build.ps1 -SkipTests -SkipSamples
36 | shell: pwsh
37 |
38 | - name: Run performance tests
39 | run: ./RunPerfTests.ps1 -Filter "*QuickBenchmarks*"
40 | shell: pwsh
41 |
42 | - name: Upload perf test results artifact
43 | uses: actions/upload-artifact@v4
44 | with:
45 | name: perftestresults
46 | path: artifacts\perftests
47 |
48 | - name: Get last commit message
49 | id: last_commit
50 | if: success() && github.ref == 'refs/heads/main'
51 | run: |
52 | git log -1 --pretty=%B > last_commit_message.txt
53 | shell: pwsh
54 |
55 | - name: Create Release
56 | if: github.ref == 'refs/heads/main' && success()
57 | env:
58 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
59 | run: |
60 | $baseFileName = "Serilog.Sinks.MSSqlServer.${{ env.VERSION }}"
61 |
62 | $nupkgFile = Get-ChildItem -Path "artifacts/$baseFileName*.nupkg" | Select-Object -First 1
63 | $snupkgFile = Get-ChildItem -Path "artifacts/$baseFileName*.snupkg" | Select-Object -First 1
64 |
65 | if (-not $nupkgFile) { Write-Error "nupkg file not found" ; exit 1 }
66 | if (-not $snupkgFile) { Write-Error "snupkg file not found" ; exit 1 }
67 |
68 | $nupkgFilePath = $nupkgFile.FullName -replace '\\', '/'
69 | $snupkgFilePath = $snupkgFile.FullName -replace '\\', '/'
70 |
71 | Write-Host "Uploading files: $nupkgFilePath, $snupkgFilePath"
72 |
73 | gh release create v${{ env.VERSION }} `
74 | --title "v${{ env.VERSION }}" `
75 | --notes "$(Get-Content last_commit_message.txt)" `
76 | $nupkgFilePath $snupkgFilePath
77 | shell: pwsh
78 |
79 | - name: Publish to nuget.org
80 | run: |
81 | nuget push artifacts\*.nupkg -Source https://api.nuget.org/v3/index.json -ApiKey ${{ secrets.NUGET_API_KEY }}
82 | shell: pwsh
83 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | Please refer to the [Serilog Code of Conduct](https://github.com/serilog/serilog/blob/dev/CODE_OF_CONDUCT.md) which covers all repositories within the Serilog Organization.
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Please refer to the [Serilog Contributing Guidelines](https://github.com/serilog/serilog/blob/dev/CONTRIBUTING.md) which also apply to the MSSqlServer Sink.
4 |
--------------------------------------------------------------------------------
/DEVELOPMENT.md:
--------------------------------------------------------------------------------
1 | # Creating Releases
2 |
3 | ## Creating a Pre-release (-dev suffix)
4 |
5 | Whenever the `dev` branch is updated (after merging a pull request), the `Release` action is triggered. This action builds a nuget package with a prerelease identifier of the format `-dev-nnnnn` appended to the version number. This package is automatically published on nuget.org.
6 |
7 | ## Creating a latest Release
8 |
9 | ### Normal Update (no major version change) {#normal-update}
10 |
11 | 1. On the `dev` branch, update CHANGES.md and `VersionPrefix` in Serilog.Sinks.MSSqlServer.csproj.
12 |
13 | 1. Create a PR to merge the `dev` branch into `main`. The `Release` action will be triggered. This action builds a nuget package and publishes it on nuget.org. Additionally a release is created in the GitHub repo. The release summary will be taken from the description of the PR, so best thing is to put something similar to the version summary in CHANGES.md in there.
14 |
15 | 1. After the release is done, increase the patch version number in `VersionPrefix` in Serilog.Sinks.MSSqlServer.csproj on the `dev` branch. This ensures that the next dev release will have a higher version number than the latest release.
16 |
17 | ### Major Release (major version change)
18 |
19 | 1. On the `dev` branch, update CHANGES.md and increase the major version in `VersionPrefix` in Serilog.Sinks.MSSqlServer.csproj. Also set `EnablePackageValidation` to false because on an intial release of a new major version you don't have a baseline version yet on nuget.org to compare with.
20 |
21 | 1. Create a PR to merge the `dev` branch into `main`. The `Release` action will be triggered. This works the same as described above under [Normal Update]({#normal-update).
22 |
23 | 1. After the release is done make some changes in Serilog.Sinks.MSSqlServer.csproj on the `dev` branch. Set `EnablePackageValidation` back to true and `PackageValidationBaselineVersion` to the version of the new major release you just created (e.g. 7.0.0). Then also increase the patch version number in `VersionPrefix` (e.g. 7.0.1).
24 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | all
4 | high
5 | true
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Directory.Packages.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/RunPerfTests.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter(Mandatory = $false)]
4 | [string]
5 | $Filter = "*"
6 | )
7 |
8 | echo "perf: Performance tests started with Filter = $Filter"
9 |
10 | try
11 | {
12 | Push-Location $PSScriptRoot
13 |
14 | $artifactsPath = "$PSScriptRoot\artifacts\perftests"
15 |
16 | if (Test-Path "$artifactsPath")
17 | {
18 | echo "perf: Cleaning $artifactsPath"
19 | Remove-Item "$artifactsPath" -Force -Recurse
20 | }
21 |
22 | New-Item -Path "$artifactsPath" -ItemType Directory
23 |
24 | $perfTestProjectPath = "$PSScriptRoot/test/Serilog.Sinks.MSSqlServer.PerformanceTests"
25 | try
26 | {
27 | Push-Location "$perfTestProjectPath"
28 |
29 | echo "perf: Running performance test project in $perfTestProjectPath"
30 | & dotnet run -c Release -- -f $Filter
31 |
32 | cp ".\BenchmarkDotNet.Artifacts\results\*.*" "$artifactsPath\"
33 | }
34 | finally
35 | {
36 | Pop-Location
37 | }
38 |
39 | }
40 | finally
41 | {
42 | Pop-Location
43 | }
44 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | We currently do not maintain older major versions of the sink and backport security fixes. Fixes are usually created as a new release based on the latest existing release.
6 |
7 | ## Reporting a Vulnerability
8 |
9 | If you find a security related problem in the library, please create an issue in the GitHub repository and give us as much details and context as you can.
10 |
--------------------------------------------------------------------------------
/assets/Serilog.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serilog-mssql/serilog-sinks-mssqlserver/144ad397ed50e6b26d9c02cdd6f052253301e9fc/assets/Serilog.snk
--------------------------------------------------------------------------------
/sample/AppConfigDemo/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/sample/AppConfigDemo/AppConfigDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net462
4 | Exe
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/AppConfigDemo/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using Serilog;
4 | using Serilog.Events;
5 | using Serilog.Sinks.MSSqlServer;
6 |
7 | namespace AppConfigDemo
8 | {
9 | public static class Program
10 | {
11 | private const string _connectionString = "Server=localhost;Database=LogTest;Integrated Security=SSPI;Encrypt=False;";
12 | private const string _schemaName = "dbo";
13 | private const string _tableName = "LogEvents";
14 |
15 | public static void Main()
16 | {
17 | // New MSSqlServerSinkOptions based interface
18 | Log.Logger = new LoggerConfiguration().WriteTo
19 | .MSSqlServer(
20 | connectionString: _connectionString,
21 | sinkOptions: new MSSqlServerSinkOptions
22 | {
23 | TableName = _tableName,
24 | SchemaName = _schemaName,
25 | AutoCreateSqlTable = true
26 | },
27 | restrictedToMinimumLevel: LogEventLevel.Debug,
28 | formatProvider: null,
29 | columnOptions: null,
30 | logEventFormatter: null)
31 | .CreateLogger();
32 |
33 | Log.Debug("Getting started");
34 |
35 | Log.Information("Hello {Name} from thread {ThreadId}", Environment.GetEnvironmentVariable("USERNAME"),
36 | Thread.CurrentThread.ManagedThreadId);
37 |
38 | Log.Warning("No coins remain at position {@Position}", new { Lat = 25, Long = 134 });
39 |
40 | Log.CloseAndFlush();
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/sample/CombinedConfigDemo/CombinedConfigDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 |
7 |
8 |
9 |
10 | PreserveNewest
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/sample/CombinedConfigDemo/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using Microsoft.Extensions.Configuration;
4 | using Serilog;
5 | using Serilog.Sinks.MSSqlServer;
6 |
7 | namespace CombinedConfigDemo
8 | {
9 | // This sample app reads connection string and column options from appsettings.json
10 | // while schema name, table name and autoCreateSqlTable are supplied programmatically
11 | // as parameters to the MSSqlServer() method.
12 | public static class Program
13 | {
14 | private const string _connectionStringName = "LogDatabase";
15 | private const string _schemaName = "dbo";
16 | private const string _tableName = "LogEvents";
17 |
18 | public static void Main()
19 | {
20 | var configuration = new ConfigurationBuilder()
21 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
22 | .Build();
23 | var columnOptionsSection = configuration.GetSection("Serilog:ColumnOptions");
24 | var sinkOptionsSection = configuration.GetSection("Serilog:SinkOptions");
25 |
26 | // Legacy interface - do not use this anymore
27 | //Log.Logger = new LoggerConfiguration()
28 | // .WriteTo.MSSqlServer(
29 | // connectionString: _connectionStringName,
30 | // tableName: _tableName,
31 | // appConfiguration: configuration,
32 | // autoCreateSqlTable: true,
33 | // columnOptionsSection: columnOptionsSection,
34 | // schemaName: _schemaName)
35 | // .CreateLogger();
36 |
37 | // New SinkOptions based interface
38 | Log.Logger = new LoggerConfiguration()
39 | .WriteTo.MSSqlServer(
40 | connectionString: _connectionStringName,
41 | sinkOptions: new MSSqlServerSinkOptions
42 | {
43 | TableName = _tableName,
44 | SchemaName = _schemaName,
45 | AutoCreateSqlTable = true
46 | },
47 | sinkOptionsSection: sinkOptionsSection,
48 | appConfiguration: configuration,
49 | columnOptionsSection: columnOptionsSection)
50 | .CreateLogger();
51 |
52 | Log.Information("Hello {Name} from thread {ThreadId}", Environment.GetEnvironmentVariable("USERNAME"), Environment.CurrentManagedThreadId);
53 |
54 | Log.Warning("No coins remain at position {@Position}", new { Lat = 25, Long = 134 });
55 |
56 | Log.CloseAndFlush();
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/sample/CombinedConfigDemo/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "ConnectionStrings": {
10 | "LogDatabase": "Server=localhost;Database=LogTest;Integrated Security=SSPI;Encrypt=False;"
11 | },
12 | "Serilog": {
13 | "SinkOptions": {
14 | "batchPostingLimit": 5,
15 | "batchPeriod": "00:00:15",
16 | "eagerlyEmitFirstEvent": true
17 | },
18 | "ColumnOptions": {
19 | "addStandardColumns": [ "LogEvent" ],
20 | "removeStandardColumns": [ "MessageTemplate", "Properties" ],
21 | "timeStamp": {
22 | "columnName": "Timestamp",
23 | "convertToUtc": false
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/sample/CustomLogEventFormatterDemo/CustomLogEventFormatterDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/sample/CustomLogEventFormatterDemo/FlatLogEventFormatter.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Linq;
3 | using Serilog.Events;
4 | using Serilog.Formatting;
5 |
6 | namespace CustomLogEventFormatterDemo
7 | {
8 | public class FlatLogEventFormatter : ITextFormatter
9 | {
10 | public void Format(LogEvent logEvent, TextWriter output)
11 | {
12 | logEvent.Properties.ToList().ForEach(e =>
13 | {
14 | output.Write($"{e.Key}={e.Value} ");
15 | });
16 | output.WriteLine();
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/sample/NetStandardDemo/NetStandardDemoApp/NetStandardDemoApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/sample/NetStandardDemo/NetStandardDemoApp/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using NetStandardDemoLib;
4 | using Serilog;
5 |
6 | namespace NetStandardDemoApp
7 | {
8 | public static class Program
9 | {
10 | public static void Main()
11 | {
12 | Log.Logger = Initializer.CreateLoggerConfiguration().CreateLogger();
13 |
14 | Log.Debug("Getting started");
15 |
16 | Log.Information("Hello {Name} from thread {ThreadId}", Environment.GetEnvironmentVariable("USERNAME"), Environment.CurrentManagedThreadId);
17 |
18 | Log.Warning("No coins remain at position {@Position}", new { Lat = 25, Long = 134 });
19 |
20 | Log.CloseAndFlush();
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/sample/NetStandardDemo/NetStandardDemoLib/Initializer.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 | using System.Data;
3 | using Serilog;
4 | using Serilog.Events;
5 | using Serilog.Sinks.MSSqlServer;
6 |
7 | namespace NetStandardDemoLib
8 | {
9 | public static class Initializer
10 | {
11 | private const string _connectionString = "Server=localhost;Database=LogTest;Integrated Security=SSPI;Encrypt=False;";
12 | private const string _tableName = "LogEvents";
13 |
14 | public static LoggerConfiguration CreateLoggerConfiguration()
15 | {
16 | return new LoggerConfiguration()
17 | .Enrich.FromLogContext()
18 | .WriteTo.MSSqlServer(
19 | _connectionString,
20 | new MSSqlServerSinkOptions
21 | {
22 | TableName = _tableName,
23 | AutoCreateSqlTable = true
24 | },
25 | sinkOptionsSection: null,
26 | appConfiguration: null,
27 | restrictedToMinimumLevel: LevelAlias.Minimum,
28 | formatProvider: null,
29 | columnOptions: BuildColumnOptions(),
30 | columnOptionsSection: null,
31 | logEventFormatter: null);
32 |
33 | }
34 |
35 | private static ColumnOptions BuildColumnOptions()
36 | {
37 | var columnOptions = new ColumnOptions
38 | {
39 | TimeStamp =
40 | {
41 | ColumnName = "TimeStampUTC",
42 | ConvertToUtc = true,
43 | },
44 |
45 | AdditionalColumns = new Collection
46 | {
47 | new SqlColumn { DataType = SqlDbType.NVarChar, ColumnName = "MachineName" },
48 | new SqlColumn { DataType = SqlDbType.NVarChar, ColumnName = "ProcessName" },
49 | new SqlColumn { DataType = SqlDbType.NVarChar, ColumnName = "ThreadId" },
50 | new SqlColumn { DataType = SqlDbType.NVarChar, ColumnName = "CallerName" },
51 | new SqlColumn { DataType = SqlDbType.NVarChar, ColumnName = "SourceFile" },
52 | new SqlColumn { DataType = SqlDbType.NVarChar, ColumnName = "LineNumber" }
53 | }
54 | };
55 |
56 | columnOptions.Store.Remove(StandardColumn.Properties);
57 |
58 | return columnOptions;
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/sample/NetStandardDemo/NetStandardDemoLib/NetStandardDemoLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sample/WorkerServiceDemo/CustomLogEventFormatter.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Linq;
3 | using Serilog.Events;
4 | using Serilog.Formatting;
5 |
6 | namespace WorkerServiceDemo
7 | {
8 | public class CustomLogEventFormatter : ITextFormatter
9 | {
10 | public static CustomLogEventFormatter Formatter { get; } = new CustomLogEventFormatter();
11 |
12 | public void Format(LogEvent logEvent, TextWriter output)
13 | {
14 | logEvent.Properties.ToList()
15 | .ForEach(e => output.Write($"{e.Key}={e.Value} "));
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/sample/WorkerServiceDemo/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.Extensions.Hosting;
4 | using Serilog;
5 |
6 | namespace WorkerServiceDemo
7 | {
8 | public static class Program
9 | {
10 | public static void Main(string[] args)
11 | {
12 | //Serilog.Debugging.SelfLog.Enable(Console.Error);
13 | CreateHostBuilder(args).Build().Run();
14 | }
15 |
16 | public static IHostBuilder CreateHostBuilder(string[] args) =>
17 | Host.CreateDefaultBuilder(args)
18 | .ConfigureServices((hostContext, services) =>
19 | {
20 | services.AddHostedService();
21 | })
22 | .UseSerilog((hostingContext, loggerConfiguration) =>
23 | {
24 | loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration);
25 | });
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/sample/WorkerServiceDemo/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "WorkerServiceDemo": {
4 | "commandName": "Project",
5 | "environmentVariables": {
6 | "DOTNET_ENVIRONMENT": "Development"
7 | }
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/sample/WorkerServiceDemo/Structured.cs:
--------------------------------------------------------------------------------
1 | namespace WorkerServiceDemo
2 | {
3 | internal class Structured
4 | {
5 | public string Name { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/sample/WorkerServiceDemo/Worker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Microsoft.Extensions.Hosting;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace WorkerServiceDemo
8 | {
9 | public class Worker : BackgroundService
10 | {
11 | private readonly ILogger _logger;
12 |
13 | public Worker(ILogger logger)
14 | {
15 | _logger = logger;
16 | }
17 |
18 | protected override async Task ExecuteAsync(CancellationToken stoppingToken)
19 | {
20 | _logger.LogInformation("Worker started");
21 |
22 | // Logging child property Name of structured object structured
23 | // to a separate column according to configuration in AdditionalColumns in appsettings.json
24 | var structured = new Structured
25 | {
26 | Name = "Structured Subproperty Value"
27 | };
28 | _logger.LogInformation("{@Structured} {@Scalar}", structured, "Scalar Value");
29 |
30 |
31 | // Logging a property with dots in its name to AdditionalColumn3
32 | // but treat it as unstructured according to configuration in AdditionalColumns in appsettings.json
33 | _logger.LogInformation("Non-structured property with dot-name to AdditionalColumn3 {@NonstructuredProperty.WithNameContainingDots.Name}",
34 | new Random().Next().ToString());
35 |
36 | while (!stoppingToken.IsCancellationRequested)
37 | {
38 | _logger.LogInformation("Worker running at: {time}. CustomProperty1: {CustomProperty1}",
39 | DateTimeOffset.Now, "Value");
40 | await Task.Delay(1000, stoppingToken);
41 | }
42 |
43 | _logger.LogInformation("Worker stopping ...");
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/sample/WorkerServiceDemo/WorkerServiceDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | dotnet-WorkerServiceDemo-A4DFF8A6-AC69-443B-A3B8-34E284CD1C78
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/sample/WorkerServiceDemo/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/sample/WorkerServiceDemo/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "Serilog": {
10 | "Using": [ "Serilog.Sinks.MSSqlServer" ],
11 | "MinimumLevel": "Debug",
12 | "WriteTo": [
13 | {
14 | "Name": "MSSqlServer",
15 | "Args": {
16 | "connectionString": "Server=localhost;Database=LogTest;Integrated Security=SSPI;Encrypt=False;",
17 | "sinkOptionsSection": {
18 | "tableName": "LogEvents",
19 | "autoCreateSqlDatabase": true,
20 | "autoCreateSqlTable": true
21 | },
22 | "restrictedToMinimumLevel": "Information",
23 | "columnOptionsSection": {
24 | "addStandardColumns": [ "LogEvent", "TraceId", "SpanId" ],
25 | "removeStandardColumns": [ "MessageTemplate", "Properties" ],
26 | "timeStamp": {
27 | "columnName": "Timestamp",
28 | "convertToUtc": false
29 | },
30 | "customColumns": [
31 | {
32 | "columnName": "AdditionalColumn1",
33 | "propertyName": "CustomProperty1",
34 | "dataType": "12"
35 | },
36 | {
37 | "columnName": "AdditionalColumn2",
38 | "propertyName": "Structured.Name",
39 | "dataType": "12"
40 | },
41 | {
42 | "columnName": "AdditionalColumn3",
43 | "propertyName": "NonstructuredProperty.WithNameContainingDots.Name",
44 | "resolveHierarchicalPropertyName": false,
45 | "dataType": "12"
46 | }
47 | ]
48 | },
49 | "logEventFormatter": "WorkerServiceDemo.CustomLogEventFormatter::Formatter, WorkerServiceDemo"
50 | }
51 | }
52 | ],
53 | "AuditTo": [
54 | {
55 | "Name": "MSSqlServer",
56 | "Args": {
57 | "connectionString": "Server=localhost;Database=LogTest;Integrated Security=SSPI;Encrypt=False;",
58 | "restrictedToMinimumLevel": "Information",
59 | "logEventFormatter": "WorkerServiceDemo.CustomLogEventFormatter::Formatter, WorkerServiceDemo",
60 | "sinkOptionsSection": {
61 | "tableName": "LogEventsAudit",
62 | "autoCreateSqlDatabase": true,
63 | "autoCreateSqlTable": true
64 | },
65 | "columnOptionsSection": {
66 | "addStandardColumns": [ "LogEvent", "TraceId", "SpanId" ],
67 | "removeStandardColumns": [ "MessageTemplate", "Properties" ],
68 | "timeStamp": {
69 | "columnName": "Timestamp",
70 | "convertToUtc": false
71 | },
72 | "customColumns": [
73 | {
74 | "columnName": "AdditionalColumn1",
75 | "propertyName": "CustomProperty1",
76 | "dataType": "12"
77 | },
78 | {
79 | "columnName": "AdditionalColumn2",
80 | "propertyName": "Structured.Name",
81 | "dataType": "12"
82 | },
83 | {
84 | "columnName": "AdditionalColumn3",
85 | "propertyName": "NonstructuredProperty.WithNameContainingDots.Name",
86 | "resolveHierarchicalPropertyName": false,
87 | "dataType": "12"
88 | }
89 | ]
90 | }
91 | }
92 | }
93 | ]
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Factories/IMSSqlServerAuditSinkFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog.Core;
3 | using Serilog.Formatting;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Configuration.Factories
6 | {
7 | internal interface IMSSqlServerAuditSinkFactory
8 | {
9 | ILogEventSink Create(
10 | string connectionString,
11 | MSSqlServerSinkOptions sinkOptions,
12 | IFormatProvider formatProvider,
13 | ColumnOptions columnOptions,
14 | ITextFormatter logEventFormatter);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Factories/IMSSqlServerSinkFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog.Formatting;
3 | using Serilog.Core;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Configuration.Factories
6 | {
7 | internal interface IMSSqlServerSinkFactory
8 | {
9 | IBatchedLogEventSink Create(
10 | string connectionString,
11 | MSSqlServerSinkOptions sinkOptions,
12 | IFormatProvider formatProvider,
13 | ColumnOptions columnOptions,
14 | ITextFormatter logEventFormatter);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Factories/IPeriodicBatchingSinkFactory.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Core;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Configuration.Factories
4 | {
5 | internal interface IPeriodicBatchingSinkFactory
6 | {
7 | ILogEventSink Create(IBatchedLogEventSink sink, MSSqlServerSinkOptions sinkOptions);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Factories/MSSqlServerAuditSinkFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog.Core;
3 | using Serilog.Formatting;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Configuration.Factories
6 | {
7 | internal class MSSqlServerAuditSinkFactory : IMSSqlServerAuditSinkFactory
8 | {
9 | public ILogEventSink Create(
10 | string connectionString,
11 | MSSqlServerSinkOptions sinkOptions,
12 | IFormatProvider formatProvider,
13 | ColumnOptions columnOptions,
14 | ITextFormatter logEventFormatter) =>
15 | new MSSqlServerAuditSink(
16 | connectionString,
17 | sinkOptions,
18 | formatProvider,
19 | columnOptions,
20 | logEventFormatter);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Factories/MSSqlServerSinkFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog.Formatting;
3 | using Serilog.Core;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Configuration.Factories
6 | {
7 | internal class MSSqlServerSinkFactory : IMSSqlServerSinkFactory
8 | {
9 | public IBatchedLogEventSink Create(
10 | string connectionString,
11 | MSSqlServerSinkOptions sinkOptions,
12 | IFormatProvider formatProvider,
13 | ColumnOptions columnOptions,
14 | ITextFormatter logEventFormatter) =>
15 | new MSSqlServerSink(
16 | connectionString,
17 | sinkOptions,
18 | formatProvider,
19 | columnOptions,
20 | logEventFormatter);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Factories/PeriodicBatchingSinkFactory.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Configuration;
2 | using Serilog.Core;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Configuration.Factories
5 | {
6 | internal class PeriodicBatchingSinkFactory : IPeriodicBatchingSinkFactory
7 | {
8 | public ILogEventSink Create(IBatchedLogEventSink sink, MSSqlServerSinkOptions sinkOptions)
9 | {
10 | var periodicBatchingSinkOptions = new BatchingOptions
11 | {
12 | BatchSizeLimit = sinkOptions.BatchPostingLimit,
13 | BufferingTimeLimit = sinkOptions.BatchPeriod,
14 | EagerlyEmitFirstEvent = sinkOptions.EagerlyEmitFirstEvent
15 | };
16 | return LoggerSinkConfiguration.CreateSink(lc => lc.Sink(sink, periodicBatchingSinkOptions));
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/Microsoft.Extensions.Configuration/ApplyMicrosoftExtensionsConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using Serilog.Sinks.MSSqlServer.Configuration;
3 |
4 | namespace Serilog.Sinks.MSSqlServer
5 | {
6 | internal class ApplyMicrosoftExtensionsConfiguration : IApplyMicrosoftExtensionsConfiguration
7 | {
8 | private readonly IMicrosoftExtensionsConnectionStringProvider _connectionStringProvider;
9 | private readonly IMicrosoftExtensionsColumnOptionsProvider _columnOptionsProvider;
10 | private readonly IMicrosoftExtensionsSinkOptionsProvider _sinkOptionsProvider;
11 |
12 | public ApplyMicrosoftExtensionsConfiguration() : this(
13 | new MicrosoftExtensionsConnectionStringProvider(),
14 | new MicrosoftExtensionsColumnOptionsProvider(),
15 | new MicrosoftExtensionsSinkOptionsProvider())
16 | {
17 | }
18 |
19 | // Constructor with injectable dependencies for tests
20 | internal ApplyMicrosoftExtensionsConfiguration(
21 | IMicrosoftExtensionsConnectionStringProvider connectionStringProvider,
22 | IMicrosoftExtensionsColumnOptionsProvider columnOptionsProvider,
23 | IMicrosoftExtensionsSinkOptionsProvider sinkOptionsProvider)
24 | {
25 | _connectionStringProvider = connectionStringProvider;
26 | _columnOptionsProvider = columnOptionsProvider;
27 | _sinkOptionsProvider = sinkOptionsProvider;
28 | }
29 |
30 | public string GetConnectionString(string nameOrConnectionString, IConfiguration appConfiguration) =>
31 | _connectionStringProvider.GetConnectionString(nameOrConnectionString, appConfiguration);
32 |
33 | public ColumnOptions ConfigureColumnOptions(ColumnOptions columnOptions, IConfigurationSection config) =>
34 | _columnOptionsProvider.ConfigureColumnOptions(columnOptions, config);
35 |
36 | public MSSqlServerSinkOptions ConfigureSinkOptions(MSSqlServerSinkOptions sinkOptions, IConfigurationSection config) =>
37 | _sinkOptionsProvider.ConfigureSinkOptions(sinkOptions, config);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/Microsoft.Extensions.Configuration/IApplyMicrosoftExtensionsConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 |
3 | namespace Serilog.Sinks.MSSqlServer
4 | {
5 | internal interface IApplyMicrosoftExtensionsConfiguration
6 | {
7 | string GetConnectionString(string nameOrConnectionString, IConfiguration appConfiguration);
8 | ColumnOptions ConfigureColumnOptions(ColumnOptions columnOptions, IConfigurationSection config);
9 | MSSqlServerSinkOptions ConfigureSinkOptions(MSSqlServerSinkOptions sinkOptions, IConfigurationSection config);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/Microsoft.Extensions.Configuration/IMicrosoftExtensionsColumnOptionsProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Configuration
4 | {
5 | internal interface IMicrosoftExtensionsColumnOptionsProvider
6 | {
7 | ColumnOptions ConfigureColumnOptions(ColumnOptions columnOptions, IConfigurationSection config);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/Microsoft.Extensions.Configuration/IMicrosoftExtensionsConnectionStringProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Configuration
4 | {
5 | internal interface IMicrosoftExtensionsConnectionStringProvider
6 | {
7 | string GetConnectionString(string nameOrConnectionString, IConfiguration appConfiguration);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/Microsoft.Extensions.Configuration/IMicrosoftExtensionsSinkOptionsProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Configuration
4 | {
5 | internal interface IMicrosoftExtensionsSinkOptionsProvider
6 | {
7 | MSSqlServerSinkOptions ConfigureSinkOptions(MSSqlServerSinkOptions sinkOptions, IConfigurationSection config);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/Microsoft.Extensions.Configuration/MicrosoftExtensionsConnectionStringProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Extensions.Configuration;
3 | using Serilog.Debugging;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Configuration
6 | {
7 | internal class MicrosoftExtensionsConnectionStringProvider : IMicrosoftExtensionsConnectionStringProvider
8 | {
9 | public string GetConnectionString(string nameOrConnectionString, IConfiguration appConfiguration)
10 | {
11 | // If there is an `=`, we assume this is a raw connection string not a named value
12 | // If there are no `=`, attempt to pull the named value from config
13 | if (nameOrConnectionString.IndexOf("=", StringComparison.InvariantCultureIgnoreCase) > -1) return nameOrConnectionString;
14 | var cs = appConfiguration?.GetConnectionString(nameOrConnectionString);
15 | if (string.IsNullOrEmpty(cs))
16 | {
17 | SelfLog.WriteLine("MSSqlServer sink configured value {0} is not found in ConnectionStrings settings and does not appear to be a raw connection string.",
18 | nameOrConnectionString);
19 | }
20 | return cs;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/Microsoft.Extensions.Configuration/MicrosoftExtensionsSinkOptionsProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using Microsoft.Extensions.Configuration;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Configuration
6 | {
7 | internal class MicrosoftExtensionsSinkOptionsProvider : IMicrosoftExtensionsSinkOptionsProvider
8 | {
9 | public MSSqlServerSinkOptions ConfigureSinkOptions(MSSqlServerSinkOptions sinkOptions, IConfigurationSection config)
10 | {
11 | if (config == null)
12 | {
13 | return sinkOptions;
14 | }
15 |
16 | ReadTableOptions(config, sinkOptions);
17 | ReadBatchSettings(config, sinkOptions);
18 |
19 | return sinkOptions;
20 | }
21 |
22 | private static void ReadTableOptions(IConfigurationSection config, MSSqlServerSinkOptions sinkOptions)
23 | {
24 | SetProperty.IfNotNull(config["tableName"], val => sinkOptions.TableName = val);
25 | SetProperty.IfNotNull(config["schemaName"], val => sinkOptions.SchemaName = val);
26 | SetProperty.IfNotNull(config["autoCreateSqlDatabase"], val => sinkOptions.AutoCreateSqlDatabase = val);
27 | SetProperty.IfNotNull(config["autoCreateSqlTable"], val => sinkOptions.AutoCreateSqlTable = val);
28 | SetProperty.IfNotNull(config["enlistInTransaction"], val => sinkOptions.EnlistInTransaction = val);
29 | }
30 |
31 | private static void ReadBatchSettings(IConfigurationSection config, MSSqlServerSinkOptions sinkOptions)
32 | {
33 | SetProperty.IfNotNull(config["batchPostingLimit"], val => sinkOptions.BatchPostingLimit = val);
34 | SetProperty.IfNotNull(config["batchPeriod"], val => sinkOptions.BatchPeriod = TimeSpan.Parse(val, CultureInfo.InvariantCulture));
35 | SetProperty.IfNotNull(config["eagerlyEmitFirstEvent"], val => sinkOptions.EagerlyEmitFirstEvent = val);
36 | SetProperty.IfNotNull(config["useSqlBulkCopy"], val => sinkOptions.UseSqlBulkCopy = val);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/SetProperty.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 |
4 | namespace Serilog.Sinks.MSSqlServer
5 | {
6 | ///
7 | /// Helper for applying only those properties actually specified in external configuration.
8 | ///
9 | public static partial class SetProperty
10 | {
11 | // Usage:
12 | // SetProperty.IfValueNotNull(stringFromConfig, (boolOutputValue) => opts.BoolProperty = boolOutputValue);
13 |
14 | ///
15 | /// Simulates passing a property-setter to an "out" argument.
16 | ///
17 | public delegate void PropertySetter(T value);
18 |
19 | ///
20 | /// This will only set a value (execute the PropertySetter delegate) if the value is non-null.
21 | /// It also converts the provided value to the requested type. This allows configuration to only
22 | /// apply property changes when external configuration has actually provided a value.
23 | ///
24 | public static void IfNotNull(string value, PropertySetter setter)
25 | {
26 | if (value == null || setter == null) return;
27 | try
28 | {
29 | var setting = (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
30 | setter(setting);
31 | }
32 | // don't change the property if the conversion fails
33 | catch (InvalidCastException) { }
34 | catch (OverflowException) { }
35 | }
36 |
37 | ///
38 | /// This will only set a value (execute the PropertySetter delegate) if the value is non-null and
39 | /// isn't empty or whitespace. This override is used when {T} is a string value that can't be empty.
40 | /// It also converts the provided value to the requested type. This allows configuration to only
41 | /// apply property changes when external configuration has actually provided a value.
42 | ///
43 | public static void IfNotNullOrEmpty(string value, PropertySetter setter)
44 | {
45 | if (string.IsNullOrWhiteSpace(value)) return;
46 | IfNotNull(value, setter);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/ApplySystemConfiguration.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 | using Serilog.Configuration;
3 | using Serilog.Sinks.MSSqlServer.Configuration;
4 |
5 | namespace Serilog.Sinks.MSSqlServer
6 | {
7 | internal class ApplySystemConfiguration : IApplySystemConfiguration
8 | {
9 | private readonly ISystemConfigurationConnectionStringProvider _connectionStringProvider;
10 | private readonly ISystemConfigurationColumnOptionsProvider _columnOptionsProvider;
11 | private readonly ISystemConfigurationSinkOptionsProvider _sinkOptionsProvider;
12 |
13 | public ApplySystemConfiguration() : this(
14 | new SystemConfigurationConnectionStringProvider(),
15 | new SystemConfigurationColumnOptionsProvider(),
16 | new SystemConfigurationSinkOptionsProvider())
17 | {
18 | }
19 |
20 | // Constructor with injectable dependencies for tests
21 | internal ApplySystemConfiguration(
22 | ISystemConfigurationConnectionStringProvider connectionStringProvider,
23 | ISystemConfigurationColumnOptionsProvider columnOptionsProvider,
24 | ISystemConfigurationSinkOptionsProvider sinkOptionsProvider)
25 | {
26 | _connectionStringProvider = connectionStringProvider;
27 | _columnOptionsProvider = columnOptionsProvider;
28 | _sinkOptionsProvider = sinkOptionsProvider;
29 | }
30 |
31 | public MSSqlServerConfigurationSection GetSinkConfigurationSection(string configurationSectionName)
32 | {
33 | return ConfigurationManager.GetSection(configurationSectionName) as MSSqlServerConfigurationSection;
34 | }
35 |
36 | public string GetConnectionString(string nameOrConnectionString) =>
37 | _connectionStringProvider.GetConnectionString(nameOrConnectionString);
38 |
39 | public ColumnOptions ConfigureColumnOptions(MSSqlServerConfigurationSection config, ColumnOptions columnOptions) =>
40 | _columnOptionsProvider.ConfigureColumnOptions(config, columnOptions);
41 |
42 | public MSSqlServerSinkOptions ConfigureSinkOptions(MSSqlServerConfigurationSection config, MSSqlServerSinkOptions sinkOptions) =>
43 | _sinkOptionsProvider.ConfigureSinkOptions(config, sinkOptions);
44 |
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/ColumnCollection.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2015 Serilog Contributors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System.Configuration;
16 |
17 | // Disable XML comment warnings for internal config classes which are required to have public members
18 | #pragma warning disable 1591
19 |
20 | namespace Serilog.Sinks.MSSqlServer
21 | {
22 | public class ColumnCollection : ConfigurationElementCollection
23 | {
24 | protected override ConfigurationElement CreateNewElement()
25 | {
26 | return new ColumnConfig();
27 | }
28 |
29 | protected override object GetElementKey(ConfigurationElement element)
30 | {
31 | return ((ColumnConfig)element)?.ColumnName;
32 | }
33 |
34 | // For testing
35 | internal void Add(ColumnConfig config)
36 | {
37 | BaseAdd(config);
38 | }
39 | }
40 | }
41 |
42 | #pragma warning restore 1591
43 |
44 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/IApplySystemConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Configuration;
2 |
3 | namespace Serilog.Sinks.MSSqlServer
4 | {
5 | internal interface IApplySystemConfiguration
6 | {
7 | MSSqlServerConfigurationSection GetSinkConfigurationSection(string configurationSectionName);
8 | string GetConnectionString(string nameOrConnectionString);
9 | ColumnOptions ConfigureColumnOptions(MSSqlServerConfigurationSection config, ColumnOptions columnOptions);
10 | MSSqlServerSinkOptions ConfigureSinkOptions(MSSqlServerConfigurationSection config, MSSqlServerSinkOptions sinkOptions);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/ISystemConfigurationColumnOptionsProvider.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Configuration;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Configuration
4 | {
5 | internal interface ISystemConfigurationColumnOptionsProvider
6 | {
7 | ColumnOptions ConfigureColumnOptions(MSSqlServerConfigurationSection config, ColumnOptions columnOptions);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/ISystemConfigurationConnectionStringProvider.cs:
--------------------------------------------------------------------------------
1 | namespace Serilog.Sinks.MSSqlServer.Configuration
2 | {
3 | internal interface ISystemConfigurationConnectionStringProvider
4 | {
5 | string GetConnectionString(string nameOrConnectionString);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/ISystemConfigurationSinkOptionsProvider.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Configuration;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Configuration
4 | {
5 | internal interface ISystemConfigurationSinkOptionsProvider
6 | {
7 | MSSqlServerSinkOptions ConfigureSinkOptions(MSSqlServerConfigurationSection config, MSSqlServerSinkOptions sinkOptions);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SetPropertyValueOrigin.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | namespace Serilog.Sinks.MSSqlServer
4 | {
5 | public static partial class SetProperty
6 | {
7 | // A null-check isn't possible for XML config strings; they default to an empty string and setting DefaultValue
8 | // to null doesn't work because internally the ConfigurationProperty attribute changes it back to empty string.
9 |
10 | ///
11 | /// Test the underlying property collection's value-origin flag for a non-default string value. Empty strings allowed.
12 | ///
13 | public static void IfProvided(ConfigurationElement element, string propertyName, PropertySetter setter)
14 | {
15 | if (element == null)
16 | {
17 | return;
18 | }
19 |
20 | var property = element.ElementInformation.Properties[propertyName];
21 | if (property.ValueOrigin == PropertyValueOrigin.Default) return;
22 | IfNotNull((string)property.Value, setter);
23 | }
24 |
25 | ///
26 | /// Test the underlying property collection's value-origin flag for a non-default, non-null, non-empty string value.
27 | ///
28 | public static void IfProvidedNotEmpty(ConfigurationElement element, string propertyName, PropertySetter setter)
29 | {
30 | if (element == null)
31 | {
32 | return;
33 | }
34 |
35 | var property = element.ElementInformation.Properties[propertyName];
36 | if (property.ValueOrigin == PropertyValueOrigin.Default) return;
37 | IfNotNullOrEmpty((string)property.Value, setter);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/StandardColumnCollection.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | // Disable XML comment warnings for internal config classes which are required to have public members
4 | #pragma warning disable 1591
5 |
6 | namespace Serilog.Sinks.MSSqlServer
7 | {
8 | public class StandardColumnCollection : ConfigurationElementCollection
9 | {
10 | protected override ConfigurationElement CreateNewElement()
11 | {
12 | return new StandardColumnConfig();
13 | }
14 |
15 | protected override object GetElementKey(ConfigurationElement element)
16 | {
17 | return ((StandardColumnConfig)element)?.Name;
18 | }
19 | }
20 | }
21 |
22 | #pragma warning restore 1591
23 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/StandardColumnConfig.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | // Disable XML comment warnings for internal config classes which are required to have public members
4 | #pragma warning disable 1591
5 |
6 | namespace Serilog.Sinks.MSSqlServer
7 | {
8 | public class StandardColumnConfig : ConfigurationElement
9 | {
10 | public StandardColumnConfig()
11 | { }
12 |
13 | [ConfigurationProperty("Name", IsRequired = true, IsKey = true)]
14 | public string Name
15 | {
16 | get { return (string)this[nameof(Name)]; }
17 | set { this[nameof(Name)] = value; }
18 | }
19 | }
20 | }
21 |
22 | #pragma warning restore 1591
23 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/StandardColumnConfigException.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | // Disable XML comment warnings for internal config classes which are required to have public members
4 | #pragma warning disable 1591
5 |
6 | namespace Serilog.Sinks.MSSqlServer
7 | {
8 | public class StandardColumnConfigException : ColumnConfig
9 | {
10 | public StandardColumnConfigException() : base()
11 | { }
12 |
13 | // override to set IsRequired = false
14 | [ConfigurationProperty("ColumnName", IsRequired = false, IsKey = true)]
15 | public override string ColumnName
16 | {
17 | get => base.ColumnName;
18 | set => base.ColumnName = value;
19 | }
20 | }
21 | }
22 |
23 | #pragma warning restore 1591
24 |
25 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/StandardColumnConfigId.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | // Disable XML comment warnings for internal config classes which are required to have public members
4 | #pragma warning disable 1591
5 |
6 | namespace Serilog.Sinks.MSSqlServer
7 | {
8 | public class StandardColumnConfigId : ColumnConfig
9 | {
10 | public StandardColumnConfigId() : base()
11 | { }
12 |
13 | // override to set IsRequired = false
14 | [ConfigurationProperty("ColumnName", IsRequired = false, IsKey = true)]
15 | public override string ColumnName
16 | {
17 | get => base.ColumnName;
18 | set => base.ColumnName = value;
19 | }
20 | }
21 | }
22 |
23 | #pragma warning restore 1591
24 |
25 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/StandardColumnConfigLevel.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | // Disable XML comment warnings for internal config classes which are required to have public members
4 | #pragma warning disable 1591
5 |
6 | namespace Serilog.Sinks.MSSqlServer
7 | {
8 | public class StandardColumnConfigLevel : ColumnConfig
9 | {
10 | public StandardColumnConfigLevel() : base()
11 | { }
12 |
13 | // override to set IsRequired = false
14 | [ConfigurationProperty("ColumnName", IsRequired = false, IsKey = true)]
15 | public override string ColumnName
16 | {
17 | get => base.ColumnName;
18 | set => base.ColumnName = value;
19 | }
20 |
21 | [ConfigurationProperty("StoreAsEnum")]
22 | public string StoreAsEnum
23 | {
24 | get => (string)base[nameof(StoreAsEnum)];
25 | set
26 | {
27 | base[nameof(StoreAsEnum)] = value;
28 | }
29 | }
30 |
31 | }
32 | }
33 |
34 | #pragma warning restore 1591
35 |
36 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/StandardColumnConfigLogEvent.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | // Disable XML comment warnings for internal config classes which are required to have public members
4 | #pragma warning disable 1591
5 |
6 | namespace Serilog.Sinks.MSSqlServer
7 | {
8 | public class StandardColumnConfigLogEvent : ColumnConfig
9 | {
10 | public StandardColumnConfigLogEvent() : base()
11 | { }
12 |
13 | // override to set IsRequired = false
14 | [ConfigurationProperty("ColumnName", IsRequired = false, IsKey = true)]
15 | public override string ColumnName
16 | {
17 | get => base.ColumnName;
18 | set => base.ColumnName = value;
19 | }
20 |
21 | [ConfigurationProperty("ExcludeAdditionalProperties")]
22 | public string ExcludeAdditionalProperties
23 | {
24 | get => (string)base[nameof(ExcludeAdditionalProperties)];
25 | set
26 | {
27 | base[nameof(ExcludeAdditionalProperties)] = value;
28 | }
29 | }
30 |
31 | [ConfigurationProperty("ExcludeStandardColumns")]
32 | public string ExcludeStandardColumns
33 | {
34 | get => (string)base[nameof(ExcludeStandardColumns)];
35 | set
36 | {
37 | base[nameof(ExcludeStandardColumns)] = value;
38 | }
39 | }
40 | }
41 | }
42 |
43 | #pragma warning restore 1591
44 |
45 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/StandardColumnConfigMessage.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | // Disable XML comment warnings for internal config classes which are required to have public members
4 | #pragma warning disable 1591
5 |
6 | namespace Serilog.Sinks.MSSqlServer
7 | {
8 | public class StandardColumnConfigMessage : ColumnConfig
9 | {
10 | public StandardColumnConfigMessage() : base()
11 | { }
12 |
13 | // override to set IsRequired = false
14 | [ConfigurationProperty("ColumnName", IsRequired = false, IsKey = true)]
15 | public override string ColumnName
16 | {
17 | get => base.ColumnName;
18 | set => base.ColumnName = value;
19 | }
20 | }
21 | }
22 |
23 | #pragma warning restore 1591
24 |
25 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/StandardColumnConfigMessageTemplate.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | // Disable XML comment warnings for internal config classes which are required to have public members
4 | #pragma warning disable 1591
5 |
6 | namespace Serilog.Sinks.MSSqlServer
7 | {
8 | public class StandardColumnConfigMessageTemplate : ColumnConfig
9 | {
10 | public StandardColumnConfigMessageTemplate() : base()
11 | { }
12 |
13 | // override to set IsRequired = false
14 | [ConfigurationProperty("ColumnName", IsRequired = false, IsKey = true)]
15 | public override string ColumnName
16 | {
17 | get => base.ColumnName;
18 | set => base.ColumnName = value;
19 | }
20 | }
21 | }
22 |
23 | #pragma warning restore 1591
24 |
25 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/StandardColumnConfigSpanId.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | // Disable XML comment warnings for internal config classes which are required to have public members
4 | #pragma warning disable 1591
5 |
6 | namespace Serilog.Sinks.MSSqlServer
7 | {
8 | public class StandardColumnConfigSpanId : ColumnConfig
9 | {
10 | public StandardColumnConfigSpanId() : base()
11 | { }
12 |
13 | // override to set IsRequired = false
14 | [ConfigurationProperty("ColumnName", IsRequired = false, IsKey = true)]
15 | public override string ColumnName
16 | {
17 | get => base.ColumnName;
18 | set => base.ColumnName = value;
19 | }
20 | }
21 | }
22 |
23 | #pragma warning restore 1591
24 |
25 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/StandardColumnConfigTimeStamp.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | // Disable XML comment warnings for internal config classes which are required to have public members
4 | #pragma warning disable 1591
5 |
6 | namespace Serilog.Sinks.MSSqlServer
7 | {
8 | public class StandardColumnConfigTimeStamp : ColumnConfig
9 | {
10 | public StandardColumnConfigTimeStamp() : base()
11 | { }
12 |
13 | // override to set IsRequired = false
14 | [ConfigurationProperty("ColumnName", IsRequired = false, IsKey = true)]
15 | public override string ColumnName
16 | {
17 | get => base.ColumnName;
18 | set => base.ColumnName = value;
19 | }
20 |
21 | [ConfigurationProperty("ConvertToUtc")]
22 | public string ConvertToUtc
23 | {
24 | get => (string)base[nameof(ConvertToUtc)];
25 | set
26 | {
27 | base[nameof(ConvertToUtc)] = value;
28 | }
29 | }
30 |
31 | }
32 | }
33 |
34 | #pragma warning restore 1591
35 |
36 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/StandardColumnConfigTraceId.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | // Disable XML comment warnings for internal config classes which are required to have public members
4 | #pragma warning disable 1591
5 |
6 | namespace Serilog.Sinks.MSSqlServer
7 | {
8 | public class StandardColumnConfigTraceId : ColumnConfig
9 | {
10 | public StandardColumnConfigTraceId() : base()
11 | { }
12 |
13 | // override to set IsRequired = false
14 | [ConfigurationProperty("ColumnName", IsRequired = false, IsKey = true)]
15 | public override string ColumnName
16 | {
17 | get => base.ColumnName;
18 | set => base.ColumnName = value;
19 | }
20 | }
21 | }
22 |
23 | #pragma warning restore 1591
24 |
25 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SystemConfigurationConnectionStringProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Configuration;
3 | using Serilog.Debugging;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Configuration
6 | {
7 | internal class SystemConfigurationConnectionStringProvider : ISystemConfigurationConnectionStringProvider
8 | {
9 | public string GetConnectionString(string nameOrConnectionString)
10 | {
11 | // If there is an `=`, we assume this is a raw connection string not a named value
12 | // If there are no `=`, attempt to pull the named value from config
13 | if (nameOrConnectionString.IndexOf("=", StringComparison.InvariantCultureIgnoreCase) < 0)
14 | {
15 | var cs = ConfigurationManager.ConnectionStrings[nameOrConnectionString];
16 | if (cs != null)
17 | {
18 | return cs.ConnectionString;
19 | }
20 | else
21 | {
22 | SelfLog.WriteLine("MSSqlServer sink configured value {0} is not found in ConnectionStrings settings and does not appear to be a raw connection string.", nameOrConnectionString);
23 | }
24 | }
25 |
26 | return nameOrConnectionString;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SystemConfigurationSinkOptionsProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using Serilog.Configuration;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Configuration
6 | {
7 | internal class SystemConfigurationSinkOptionsProvider : ISystemConfigurationSinkOptionsProvider
8 | {
9 | public MSSqlServerSinkOptions ConfigureSinkOptions(MSSqlServerConfigurationSection config, MSSqlServerSinkOptions sinkOptions)
10 | {
11 | ReadTableOptions(config, sinkOptions);
12 | ReadBatchSettings(config, sinkOptions);
13 |
14 | return sinkOptions;
15 | }
16 |
17 | private static void ReadTableOptions(MSSqlServerConfigurationSection config, MSSqlServerSinkOptions sinkOptions)
18 | {
19 | SetProperty.IfProvided(config.TableName, nameof(config.TableName.Value), value => sinkOptions.TableName = value);
20 | SetProperty.IfProvided(config.SchemaName, nameof(config.SchemaName.Value), value => sinkOptions.SchemaName = value);
21 | SetProperty.IfProvided(config.AutoCreateSqlDatabase, nameof(config.AutoCreateSqlDatabase.Value),
22 | value => sinkOptions.AutoCreateSqlDatabase = value);
23 | SetProperty.IfProvided(config.AutoCreateSqlTable, nameof(config.AutoCreateSqlTable.Value),
24 | value => sinkOptions.AutoCreateSqlTable = value);
25 | SetProperty.IfProvided(config.EnlistInTransaction, nameof(config.EnlistInTransaction.Value),
26 | value => sinkOptions.EnlistInTransaction = value);
27 | }
28 |
29 | private static void ReadBatchSettings(MSSqlServerConfigurationSection config, MSSqlServerSinkOptions sinkOptions)
30 | {
31 | SetProperty.IfProvided(config.BatchPostingLimit, nameof(config.BatchPostingLimit.Value), value => sinkOptions.BatchPostingLimit = value);
32 | SetProperty.IfProvided(config.BatchPeriod, nameof(config.BatchPeriod.Value), value => sinkOptions.BatchPeriod = TimeSpan.Parse(value, CultureInfo.InvariantCulture));
33 | SetProperty.IfProvided(config.EagerlyEmitFirstEvent, nameof(config.EagerlyEmitFirstEvent.Value),
34 | value => sinkOptions.EagerlyEmitFirstEvent = value);
35 | SetProperty.IfProvided(config.UseSqlBulkCopy, nameof(config.UseSqlBulkCopy.Value),
36 | value => sinkOptions.UseSqlBulkCopy = value);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/ValueConfigElement.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | namespace Serilog.Sinks.MSSqlServer
4 | {
5 | ///
6 | /// Class for reading System.Configuration (app.config, web.config) elements of the form <PropertyName Value="Some value" />'
7 | ///
8 | public class ValueConfigElement : ConfigurationElement
9 | {
10 | ///
11 | /// Intiazes a new instance of ValueConfigElement
12 | ///
13 | public ValueConfigElement()
14 | {
15 | }
16 |
17 | ///
18 | /// Intiazes a new instance of ValueConfigElement with a value
19 | ///
20 | public ValueConfigElement(string value)
21 | {
22 | Value = value;
23 | }
24 |
25 | ///
26 | /// The value property
27 | ///
28 | [ConfigurationProperty(nameof(Value), IsRequired = true)]
29 | public string Value
30 | {
31 | get { return (string)this[nameof(Value)]; }
32 | set { this[nameof(Value)] = value; }
33 | }
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | using static System.FormattableString;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Extensions
4 | {
5 | internal static class StringExtensions
6 | {
7 | public static string TruncateOutput(this string value, int dataLength) =>
8 | dataLength < 0
9 | ? value // No need to truncate if length set to maximum
10 | : value.Truncate(dataLength, "...");
11 |
12 | public static string Truncate(this string value, int maxLength, string suffix)
13 | {
14 | if (value == null) return null;
15 | else if (value.Length <= maxLength) return value;
16 |
17 | var suffixLength = suffix?.Length ?? 0;
18 | if (maxLength <= suffixLength) return string.Empty;
19 |
20 | var correctedMaxLength = maxLength - suffixLength;
21 | return Invariant($"{value.Substring(0, correctedMaxLength)}{suffix}");
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 | // This file is used by Code Analysis to maintain SuppressMessage
2 | // attributes that are applied to this project.
3 | // Project-level suppressions either have no target or are given
4 | // a specific target and scoped to a namespace, type, member, etc.
5 |
6 | using System.Diagnostics.CodeAnalysis;
7 |
8 | [assembly: SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Supplying string literals and not using resources is accepted within this project.", Scope = "namespaceanddescendants", Target = "~N:Serilog.Sinks.MSSqlServer")]
9 | [assembly: SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "Too hard to change. Accepted for now.", Scope = "namespaceanddescendants", Target = "~N:Serilog.Sinks.MSSqlServer")]
10 | [assembly: SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "Cannot be changed on public classes for backward compatibility reasons.", Scope = "namespaceanddescendants", Target = "~N:Serilog.Sinks.MSSqlServer")]
11 | [assembly: SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "Cannot be changed on public classes for backward compatibility reasons.", Scope = "type", Target = "~T:Serilog.Sinks.MSSqlServer.ColumnOptions")]
12 | [assembly: SuppressMessage("Design", "CA1010:Collections should implement generic interface", Justification = "Cannot be changed on public classes for backward compatibility reasons.", Scope = "namespaceanddescendants", Target = "~N:Serilog.Sinks.MSSqlServer")]
13 | [assembly: SuppressMessage("Performance", "CA1822: Mark members as static", Justification = "Using instance members over static for better testablilty.", Scope = "namespaceanddescendants", Target = "~N:Serilog.Sinks.MSSqlServer")]
14 | [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Too hard to change. Accepted for now.", Scope = "member", Target = "~M:Serilog.Sinks.MSSqlServer.SetProperty.IfNotNull``1(System.String,Serilog.Sinks.MSSqlServer.SetProperty.PropertySetter{``0})")]
15 | [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Too hard to change. Accepted for now.", Scope = "member", Target = "~M:Serilog.Sinks.MSSqlServer.Platform.SqlCommandExecutor.Execute()")]
16 | [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Too hard to change. Accepted for now.", Scope = "member", Target = "~M:Serilog.Sinks.MSSqlServer.Output.StandardColumnDataGenerator.ConvertPropertiesToXmlStructure(System.Collections.Generic.IEnumerable{System.Collections.Generic.KeyValuePair{System.String,Serilog.Events.LogEventPropertyValue}})~System.String")]
17 | [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Too hard to change. Accepted for now.", Scope = "member", Target = "~M:Serilog.Sinks.MSSqlServer.Output.AdditionalColumnDataGenerator.TryChangeType(System.Object,System.Type,System.Object@)~System.Boolean")]
18 | [assembly: SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "Cannot be changed on public classes for backward compatibility reasons.", Scope = "type", Target = "~T:Serilog.Sinks.MSSqlServer.StandardColumnConfigException")]
19 | [assembly: SuppressMessage("Naming", "CA1725:Parameter names should match base declaration", Justification = "Cannot be changed on public classes for backward compatibility reasons.", Scope = "member", Target = "~M:Serilog.Sinks.MSSqlServer.MSSqlServerSink.EmitBatchAsync(System.Collections.Generic.IEnumerable{Serilog.Events.LogEvent})~System.Threading.Tasks.Task")]
20 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Images/serilog-sink-nuget.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serilog-mssql/serilog-sinks-mssqlserver/144ad397ed50e6b26d9c02cdd6f052253301e9fc/src/Serilog.Sinks.MSSqlServer/Images/serilog-sink-nuget.png
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/ColumnOptions/ExceptionColumnOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 |
4 | namespace Serilog.Sinks.MSSqlServer
5 | {
6 | public partial class ColumnOptions // Standard Column options are inner classes for backwards-compatibility.
7 | {
8 | ///
9 | /// Options for the Exception column.
10 | ///
11 | public class ExceptionColumnOptions : SqlColumn
12 | {
13 | ///
14 | /// Constructor.
15 | ///
16 | public ExceptionColumnOptions() : base()
17 | {
18 | StandardColumnIdentifier = StandardColumn.Exception;
19 | DataType = SqlDbType.NVarChar;
20 | }
21 |
22 | ///
23 | /// The Exception column defaults to NVarChar and must be of a character-storage data type.
24 | ///
25 | public new SqlDbType DataType
26 | {
27 | get => base.DataType;
28 | set
29 | {
30 | if (!SqlDataTypes.VariableCharacterColumnTypes.Contains(value))
31 | throw new ArgumentException("The Standard Column \"Exception\" must be NVarChar or VarChar.");
32 | base.DataType = value;
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/ColumnOptions/IdColumnOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 | using Serilog.Debugging;
4 |
5 | namespace Serilog.Sinks.MSSqlServer
6 | {
7 | public partial class ColumnOptions // Standard Column options are inner classes for backwards-compatibility.
8 | {
9 | ///
10 | /// Options for the Id column.
11 | ///
12 | public class IdColumnOptions : SqlColumn
13 | {
14 | ///
15 | /// Constructor.
16 | ///
17 | public IdColumnOptions() : base()
18 | {
19 | StandardColumnIdentifier = StandardColumn.Id;
20 | DataType = SqlDbType.Int;
21 | base.AllowNull = false;
22 | }
23 |
24 | ///
25 | /// The Id column must be either Int (default) or BigInt.
26 | ///
27 | public new SqlDbType DataType
28 | {
29 | get => base.DataType;
30 | set
31 | {
32 | if (value != SqlDbType.Int && value != SqlDbType.BigInt)
33 | throw new ArgumentException("The Standard Column \"Id\" must be of data type Int or BigInt.");
34 | base.DataType = value;
35 | }
36 | }
37 |
38 | ///
39 | /// The Id column must never allow null values (it is an auto-incremnting identity value and normally the primary key).
40 | ///
41 | public new bool AllowNull // shadow base class with "new" to prevent accidentally setting this to true
42 | {
43 | get => false;
44 | set
45 | {
46 | if (value)
47 | throw new ArgumentException("The Standard Column \"Id\" must always be NOT NULL.");
48 | }
49 | }
50 |
51 | ///
52 | /// Overrides the SqlColumn base method to also set Id-specific properties.
53 | ///
54 | internal override DataColumn AsDataColumn()
55 | {
56 | var dataColumn = base.AsDataColumn();
57 | dataColumn.AutoIncrement = true;
58 | dataColumn.Unique = true;
59 | dataColumn.AllowDBNull = false;
60 | return dataColumn;
61 | }
62 |
63 | ///
64 | /// Set the DataType property to BigInt.
65 | ///
66 | [Obsolete("Set the DataType property to BigInt. This will be removed in a future release.", error: false)]
67 | public bool BigInt
68 | {
69 | get => (base.DataType == SqlDbType.BigInt);
70 | set
71 | {
72 | base.DataType = value ? SqlDbType.BigInt : SqlDbType.Int;
73 | SelfLog.WriteLine("Deprecated: The Standard Column \"Id.BigInt\" property will be removed in a future release. Please set the \"DataType\" property.");
74 | }
75 | }
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/ColumnOptions/LevelColumnOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 |
4 | namespace Serilog.Sinks.MSSqlServer
5 | {
6 | public partial class ColumnOptions // Standard Column options are inner classes for backwards-compatibility.
7 | {
8 | ///
9 | /// Options for the Level column.
10 | ///
11 | public class LevelColumnOptions : SqlColumn
12 | {
13 | ///
14 | /// Constructor.
15 | ///
16 | public LevelColumnOptions() : base()
17 | {
18 | StandardColumnIdentifier = StandardColumn.Level;
19 | DataType = SqlDbType.NVarChar;
20 | }
21 |
22 | ///
23 | /// The Level column must be either NVarChar (the default) or TinyInt (which stores the underlying Level enum value).
24 | /// The recommended DataLength for NVarChar is 16 characters.
25 | ///
26 | public new SqlDbType DataType
27 | {
28 | get => base.DataType;
29 | set
30 | {
31 | if (!SqlDataTypes.VariableCharacterColumnTypes.Contains(value) && value != SqlDbType.TinyInt)
32 | throw new ArgumentException("The Standard Column \"Level\" must be of data type NVarChar, VarChar or TinyInt.");
33 | base.DataType = value;
34 | }
35 | }
36 |
37 |
38 | ///
39 | /// If true will store Level as an enum in a tinyint column as opposed to a string.
40 | ///
41 | public bool StoreAsEnum
42 | {
43 | get => (base.DataType == SqlDbType.TinyInt);
44 | set
45 | {
46 | base.DataType = value ? SqlDbType.TinyInt : SqlDbType.NVarChar;
47 | }
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/ColumnOptions/LogEventColumnOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 |
4 | namespace Serilog.Sinks.MSSqlServer
5 | {
6 | public partial class ColumnOptions // Standard Column options are inner classes for backwards-compatibility.
7 | {
8 | ///
9 | /// Options for the LogEvent column.
10 | ///
11 | public class LogEventColumnOptions : SqlColumn
12 | {
13 | ///
14 | /// Constructor.
15 | ///
16 | public LogEventColumnOptions() : base()
17 | {
18 | StandardColumnIdentifier = StandardColumn.LogEvent;
19 | DataType = SqlDbType.NVarChar;
20 | }
21 |
22 | ///
23 | /// The LogEvent column defaults to NVarChar and must be of a character-storage data type.
24 | ///
25 | public new SqlDbType DataType
26 | {
27 | get => base.DataType;
28 | set
29 | {
30 | if (!SqlDataTypes.VariableCharacterColumnTypes.Contains(value))
31 | throw new ArgumentException("The Standard Column \"LogEvent\" must be NVarChar or VarChar.");
32 | base.DataType = value;
33 | }
34 | }
35 |
36 | ///
37 | /// Exclude properties from the LogEvent column if they are being saved to additional columns.
38 | /// Defaults to false for backwards-compatibility, but true is the recommended setting.
39 | ///
40 | public bool ExcludeAdditionalProperties { get; set; }
41 |
42 | ///
43 | /// Whether to include Standard Columns in the LogEvent column (for backwards compatibility).
44 | /// Defaults to false for backwards-compatibility, but true is the recommended setting.
45 | ///
46 | public bool ExcludeStandardColumns { get; set; }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/ColumnOptions/MessageColumnOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 |
4 | namespace Serilog.Sinks.MSSqlServer
5 | {
6 | public partial class ColumnOptions // Standard Column options are inner classes for backwards-compatibility.
7 | {
8 | ///
9 | /// Options for the message column
10 | ///
11 | public class MessageColumnOptions : SqlColumn
12 | {
13 | ///
14 | /// Constructor.
15 | ///
16 | public MessageColumnOptions() : base()
17 | {
18 | StandardColumnIdentifier = StandardColumn.Message;
19 | DataType = SqlDbType.NVarChar;
20 | }
21 |
22 | ///
23 | /// The Message column defaults to NVarChar and must be of a character-storage data type.
24 | ///
25 | public new SqlDbType DataType
26 | {
27 | get => base.DataType;
28 | set
29 | {
30 | if (!SqlDataTypes.VariableCharacterColumnTypes.Contains(value))
31 | throw new ArgumentException("The Standard Column \"Message\" must be NVarChar or VarChar.");
32 | base.DataType = value;
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/ColumnOptions/MessageTemplateColumnOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 |
4 | namespace Serilog.Sinks.MSSqlServer
5 | {
6 | public partial class ColumnOptions // Standard Column options are inner classes for backwards-compatibility.
7 | {
8 | ///
9 | /// Options for the MessageTemplate column.
10 | ///
11 | public class MessageTemplateColumnOptions : SqlColumn
12 | {
13 | ///
14 | /// Constructor.
15 | ///
16 | public MessageTemplateColumnOptions() : base()
17 | {
18 | StandardColumnIdentifier = StandardColumn.MessageTemplate;
19 | DataType = SqlDbType.NVarChar;
20 | }
21 |
22 | ///
23 | /// The MessageTemplate column defaults to NVarChar and must be of a character-storage data type.
24 | ///
25 | public new SqlDbType DataType
26 | {
27 | get => base.DataType;
28 | set
29 | {
30 | if (!SqlDataTypes.VariableCharacterColumnTypes.Contains(value))
31 | throw new ArgumentException("The Standard Column \"MessageTemplate\" must be NVarChar or VarChar.");
32 | base.DataType = value;
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/ColumnOptions/SpanIdColumnOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 |
4 | namespace Serilog.Sinks.MSSqlServer
5 | {
6 | public partial class ColumnOptions // Standard Column options are inner classes for backwards-compatibility.
7 | {
8 | ///
9 | /// Options for the SpanId column.
10 | ///
11 | public class SpanIdColumnOptions : SqlColumn
12 | {
13 | ///
14 | /// Constructor.
15 | ///
16 | public SpanIdColumnOptions() : base()
17 | {
18 | StandardColumnIdentifier = StandardColumn.SpanId;
19 | DataType = SqlDbType.NVarChar;
20 | }
21 |
22 | ///
23 | /// The SpanId column defaults to NVarChar and must be of a character-storage data type.
24 | ///
25 | public new SqlDbType DataType
26 | {
27 | get => base.DataType;
28 | set
29 | {
30 | if (!SqlDataTypes.VariableCharacterColumnTypes.Contains(value))
31 | throw new ArgumentException("The Standard Column \"SpanId\" must be NVarChar or VarChar.");
32 | base.DataType = value;
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/ColumnOptions/TimeStampColumnOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 |
4 | namespace Serilog.Sinks.MSSqlServer
5 | {
6 | public partial class ColumnOptions // Standard Column options are inner classes for backwards-compatibility.
7 | {
8 | ///
9 | /// Options for the TimeStamp column.
10 | ///
11 | public class TimeStampColumnOptions : SqlColumn
12 | {
13 | ///
14 | /// Constructor.
15 | ///
16 | public TimeStampColumnOptions() : base()
17 | {
18 | StandardColumnIdentifier = StandardColumn.TimeStamp;
19 | DataType = SqlDbType.DateTime;
20 | }
21 |
22 | ///
23 | /// The TimeStamp column only supports the DateTime, DateTime2 and DateTimeOffset data types.
24 | ///
25 | public new SqlDbType DataType
26 | {
27 | get => base.DataType;
28 | set
29 | {
30 | if (value != SqlDbType.DateTime && value != SqlDbType.DateTimeOffset && value != SqlDbType.DateTime2)
31 | throw new ArgumentException("The Standard Column \"TimeStamp\" only supports the DateTime, DateTime2 and DateTimeOffset formats.");
32 | base.DataType = value;
33 | }
34 | }
35 |
36 | ///
37 | /// If true, the logging source's local time is converted to Coordinated Universal Time.
38 | /// By definition, UTC does not include any timezone or timezone offset information.
39 | ///
40 | public bool ConvertToUtc { get; set; }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/ColumnOptions/TraceIdColumnOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 |
4 | namespace Serilog.Sinks.MSSqlServer
5 | {
6 | public partial class ColumnOptions // Standard Column options are inner classes for backwards-compatibility.
7 | {
8 | ///
9 | /// Options for the TraceId column.
10 | ///
11 | public class TraceIdColumnOptions : SqlColumn
12 | {
13 | ///
14 | /// Constructor.
15 | ///
16 | public TraceIdColumnOptions() : base()
17 | {
18 | StandardColumnIdentifier = StandardColumn.TraceId;
19 | DataType = SqlDbType.NVarChar;
20 | }
21 |
22 | ///
23 | /// The TraceId column defaults to NVarChar and must be of a character-storage data type.
24 | ///
25 | public new SqlDbType DataType
26 | {
27 | get => base.DataType;
28 | set
29 | {
30 | if (!SqlDataTypes.VariableCharacterColumnTypes.Contains(value))
31 | throw new ArgumentException("The Standard Column \"TraceId\" must be NVarChar or VarChar.");
32 | base.DataType = value;
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Dependencies/SinkDependencies.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.MSSqlServer.Platform;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Dependencies
4 | {
5 | internal class SinkDependencies
6 | {
7 | public ISqlCommandExecutor SqlDatabaseCreator { get; set; }
8 | public ISqlCommandExecutor SqlTableCreator { get; set; }
9 | public ISqlBulkBatchWriter SqlBulkBatchWriter { get; set; }
10 | public ISqlLogEventWriter SqlLogEventWriter { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog.Formatting;
3 | using Serilog.Sinks.MSSqlServer.Output;
4 | using Serilog.Sinks.MSSqlServer.Platform;
5 | using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
6 |
7 | namespace Serilog.Sinks.MSSqlServer.Dependencies
8 | {
9 | internal static class SinkDependenciesFactory
10 | {
11 | internal static SinkDependencies Create(
12 | string connectionString,
13 | MSSqlServerSinkOptions sinkOptions,
14 | IFormatProvider formatProvider,
15 | ColumnOptions columnOptions,
16 | ITextFormatter logEventFormatter)
17 | {
18 | columnOptions = columnOptions ?? new ColumnOptions();
19 | columnOptions.FinalizeConfigurationForSinkConstructor();
20 |
21 | // Add 'Enlist=false', so that ambient transactions (TransactionScope) will not affect/rollback logging
22 | // unless sink option EnlistInTransaction is set to true.
23 | var sqlConnectionStringBuilderWrapper = new SqlConnectionStringBuilderWrapper(
24 | connectionString, sinkOptions.EnlistInTransaction);
25 | var sqlConnectionFactory = new SqlConnectionFactory(sqlConnectionStringBuilderWrapper, sinkOptions.ConnectionConfiguration);
26 | var sqlCommandFactory = new SqlCommandFactory();
27 | var dataTableCreator = new DataTableCreator(sinkOptions.TableName, columnOptions);
28 | var sqlCreateTableWriter = new SqlCreateTableWriter(sinkOptions.SchemaName,
29 | sinkOptions.TableName, columnOptions, dataTableCreator);
30 |
31 | var sqlConnectionStringBuilderWrapperNoDb = new SqlConnectionStringBuilderWrapper(
32 | connectionString, sinkOptions.EnlistInTransaction)
33 | {
34 | InitialCatalog = ""
35 | };
36 | var sqlConnectionFactoryNoDb =
37 | new SqlConnectionFactory(sqlConnectionStringBuilderWrapperNoDb, sinkOptions.ConnectionConfiguration);
38 | var sqlCreateDatabaseWriter = new SqlCreateDatabaseWriter(sqlConnectionStringBuilderWrapper.InitialCatalog);
39 |
40 | var logEventDataGenerator =
41 | new LogEventDataGenerator(columnOptions,
42 | new StandardColumnDataGenerator(columnOptions, formatProvider,
43 | new XmlPropertyFormatter(),
44 | logEventFormatter),
45 | new AdditionalColumnDataGenerator(
46 | new ColumnSimplePropertyValueResolver(),
47 | new ColumnHierarchicalPropertyValueResolver()));
48 |
49 | var sinkDependencies = new SinkDependencies
50 | {
51 | SqlDatabaseCreator = new SqlDatabaseCreator(
52 | sqlCreateDatabaseWriter, sqlConnectionFactoryNoDb, sqlCommandFactory),
53 | SqlTableCreator = new SqlTableCreator(
54 | sqlCreateTableWriter, sqlConnectionFactory, sqlCommandFactory),
55 | SqlBulkBatchWriter = new SqlBulkBatchWriter(
56 | sinkOptions.TableName, sinkOptions.SchemaName, columnOptions.DisableTriggers,
57 | dataTableCreator, sqlConnectionFactory, logEventDataGenerator),
58 | SqlLogEventWriter = new SqlInsertStatementWriter(
59 | sinkOptions.TableName, sinkOptions.SchemaName,
60 | sqlConnectionFactory, sqlCommandFactory, logEventDataGenerator)
61 | };
62 |
63 | return sinkDependencies;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Options/SinkOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Sinks.MSSqlServer.Options
4 | {
5 | ///
6 | /// Provides MSSqlServerSink with configurable options.
7 | ///
8 | [Obsolete("Replace SinkOptions with MSSqlServerSinkOptions. SinkOptions will be removed in a future release.", error: false)]
9 | public class SinkOptions : MSSqlServerSinkOptions
10 | {
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/ColumnHierarchicalPropertyValueResolver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Serilog.Events;
5 |
6 | namespace Serilog.Sinks.MSSqlServer.Output
7 | {
8 | internal class ColumnHierarchicalPropertyValueResolver : IColumnHierarchicalPropertyValueResolver
9 | {
10 | public KeyValuePair GetPropertyValueForColumn(SqlColumn additionalColumn, IReadOnlyDictionary properties)
11 | {
12 | var propertyNameHierarchy = additionalColumn.PropertyNameHierarchy;
13 | KeyValuePair? nullableProperty = properties.FirstOrDefault(p => p.Key == propertyNameHierarchy[0]);
14 | if (nullableProperty == null)
15 | {
16 | // Top level property not found, return default
17 | return default;
18 | }
19 |
20 | var property = nullableProperty.Value;
21 | if (property.Value is StructureValue structureValue)
22 | {
23 | // Continue on sub property level
24 | var propertyNameHierarchyRemainder = new ArraySegment(propertyNameHierarchy.ToArray(), 1, additionalColumn.PropertyNameHierarchy.Count - 1);
25 | return GetSubPropertyValueForColumnRecursive(propertyNameHierarchyRemainder, structureValue.Properties);
26 | }
27 | else
28 | {
29 | // Sub property not found, return default
30 | return default;
31 | }
32 | }
33 |
34 | private KeyValuePair GetSubPropertyValueForColumnRecursive(IReadOnlyList propertyNameHierarchy, IReadOnlyList properties)
35 | {
36 | var property = properties.FirstOrDefault(p => p.Name == propertyNameHierarchy[0]);
37 | if (property == null)
38 | {
39 | // Current sub property not found, return default
40 | return default;
41 | }
42 |
43 | if (propertyNameHierarchy.Count == 1)
44 | {
45 | // Current sub property found and no further levels in property name of column
46 | // Return final property value
47 | return new KeyValuePair(property.Name, property.Value);
48 | }
49 | else
50 | {
51 | if (property.Value is StructureValue structureValue)
52 | {
53 | // Continue on next sub property level
54 | var propertyNameHierarchyRemainder = new ArraySegment(propertyNameHierarchy.ToArray(), 1, propertyNameHierarchy.Count - 1);
55 | return GetSubPropertyValueForColumnRecursive(propertyNameHierarchyRemainder, structureValue.Properties);
56 | }
57 | else
58 | {
59 | // Next sub property not found, return default
60 | return default;
61 | }
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/ColumnSimplePropertyValueResolver.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Serilog.Events;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Output
6 | {
7 | internal class ColumnSimplePropertyValueResolver : IColumnSimplePropertyValueResolver
8 | {
9 | public KeyValuePair GetPropertyValueForColumn(SqlColumn additionalColumn, IReadOnlyDictionary properties)
10 | {
11 | return properties.FirstOrDefault(p => p.Key == additionalColumn.PropertyName);
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/IAdditionalColumnDataGenerator.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Serilog.Events;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Output
5 | {
6 | internal interface IAdditionalColumnDataGenerator
7 | {
8 | KeyValuePair GetAdditionalColumnNameAndValue(SqlColumn additionalColumn, IReadOnlyDictionary properties);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/IColumnHierarchicalPropertyValueResolver.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Events;
2 | using System.Collections.Generic;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Output
5 | {
6 | internal interface IColumnHierarchicalPropertyValueResolver
7 | {
8 | KeyValuePair GetPropertyValueForColumn(SqlColumn additionalColumn, IReadOnlyDictionary properties);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/IColumnSimplePropertyValueResolver.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Events;
2 | using System.Collections.Generic;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Output
5 | {
6 | internal interface IColumnSimplePropertyValueResolver
7 | {
8 | KeyValuePair GetPropertyValueForColumn(SqlColumn additionalColumn, IReadOnlyDictionary properties);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/ILogEventDataGenerator.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Serilog.Events;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Output
5 | {
6 | internal interface ILogEventDataGenerator
7 | {
8 | IEnumerable> GetColumnsAndValues(LogEvent logEvent);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/IStandardColumnDataGenerator.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Serilog.Events;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Output
5 | {
6 | internal interface IStandardColumnDataGenerator
7 | {
8 | KeyValuePair GetStandardColumnNameAndValue(StandardColumn column, LogEvent logEvent);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/IXmlPropertyFormatter.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Events;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Output
4 | {
5 | internal interface IXmlPropertyFormatter
6 | {
7 | string GetValidElementName(string name);
8 | string Simplify(LogEventPropertyValue value, ColumnOptions.PropertiesColumnOptions options);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Output/LogEventDataGenerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Serilog.Events;
5 |
6 | namespace Serilog.Sinks.MSSqlServer.Output
7 | {
8 | internal class LogEventDataGenerator : ILogEventDataGenerator
9 | {
10 | private readonly ColumnOptions _columnOptions;
11 | private readonly IStandardColumnDataGenerator _standardColumnDataGenerator;
12 | private readonly IAdditionalColumnDataGenerator _additionalColumnDataGenerator;
13 |
14 | public LogEventDataGenerator(
15 | ColumnOptions columnOptions,
16 | IStandardColumnDataGenerator standardColumnDataGenerator,
17 | IAdditionalColumnDataGenerator additionalColumnDataGenerator)
18 | {
19 | _columnOptions = columnOptions ?? throw new ArgumentNullException(nameof(columnOptions));
20 | _standardColumnDataGenerator = standardColumnDataGenerator ?? throw new ArgumentNullException(nameof(standardColumnDataGenerator));
21 | _additionalColumnDataGenerator = additionalColumnDataGenerator ?? throw new ArgumentNullException(nameof(additionalColumnDataGenerator));
22 | }
23 |
24 | public IEnumerable> GetColumnsAndValues(LogEvent logEvent)
25 | {
26 | // skip Id (auto-incrementing identity)
27 | foreach (var column in _columnOptions.Store.Where(c => c != StandardColumn.Id))
28 | {
29 | yield return _standardColumnDataGenerator.GetStandardColumnNameAndValue(column, logEvent);
30 | }
31 |
32 | if (_columnOptions.AdditionalColumns != null)
33 | {
34 | foreach (var additionalColumn in _columnOptions.AdditionalColumns)
35 | {
36 | yield return _additionalColumnDataGenerator.GetAdditionalColumnNameAndValue(additionalColumn, logEvent.Properties);
37 | }
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/DataTableCreator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Platform
5 | {
6 | internal class DataTableCreator : IDataTableCreator
7 | {
8 | private readonly string _tableName;
9 | private readonly ColumnOptions _columnOptions;
10 |
11 | public DataTableCreator(string tableName, ColumnOptions columnOptions)
12 | {
13 | _tableName = tableName ?? throw new ArgumentNullException(nameof(tableName));
14 | _columnOptions = columnOptions ?? throw new ArgumentNullException(nameof(columnOptions));
15 | }
16 |
17 | public DataTable CreateDataTable()
18 | {
19 | var eventsTable = new DataTable(_tableName);
20 |
21 | foreach (var standardColumn in _columnOptions.Store)
22 | {
23 | var standardOpts = _columnOptions.GetStandardColumnOptions(standardColumn);
24 | var dataColumn = standardOpts.AsDataColumn();
25 | eventsTable.Columns.Add(dataColumn);
26 | if (standardOpts == _columnOptions.PrimaryKey)
27 | eventsTable.PrimaryKey = new DataColumn[] { dataColumn };
28 | }
29 |
30 | if (_columnOptions.AdditionalColumns != null)
31 | {
32 | foreach (var addCol in _columnOptions.AdditionalColumns)
33 | {
34 | var dataColumn = addCol.AsDataColumn();
35 | eventsTable.Columns.Add(dataColumn);
36 | if (addCol == _columnOptions.PrimaryKey)
37 | eventsTable.PrimaryKey = new DataColumn[] { dataColumn };
38 | }
39 | }
40 |
41 | return eventsTable;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/IDataTableCreator.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Platform
4 | {
5 | internal interface IDataTableCreator
6 | {
7 | DataTable CreateDataTable();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/ISqlBulkBatchWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using Serilog.Events;
5 |
6 | namespace Serilog.Sinks.MSSqlServer.Platform
7 | {
8 | internal interface ISqlBulkBatchWriter : IDisposable
9 | {
10 | Task WriteBatch(IEnumerable events);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/ISqlCommandExecutor.cs:
--------------------------------------------------------------------------------
1 | namespace Serilog.Sinks.MSSqlServer.Platform
2 | {
3 | internal interface ISqlCommandExecutor
4 | {
5 | void Execute();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/ISqlCommandFactory.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Platform
4 | {
5 | internal interface ISqlCommandFactory
6 | {
7 | ISqlCommandWrapper CreateCommand(string cmdText, ISqlConnectionWrapper sqlConnection);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/ISqlConnectionFactory.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Platform
4 | {
5 | internal interface ISqlConnectionFactory
6 | {
7 | ISqlConnectionWrapper Create();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/ISqlCreateDatabaseWriter.cs:
--------------------------------------------------------------------------------
1 | namespace Serilog.Sinks.MSSqlServer.Platform
2 | {
3 | internal interface ISqlCreateDatabaseWriter : ISqlWriter
4 | {
5 | string DatabaseName { get; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/ISqlCreateTableWriter.cs:
--------------------------------------------------------------------------------
1 | namespace Serilog.Sinks.MSSqlServer.Platform
2 | {
3 | internal interface ISqlCreateTableWriter : ISqlWriter
4 | {
5 | string TableName { get; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/ISqlLogEventWriter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using Serilog.Events;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Platform
6 | {
7 | internal interface ISqlLogEventWriter
8 | {
9 | void WriteEvent(LogEvent logEvent);
10 |
11 | Task WriteEvents(IEnumerable events);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/ISqlWriter.cs:
--------------------------------------------------------------------------------
1 | namespace Serilog.Sinks.MSSqlServer.Platform
2 | {
3 | internal interface ISqlWriter
4 | {
5 | string GetSql();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/ISqlBulkCopyWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 | using System.Threading.Tasks;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Platform.SqlClient
6 | {
7 | internal interface ISqlBulkCopyWrapper : IDisposable
8 | {
9 | void AddSqlBulkCopyColumnMapping(string sourceColumn, string destinationColumn);
10 | Task WriteToServerAsync(DataTable table);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/ISqlCommandWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Platform.SqlClient
5 | {
6 | internal interface ISqlCommandWrapper : IDisposable
7 | {
8 | void AddParameter(string parameterName, object value);
9 | int ExecuteNonQuery();
10 | Task ExecuteNonQueryAsync();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/ISqlConnectionStringBuilderWrapper.cs:
--------------------------------------------------------------------------------
1 | namespace Serilog.Sinks.MSSqlServer.Platform.SqlClient
2 | {
3 | internal interface ISqlConnectionStringBuilderWrapper
4 | {
5 | string ConnectionString { get; }
6 | string InitialCatalog { get; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/ISqlConnectionWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Microsoft.Data.SqlClient;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Platform.SqlClient
6 | {
7 | internal interface ISqlConnectionWrapper : IDisposable
8 | {
9 | SqlConnection SqlConnection { get; }
10 |
11 | void Open();
12 | Task OpenAsync();
13 | ISqlBulkCopyWrapper CreateSqlBulkCopy(bool disableTriggers, string destinationTableName);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/SqlBulkCopyWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 | using System.Threading.Tasks;
4 | using Microsoft.Data.SqlClient;
5 |
6 | namespace Serilog.Sinks.MSSqlServer.Platform.SqlClient
7 | {
8 | internal class SqlBulkCopyWrapper : ISqlBulkCopyWrapper
9 | {
10 | private readonly SqlBulkCopy _sqlBulkCopy;
11 | private bool _disposedValue;
12 |
13 | public SqlBulkCopyWrapper(SqlBulkCopy sqlBulkCopy)
14 | {
15 | _sqlBulkCopy = sqlBulkCopy ?? throw new ArgumentNullException(nameof(sqlBulkCopy));
16 | }
17 |
18 | public void AddSqlBulkCopyColumnMapping(string sourceColumn, string destinationColumn)
19 | {
20 | var mapping = new SqlBulkCopyColumnMapping(sourceColumn, destinationColumn);
21 | _sqlBulkCopy.ColumnMappings.Add(mapping);
22 | }
23 |
24 | public Task WriteToServerAsync(DataTable table) =>
25 | _sqlBulkCopy.WriteToServerAsync(table);
26 |
27 | protected virtual void Dispose(bool disposing)
28 | {
29 | if (!_disposedValue)
30 | {
31 | ((IDisposable)_sqlBulkCopy).Dispose();
32 | _disposedValue = true;
33 | }
34 | }
35 |
36 | public void Dispose()
37 | {
38 | Dispose(disposing: true);
39 | GC.SuppressFinalize(this);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/SqlCommandWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 | using System.Threading.Tasks;
4 | using Microsoft.Data.SqlClient;
5 |
6 | namespace Serilog.Sinks.MSSqlServer.Platform.SqlClient
7 | {
8 | internal class SqlCommandWrapper : ISqlCommandWrapper
9 | {
10 | private readonly SqlCommand _sqlCommand;
11 | private bool _disposedValue;
12 |
13 | public SqlCommandWrapper(SqlCommand sqlCommand)
14 | {
15 | _sqlCommand = sqlCommand ?? throw new ArgumentNullException(nameof(sqlCommand));
16 | }
17 |
18 | public void AddParameter(string parameterName, object value)
19 | {
20 | var parameter = new SqlParameter(parameterName, value ?? DBNull.Value);
21 |
22 | // The default is SqlDbType.DateTime, which will truncate the DateTime value if the actual
23 | // type in the database table is datetime2. So we explicitly set it to DateTime2, which will
24 | // work both if the field in the table is datetime and datetime2, which is also consistent with
25 | // the behavior of the non-audit sink.
26 | if (value is DateTime)
27 | parameter.SqlDbType = SqlDbType.DateTime2;
28 |
29 | _sqlCommand.Parameters.Add(parameter);
30 | }
31 |
32 | public int ExecuteNonQuery() =>
33 | _sqlCommand.ExecuteNonQuery();
34 |
35 | public Task ExecuteNonQueryAsync() =>
36 | _sqlCommand.ExecuteNonQueryAsync();
37 |
38 | protected virtual void Dispose(bool disposing)
39 | {
40 | if (!_disposedValue)
41 | {
42 | _sqlCommand.Dispose();
43 | _disposedValue = true;
44 | }
45 | }
46 |
47 | public void Dispose()
48 | {
49 | Dispose(disposing: true);
50 | GC.SuppressFinalize(this);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/SqlConnectionStringBuilderWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Data.SqlClient;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Platform.SqlClient
5 | {
6 | internal class SqlConnectionStringBuilderWrapper : ISqlConnectionStringBuilderWrapper
7 | {
8 | private readonly SqlConnectionStringBuilder _sqlConnectionStringBuilder;
9 |
10 | public SqlConnectionStringBuilderWrapper(string connectionString, bool enlist)
11 | {
12 | if (string.IsNullOrWhiteSpace(connectionString))
13 | {
14 | throw new ArgumentNullException(nameof(connectionString));
15 | }
16 |
17 | _sqlConnectionStringBuilder = new SqlConnectionStringBuilder(connectionString)
18 | {
19 | Enlist = enlist
20 | };
21 | }
22 |
23 | public string ConnectionString => _sqlConnectionStringBuilder.ConnectionString;
24 |
25 | public string InitialCatalog
26 | {
27 | get => _sqlConnectionStringBuilder.InitialCatalog;
28 | set => _sqlConnectionStringBuilder.InitialCatalog = value;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlClient/SqlConnectionWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Microsoft.Data.SqlClient;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Platform.SqlClient
6 | {
7 | internal class SqlConnectionWrapper : ISqlConnectionWrapper
8 | {
9 | private readonly SqlConnection _sqlConnection;
10 | private bool _disposedValue;
11 |
12 | public SqlConnectionWrapper(string connectionString, Action connectionConfiguration = null)
13 | {
14 | _sqlConnection = new SqlConnection(connectionString);
15 | if (connectionConfiguration != null)
16 | {
17 | connectionConfiguration(_sqlConnection);
18 | }
19 | }
20 |
21 | public SqlConnection SqlConnection => _sqlConnection;
22 |
23 | public void Open()
24 | {
25 | _sqlConnection.Open();
26 | }
27 |
28 | public async Task OpenAsync()
29 | {
30 | await _sqlConnection.OpenAsync().ConfigureAwait(false);
31 | }
32 |
33 | public ISqlBulkCopyWrapper CreateSqlBulkCopy(bool disableTriggers, string destinationTableName)
34 | {
35 | var sqlBulkCopy = disableTriggers
36 | ? new SqlBulkCopy(_sqlConnection)
37 | : new SqlBulkCopy(_sqlConnection, SqlBulkCopyOptions.CheckConstraints | SqlBulkCopyOptions.FireTriggers, null);
38 | sqlBulkCopy.DestinationTableName = destinationTableName;
39 |
40 | return new SqlBulkCopyWrapper(sqlBulkCopy);
41 | }
42 |
43 | protected virtual void Dispose(bool disposing)
44 | {
45 | if (!_disposedValue)
46 | {
47 | _sqlConnection.Dispose();
48 | _disposedValue = true;
49 | }
50 | }
51 |
52 | public void Dispose()
53 | {
54 | Dispose(disposing: true);
55 | GC.SuppressFinalize(this);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlCommandExecutor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Platform
4 | {
5 | internal abstract class SqlCommandExecutor : ISqlCommandExecutor
6 | {
7 | private readonly ISqlWriter _sqlWriter;
8 | private readonly ISqlConnectionFactory _sqlConnectionFactory;
9 | private readonly ISqlCommandFactory _sqlCommandFactory;
10 |
11 | public SqlCommandExecutor(
12 | ISqlWriter sqlWriter,
13 | ISqlConnectionFactory sqlConnectionFactory,
14 | ISqlCommandFactory sqlCommandFactory)
15 | {
16 | _sqlWriter = sqlWriter ?? throw new ArgumentNullException(nameof(sqlWriter));
17 | _sqlConnectionFactory = sqlConnectionFactory ?? throw new ArgumentNullException(nameof(sqlConnectionFactory));
18 | _sqlCommandFactory = sqlCommandFactory ?? throw new ArgumentNullException(nameof(sqlCommandFactory));
19 | }
20 |
21 | public void Execute()
22 | {
23 | try
24 | {
25 | using (var conn = _sqlConnectionFactory.Create())
26 | {
27 | var sql = _sqlWriter.GetSql();
28 | using (var cmd = _sqlCommandFactory.CreateCommand(sql, conn))
29 | {
30 | conn.Open();
31 | cmd.ExecuteNonQuery();
32 | }
33 | }
34 | }
35 | catch (Exception ex)
36 | {
37 | HandleException(ex);
38 | throw;
39 | }
40 | }
41 |
42 | protected abstract void HandleException(Exception ex);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlCommandFactory.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Data.SqlClient;
2 | using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Platform
5 | {
6 | internal class SqlCommandFactory : ISqlCommandFactory
7 | {
8 | public ISqlCommandWrapper CreateCommand(string cmdText, ISqlConnectionWrapper sqlConnection)
9 | {
10 | var sqlCommand = new SqlCommand(cmdText, sqlConnection.SqlConnection);
11 | return new SqlCommandWrapper(sqlCommand);
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlConnectionFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Data.SqlClient;
3 | using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Platform
6 | {
7 | internal class SqlConnectionFactory : ISqlConnectionFactory
8 | {
9 | private readonly string _connectionString;
10 | private readonly ISqlConnectionStringBuilderWrapper _sqlConnectionStringBuilderWrapper;
11 | private readonly Action _connectionConfiguration;
12 |
13 | public SqlConnectionFactory(ISqlConnectionStringBuilderWrapper sqlConnectionStringBuilderWrapper,
14 | Action connectionConfiguration = null)
15 | {
16 | _sqlConnectionStringBuilderWrapper = sqlConnectionStringBuilderWrapper
17 | ?? throw new ArgumentNullException(nameof(sqlConnectionStringBuilderWrapper));
18 |
19 | _connectionString = _sqlConnectionStringBuilderWrapper.ConnectionString;
20 | _connectionConfiguration = connectionConfiguration;
21 | }
22 |
23 | public ISqlConnectionWrapper Create()
24 | {
25 | return new SqlConnectionWrapper(_connectionString, _connectionConfiguration);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlCreateDatabaseWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using static System.FormattableString;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Platform
6 | {
7 | internal class SqlCreateDatabaseWriter : ISqlCreateDatabaseWriter
8 | {
9 | private readonly string _databaseName;
10 |
11 | public SqlCreateDatabaseWriter(string databaseName)
12 | {
13 | _databaseName = databaseName ?? throw new ArgumentNullException(nameof(databaseName));
14 | }
15 |
16 | public string DatabaseName => _databaseName;
17 |
18 | public string GetSql()
19 | {
20 | var sql = new StringBuilder();
21 |
22 | sql.AppendLine(Invariant($"IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = '{_databaseName}')"));
23 | sql.AppendLine("BEGIN");
24 | sql.AppendLine(Invariant($"CREATE DATABASE [{_databaseName}]"));
25 | sql.AppendLine("END");
26 |
27 | return sql.ToString();
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlDatabaseCreator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog.Debugging;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Platform
5 | {
6 | internal class SqlDatabaseCreator : SqlCommandExecutor
7 | {
8 | private readonly string _databaseName;
9 |
10 | public SqlDatabaseCreator(
11 | ISqlCreateDatabaseWriter sqlCreateDatabaseWriter,
12 | ISqlConnectionFactory sqlConnectionFactory,
13 | ISqlCommandFactory sqlCommandFactory) :
14 | base(sqlCreateDatabaseWriter, sqlConnectionFactory, sqlCommandFactory)
15 | {
16 | if (sqlCreateDatabaseWriter == null) throw new ArgumentNullException(nameof(sqlCreateDatabaseWriter));
17 | _databaseName = sqlCreateDatabaseWriter.DatabaseName;
18 | }
19 |
20 | protected override void HandleException(Exception ex)
21 | {
22 | SelfLog.WriteLine("Unable to create database {0} due to following error: {1}",
23 | _databaseName, ex);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlTableCreator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog.Debugging;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Platform
5 | {
6 | internal class SqlTableCreator : SqlCommandExecutor
7 | {
8 | private readonly string _tableName;
9 |
10 | public SqlTableCreator(
11 | ISqlCreateTableWriter sqlCreateTableWriter,
12 | ISqlConnectionFactory sqlConnectionFactory,
13 | ISqlCommandFactory sqlCommandFactory) : base(sqlCreateTableWriter, sqlConnectionFactory, sqlCommandFactory)
14 | {
15 | if (sqlCreateTableWriter == null) throw new ArgumentNullException(nameof(sqlCreateTableWriter));
16 | _tableName = sqlCreateTableWriter.TableName;
17 | }
18 |
19 | protected override void HandleException(Exception ex)
20 | {
21 | SelfLog.WriteLine("Unable to create database table {0} due to following error: {1}",
22 | _tableName, ex);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/StandardColumn.cs:
--------------------------------------------------------------------------------
1 | namespace Serilog.Sinks.MSSqlServer
2 | {
3 | ///
4 | /// List of columns that are available to be written to the database, excluding Id and additional columns.
5 | ///
6 | public enum StandardColumn
7 | {
8 | ///
9 | /// The optional primary key
10 | ///
11 | Id,
12 |
13 | ///
14 | /// The message rendered with the template given the properties associated with the event.
15 | ///
16 | Message,
17 |
18 | ///
19 | /// The message template describing the event.
20 | ///
21 | MessageTemplate,
22 |
23 | ///
24 | /// The level of the event.
25 | ///
26 | Level,
27 |
28 | ///
29 | /// The OpenTelemetry trace id of the event.
30 | ///
31 | TraceId,
32 |
33 | ///
34 | /// The OpenTelemetry span id of the event.
35 | ///
36 | SpanId,
37 |
38 | ///
39 | /// The time at which the event occurred.
40 | ///
41 | TimeStamp,
42 |
43 | ///
44 | /// An exception associated with the event, or null.
45 | ///
46 | Exception,
47 |
48 | ///
49 | /// Properties associated with the event, including those presented in .
50 | ///
51 | Properties,
52 |
53 | ///
54 | /// A log event.
55 | ///
56 | LogEvent
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/AuditSinkExtendedBenchmarks.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Data;
4 | using BenchmarkDotNet.Attributes;
5 |
6 | namespace Serilog.Sinks.MSSqlServer.PerformanceTests.Misc;
7 |
8 | [MemoryDiagnoser]
9 | public class AuditSinkExtendedBenchmarks
10 | {
11 | private const string _connectionString = @"Data Source=(localdb)\MSSQLLocalDB;Database=LogAuditExtPerfTest;Integrated Security=SSPI;Encrypt=False;";
12 | private const string _schemaName = "dbo";
13 | private const string _tableName = "LogEvents";
14 | private ILogger _log = null!;
15 | private DateTimeOffset _additionalColumn7;
16 |
17 |
18 | [Params("String One", "String Two")]
19 | public string AdditionalColumn1 { get; set; }
20 |
21 | [Params(1, 2)]
22 | public int AdditionalColumn2 { get; set; }
23 |
24 |
25 | [GlobalSetup]
26 | public void Setup()
27 | {
28 | var options = new ColumnOptions
29 | {
30 | AdditionalColumns = new List
31 | {
32 | new() { DataType = SqlDbType.NVarChar, ColumnName = "AdditionalColumn1", DataLength = 40 },
33 | new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn2" },
34 | new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn3" },
35 | new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn4" },
36 | new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn5" },
37 | new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn6" },
38 | new() { DataType = SqlDbType.DateTimeOffset, ColumnName = "AdditionalColumn7" }
39 | }
40 | };
41 | options.Store.Add(StandardColumn.LogEvent);
42 | _log = new LoggerConfiguration()
43 | .AuditTo.MSSqlServer(_connectionString,
44 | sinkOptions: new MSSqlServerSinkOptions
45 | {
46 | TableName = _tableName,
47 | SchemaName = _schemaName,
48 | AutoCreateSqlTable = true,
49 | AutoCreateSqlDatabase = true
50 | },
51 | appConfiguration: null,
52 | restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Verbose,
53 | formatProvider: null,
54 | columnOptions: options,
55 | columnOptionsSection: null)
56 | .CreateLogger();
57 |
58 | _additionalColumn7 = new DateTimeOffset(2024, 01, 01, 00, 00, 00, TimeSpan.FromHours(1));
59 | }
60 |
61 | [Benchmark]
62 | public void EmitComplexLogEvent()
63 | {
64 | _log.Information("Hello, {AdditionalColumn1} {AdditionalColumn2} {AdditionalColumn3} {AdditionalColumn4} {AdditionalColumn5} {AdditionalColumn6} {AdditionalColumn7}!",
65 | AdditionalColumn1, AdditionalColumn2, 3, 4, 5, 6, _additionalColumn7);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/AuditSinkQuickBenchmarks.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.PerformanceTests.Misc;
4 |
5 | [MemoryDiagnoser]
6 | public class AuditSinkQuickBenchmarks
7 | {
8 | private const string _connectionString = @"Data Source=(localdb)\MSSQLLocalDB;Database=LogAuditQuickPerfTest;Integrated Security=SSPI;Encrypt=False;";
9 | private const string _schemaName = "dbo";
10 | private const string _tableName = "LogEvents";
11 | private ILogger _log = null!;
12 |
13 | [GlobalSetup]
14 | public void Setup()
15 | {
16 | var options = new ColumnOptions();
17 | options.Store.Add(StandardColumn.LogEvent);
18 | _log = new LoggerConfiguration()
19 | .AuditTo.MSSqlServer(_connectionString,
20 | sinkOptions: new MSSqlServerSinkOptions
21 | {
22 | TableName = _tableName,
23 | SchemaName = _schemaName,
24 | AutoCreateSqlTable = true,
25 | AutoCreateSqlDatabase = true
26 | },
27 | appConfiguration: null,
28 | restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Verbose,
29 | formatProvider: null,
30 | columnOptions: options,
31 | columnOptionsSection: null)
32 | .CreateLogger();
33 | }
34 |
35 | [Benchmark]
36 | public void EmitLogEvent()
37 | {
38 | _log.Information("Hello, {Name}!", "World");
39 | }
40 |
41 | [Benchmark]
42 | public void IntProperties()
43 | {
44 | _log.Information("Hello, {A} {B} {C}!", 1, 2, 3);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/SinkExtendedBenchmarks.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Data;
4 | using BenchmarkDotNet.Attributes;
5 |
6 | namespace Serilog.Sinks.MSSqlServer.PerformanceTests.Misc;
7 |
8 | [MemoryDiagnoser]
9 | public class SinkExtendedBenchmarks
10 | {
11 | private const string _connectionString = @"Data Source=(localdb)\MSSQLLocalDB;Database=LogExtPerfTest;Integrated Security=SSPI;Encrypt=False;";
12 | private const string _schemaName = "dbo";
13 | private const string _tableName = "LogEvents";
14 | private ILogger _log = null!;
15 | private DateTimeOffset _additionalColumn7;
16 |
17 |
18 | [Params("String One", "String Two")]
19 | public string AdditionalColumn1 { get; set; }
20 |
21 | [Params(1, 2)]
22 | public int AdditionalColumn2 { get; set; }
23 |
24 |
25 | [GlobalSetup]
26 | public void Setup()
27 | {
28 | var options = new ColumnOptions
29 | {
30 | AdditionalColumns = new List
31 | {
32 | new() { DataType = SqlDbType.NVarChar, ColumnName = "AdditionalColumn1", DataLength = 40 },
33 | new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn2" },
34 | new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn3" },
35 | new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn4" },
36 | new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn5" },
37 | new() { DataType = SqlDbType.Int, ColumnName = "AdditionalColumn6" },
38 | new() { DataType = SqlDbType.DateTimeOffset, ColumnName = "AdditionalColumn7" }
39 | }
40 | };
41 | options.Store.Add(StandardColumn.LogEvent);
42 | _log = new LoggerConfiguration()
43 | .WriteTo.MSSqlServer(_connectionString,
44 | sinkOptions: new MSSqlServerSinkOptions
45 | {
46 | TableName = _tableName,
47 | SchemaName = _schemaName,
48 | AutoCreateSqlTable = true,
49 | AutoCreateSqlDatabase = true
50 | },
51 | appConfiguration: null,
52 | restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Verbose,
53 | formatProvider: null,
54 | columnOptions: options,
55 | columnOptionsSection: null)
56 | .CreateLogger();
57 |
58 | _additionalColumn7 = new DateTimeOffset(2024, 01, 01, 00, 00, 00, TimeSpan.FromHours(1));
59 | }
60 |
61 | [Benchmark]
62 | public void EmitComplexLogEvent()
63 | {
64 | _log.Information("Hello, {AdditionalColumn1} {AdditionalColumn2} {AdditionalColumn3} {AdditionalColumn4} {AdditionalColumn5} {AdditionalColumn6} {AdditionalColumn7}!",
65 | AdditionalColumn1, AdditionalColumn2,3, 4, 5, 6, _additionalColumn7);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Misc/SinkQuickBenchmarks.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.PerformanceTests.Misc;
4 |
5 | [MemoryDiagnoser]
6 | public class SinkQuickBenchmarks
7 | {
8 | private const string _connectionString = @"Data Source=(localdb)\MSSQLLocalDB;Database=LogQuickPerfTest;Integrated Security=SSPI;Encrypt=False;";
9 | private const string _schemaName = "dbo";
10 | private const string _tableName = "LogEvents";
11 | private ILogger _log = null!;
12 |
13 | [GlobalSetup]
14 | public void Setup()
15 | {
16 | var options = new ColumnOptions();
17 | options.Store.Add(StandardColumn.LogEvent);
18 | _log = new LoggerConfiguration()
19 | .WriteTo.MSSqlServer(_connectionString,
20 | sinkOptions: new MSSqlServerSinkOptions
21 | {
22 | TableName = _tableName,
23 | SchemaName = _schemaName,
24 | AutoCreateSqlTable = true,
25 | AutoCreateSqlDatabase = true
26 | },
27 | appConfiguration: null,
28 | restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Verbose,
29 | formatProvider: null,
30 | columnOptions: options,
31 | columnOptionsSection: null)
32 | .CreateLogger();
33 | }
34 |
35 | [Benchmark]
36 | public void EmitLogEvent()
37 | {
38 | _log.Information("Hello, {Name}!", "World");
39 | }
40 |
41 | [Benchmark]
42 | public void IntProperties()
43 | {
44 | _log.Information("Hello, {A} {B} {C}!", 1, 2, 3);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Program.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Running;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.PerformanceTests;
4 |
5 | public static class Program
6 | {
7 | public static void Main(string[] args)
8 | {
9 | BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Serilog.Sinks.MSSqlServer.PerformanceTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | true
6 | Serilog.Sinks.MSSqlServer.PerformanceTests
7 | Exe
8 | ../../assets/Serilog.snk
9 | true
10 | AnyCPU
11 | 6.0-recommended
12 | True
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlBulkBatchWriterBenchmarks.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Data;
4 | using System.Threading.Tasks;
5 | using BenchmarkDotNet.Attributes;
6 | using Moq;
7 | using Serilog.Events;
8 | using Serilog.Parsing;
9 | using Serilog.Sinks.MSSqlServer.Output;
10 | using Serilog.Sinks.MSSqlServer.Platform;
11 | using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
12 |
13 | namespace Serilog.Sinks.MSSqlServer.PerformanceTests.Platform;
14 |
15 | [MemoryDiagnoser]
16 | [MaxIterationCount(16)]
17 | public class SqlBulkBatchWriterBenchmarks : IDisposable
18 | {
19 | private const string _tableName = "TestTableName";
20 | private const string _schemaName = "TestSchemaName";
21 | private readonly DataTable _dataTable = new(_tableName);
22 | private Mock _dataTableCreatorMock;
23 | private Mock _sqlConnectionFactoryMock;
24 | private Mock _logEventDataGeneratorMock;
25 | private Mock _sqlConnectionWrapperMock;
26 | private Mock _sqlBulkCopyWrapper;
27 | private List _logEvents;
28 | private SqlBulkBatchWriter _sut;
29 |
30 | [GlobalSetup]
31 | public void Setup()
32 | {
33 | _dataTableCreatorMock = new Mock();
34 | _sqlConnectionFactoryMock = new Mock();
35 | _logEventDataGeneratorMock = new Mock();
36 | _sqlConnectionWrapperMock = new Mock();
37 | _sqlBulkCopyWrapper = new Mock();
38 |
39 | _dataTableCreatorMock.Setup(d => d.CreateDataTable()).Returns(_dataTable);
40 |
41 | _sqlConnectionFactoryMock.Setup(f => f.Create()).Returns(_sqlConnectionWrapperMock.Object);
42 | _sqlConnectionWrapperMock.Setup(c => c.CreateSqlBulkCopy(It.IsAny(), It.IsAny()))
43 | .Returns(_sqlBulkCopyWrapper.Object);
44 |
45 | CreateLogEvents();
46 |
47 | _sut = new SqlBulkBatchWriter(_tableName, _schemaName, false,
48 | _dataTableCreatorMock.Object, _sqlConnectionFactoryMock.Object, _logEventDataGeneratorMock.Object);
49 | }
50 |
51 | [Benchmark]
52 | public async Task WriteBatch()
53 | {
54 | await _sut.WriteBatch(_logEvents);
55 | }
56 |
57 | private static LogEvent CreateLogEvent()
58 | {
59 | return new LogEvent(
60 | new DateTimeOffset(2020, 1, 1, 0, 0, 0, 0, TimeSpan.Zero),
61 | LogEventLevel.Debug, null, new MessageTemplate(new List()),
62 | new List());
63 | }
64 |
65 | private void CreateLogEvents()
66 | {
67 | _logEvents = new List();
68 | var eventCount = 500_000;
69 | while (eventCount-- > 0)
70 | {
71 | _logEvents.Add(CreateLogEvent());
72 | }
73 | }
74 |
75 | public void Dispose()
76 | {
77 | GC.SuppressFinalize(this);
78 | _sut.Dispose();
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.PerformanceTests/Sinks/MSSqlServer/Platform/SqlInsertStatementWriterBenchmarks.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using BenchmarkDotNet.Attributes;
5 | using Moq;
6 | using Serilog.Events;
7 | using Serilog.Parsing;
8 | using Serilog.Sinks.MSSqlServer.Output;
9 | using Serilog.Sinks.MSSqlServer.Platform;
10 | using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
11 |
12 | namespace Serilog.Sinks.MSSqlServer.PerformanceTests.Platform;
13 |
14 | [MemoryDiagnoser]
15 | [MaxIterationCount(16)]
16 | public class SqlInsertStatementWriterBenchmarks
17 | {
18 | private const string _tableName = "TestTableName";
19 | private const string _schemaName = "TestSchemaName";
20 | private Mock _sqlConnectionFactoryMock;
21 | private Mock _sqlCommandFactoryMock;
22 | private Mock _logEventDataGeneratorMock;
23 | private Mock _sqlConnectionWrapperMock;
24 | private Mock _sqlCommandWrapperMock;
25 | private List _logEvents;
26 | private SqlInsertStatementWriter _sut;
27 |
28 | [GlobalSetup]
29 | public void Setup()
30 | {
31 | _sqlConnectionFactoryMock = new Mock();
32 | _sqlCommandFactoryMock = new Mock();
33 | _logEventDataGeneratorMock = new Mock();
34 | _sqlConnectionWrapperMock = new Mock();
35 | _sqlCommandWrapperMock = new Mock();
36 |
37 | _sqlConnectionFactoryMock.Setup(f => f.Create()).Returns(_sqlConnectionWrapperMock.Object);
38 | _sqlCommandFactoryMock.Setup(f => f.CreateCommand(It.IsAny(), It.IsAny()))
39 | .Returns(_sqlCommandWrapperMock.Object);
40 |
41 | CreateLogEvents();
42 |
43 | _sut = new SqlInsertStatementWriter(_tableName, _schemaName,
44 | _sqlConnectionFactoryMock.Object, _sqlCommandFactoryMock.Object, _logEventDataGeneratorMock.Object);
45 | }
46 |
47 | [Benchmark]
48 | public async Task WriteEvents()
49 | {
50 | await _sut.WriteEvents(_logEvents);
51 | }
52 |
53 | private static LogEvent CreateLogEvent()
54 | {
55 | return new LogEvent(
56 | new DateTimeOffset(2020, 1, 1, 0, 0, 0, 0, TimeSpan.Zero),
57 | LogEventLevel.Debug, null, new MessageTemplate(new List()),
58 | new List());
59 | }
60 |
61 | private void CreateLogEvents()
62 | {
63 | _logEvents = new List();
64 | var eventCount = 200_000;
65 | while (eventCount-- > 0)
66 | {
67 | _logEvents.Add(CreateLogEvent());
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Factories/MSSqlServerAuditSinkFactoryTests.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.MSSqlServer.Configuration.Factories;
2 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
3 | using Xunit;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Tests.Configuration.Factories
6 | {
7 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
8 | public class MSSqlServerAuditSinkFactoryTests
9 | {
10 | [Fact]
11 | public void MSSqlServerAuditSinkFactoryCreateReturnsInstance()
12 | {
13 | // Arrange
14 | var sinkOptions = new MSSqlServerSinkOptions { TableName = "TestTableName" };
15 | var sut = new MSSqlServerAuditSinkFactory();
16 |
17 | // Act
18 | var result = sut.Create(DatabaseFixture.LogEventsConnectionString, sinkOptions, null, new MSSqlServer.ColumnOptions(), null);
19 |
20 | // Assert
21 | Assert.IsType(result);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Factories/MSSqlServerSinkFactoryTests.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.MSSqlServer.Configuration.Factories;
2 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
3 | using Xunit;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Tests.Configuration.Factories
6 | {
7 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
8 | public class MSSqlServerSinkFactoryTests
9 | {
10 | [Fact]
11 | public void MSSqlServerSinkFactoryCreateReturnsInstance()
12 | {
13 | // Arrange
14 | var sinkOptions = new MSSqlServerSinkOptions { TableName = "TestTableName" };
15 | var sut = new MSSqlServerSinkFactory();
16 |
17 | // Act
18 | var result = sut.Create(DatabaseFixture.LogEventsConnectionString, sinkOptions, null, new MSSqlServer.ColumnOptions(), null);
19 |
20 | // Assert
21 | Assert.IsType(result);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Factories/PeriodicBatchingSinkFactoryTests.cs:
--------------------------------------------------------------------------------
1 | using Moq;
2 | using Serilog.Sinks.MSSqlServer.Configuration.Factories;
3 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
4 | using Serilog.Core;
5 | using Xunit;
6 |
7 | namespace Serilog.Sinks.MSSqlServer.Tests.Configuration.Factories
8 | {
9 | // BatchingSink is not public
10 | // temporarily removing this test
11 |
12 | //[Trait(TestCategory.TraitName, TestCategory.Unit)]
13 | //public class PeriodicBatchingSinkFactoryTests
14 | //{
15 | // [Fact]
16 | // public void PeriodicBatchingSinkFactoryCreateReturnsInstance()
17 | // {
18 | // // Arrange
19 | // var sinkMock = new Mock();
20 | // var sut = new PeriodicBatchingSinkFactory();
21 |
22 | // // Act
23 | // var result = sut.Create(sinkMock.Object, new MSSqlServerSinkOptions());
24 |
25 | // // Assert
26 | // Assert.IsType(result);
27 | // }
28 | //}
29 | }
30 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Implementations/Microsoft.Extensions.Configuration/MicrosoftExtensionsConnectionStringProviderTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using Moq;
3 | using Serilog.Sinks.MSSqlServer.Configuration;
4 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
5 | using Xunit;
6 |
7 | namespace Serilog.Sinks.MSSqlServer.Tests.Configuration.Implementations.Microsoft.Extensions.Configuration
8 | {
9 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
10 | public class MicrosoftExtensionsConnectionStringProviderTests
11 | {
12 | [Fact]
13 | public void GetConnectionStringCalledWithConnectionStringReturnsSameValue()
14 | {
15 | // Arrange
16 | const string connectionString = "Server=localhost;Database=LogTest;Integrated Security=SSPI;Encrypt=False;";
17 | var configurationMock = new Mock();
18 | var sut = new MicrosoftExtensionsConnectionStringProvider();
19 |
20 | // Act
21 | var result = sut.GetConnectionString(connectionString, configurationMock.Object);
22 |
23 | // Assert
24 | Assert.Equal(connectionString, result);
25 | }
26 |
27 | [Fact]
28 | public void GetConnectionStringCalledWithNameItGetsConnectionStringFromConfig()
29 | {
30 | // Arrange
31 | const string connectionStringName = "LogDatabase";
32 | const string connectionString = "Server=localhost;Database=LogTest;Integrated Security=SSPI;Encrypt=False;";
33 | var configurationMock = new Mock();
34 | var configSectionMock = new Mock();
35 | configurationMock.Setup(c => c.GetSection(It.IsAny())).Returns(configSectionMock.Object);
36 | configSectionMock.Setup(c => c[It.IsAny()]).Returns(connectionString);
37 | var sut = new MicrosoftExtensionsConnectionStringProvider();
38 |
39 | // Act
40 | var result = sut.GetConnectionString(connectionStringName, configurationMock.Object);
41 |
42 | // Assert
43 | configurationMock.Verify(c => c.GetSection("ConnectionStrings"), Times.Once);
44 | configSectionMock.Verify(c => c[connectionStringName], Times.Once);
45 | Assert.Equal(connectionString, result);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Implementations/System.Configuration/ApplySystemConfigurationTests.cs:
--------------------------------------------------------------------------------
1 | using Moq;
2 | using Serilog.Configuration;
3 | using Serilog.Sinks.MSSqlServer.Configuration;
4 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
5 | using Xunit;
6 |
7 | namespace Serilog.Sinks.MSSqlServer.Tests.Configuration.Implementations.System.Configuration
8 | {
9 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
10 | public class ApplySystemConfigurationTests
11 | {
12 | [Fact]
13 | public void GetConfigurationStringCallsAttachedConfigurationStringProvider()
14 | {
15 | // Arrange
16 | const string connectionStringName = "TestConnectionStringName";
17 | const string expectedResult = "TestConnectionString";
18 | var connectionStringProviderMock = new Mock();
19 | connectionStringProviderMock.Setup(p => p.GetConnectionString(It.IsAny())).Returns(expectedResult);
20 | var sut = new ApplySystemConfiguration(connectionStringProviderMock.Object, null, null);
21 |
22 | // Act
23 | var result = sut.GetConnectionString(connectionStringName);
24 |
25 | // Assert
26 | connectionStringProviderMock.Verify(p => p.GetConnectionString(connectionStringName), Times.Once);
27 | Assert.Equal(expectedResult, result);
28 | }
29 |
30 | [Fact]
31 | public void ConfigureColumnOptionsCallsAttachedColumnOptionsProvider()
32 | {
33 | // Arrange
34 | var inputConfigSection = new MSSqlServerConfigurationSection();
35 | var inputColumnOptions = new Serilog.Sinks.MSSqlServer.ColumnOptions();
36 | var expectedResult = new Serilog.Sinks.MSSqlServer.ColumnOptions();
37 | var columnOptionsProviderMock = new Mock();
38 | columnOptionsProviderMock.Setup(p => p.ConfigureColumnOptions(It.IsAny(), It.IsAny()))
39 | .Returns(expectedResult);
40 | var sut = new ApplySystemConfiguration(null, columnOptionsProviderMock.Object, null);
41 |
42 | // Act
43 | var result = sut.ConfigureColumnOptions(inputConfigSection, inputColumnOptions);
44 |
45 | // Assert
46 | columnOptionsProviderMock.Verify(p => p.ConfigureColumnOptions(inputConfigSection, inputColumnOptions), Times.Once);
47 | Assert.Same(expectedResult, result);
48 | }
49 |
50 | [Fact]
51 | public void ConfigureSinkOptionsCallsAttachedSinkOptionsProvider()
52 | {
53 | // Arrange
54 | var inputConfigSection = new MSSqlServerConfigurationSection();
55 | var inputSinkOptions = new MSSqlServerSinkOptions();
56 | var expectedResult = new MSSqlServerSinkOptions();
57 | var sinkOptionsProviderMock = new Mock();
58 | sinkOptionsProviderMock.Setup(p => p.ConfigureSinkOptions(It.IsAny(), It.IsAny()))
59 | .Returns(expectedResult);
60 | var sut = new ApplySystemConfiguration(null, null, sinkOptionsProviderMock.Object);
61 |
62 | // Act
63 | var result = sut.ConfigureSinkOptions(inputConfigSection, inputSinkOptions);
64 |
65 | // Assert
66 | sinkOptionsProviderMock.Verify(p => p.ConfigureSinkOptions(inputConfigSection, inputSinkOptions), Times.Once);
67 | Assert.Same(expectedResult, result);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Implementations/System.Configuration/StandardColumnConfigSpanIdTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Configuration;
3 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
4 | using Xunit;
5 |
6 | namespace Serilog.Sinks.MSSqlServer.Tests.Configuration.Implementations.System.Configuration
7 | {
8 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
9 | public class StandardColumnConfigSpanIdTests
10 | {
11 | [Fact]
12 | public void ClassSetsColumnNameRequiredAttributeToFalse()
13 | {
14 | var sut = typeof(StandardColumnConfigSpanId);
15 | var columNameProperty = sut.GetProperty("ColumnName");
16 | var configurationPropertyAttribute = (ConfigurationPropertyAttribute) Attribute.GetCustomAttribute(columNameProperty, typeof(ConfigurationPropertyAttribute));
17 |
18 | Assert.Equal("ColumnName", configurationPropertyAttribute.Name);
19 | Assert.False(configurationPropertyAttribute.IsRequired);
20 | }
21 |
22 | [Fact]
23 | public void ClassSetsColumnNameIsKeyAttributeToTrue()
24 | {
25 | var sut = typeof(StandardColumnConfigSpanId);
26 | var columNameProperty = sut.GetProperty("ColumnName");
27 | var configurationPropertyAttribute = (ConfigurationPropertyAttribute)Attribute.GetCustomAttribute(columNameProperty, typeof(ConfigurationPropertyAttribute));
28 |
29 | Assert.Equal("ColumnName", configurationPropertyAttribute.Name);
30 | Assert.True(configurationPropertyAttribute.IsKey);
31 | }
32 |
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Implementations/System.Configuration/StandardColumnConfigTraceIdTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Configuration;
3 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
4 | using Xunit;
5 |
6 | namespace Serilog.Sinks.MSSqlServer.Tests.Configuration.Implementations.System.Configuration
7 | {
8 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
9 | public class StandardColumnConfigTraceIdTests
10 | {
11 | [Fact]
12 | public void ClassSetsColumnNameRequiredAttributeToFalse()
13 | {
14 | // Arrange + act
15 | var sut = typeof(StandardColumnConfigTraceId);
16 | var columNameProperty = sut.GetProperty("ColumnName");
17 | var configurationPropertyAttribute = (ConfigurationPropertyAttribute) Attribute.GetCustomAttribute(columNameProperty, typeof(ConfigurationPropertyAttribute));
18 |
19 | // Assert
20 | Assert.Equal("ColumnName", configurationPropertyAttribute.Name);
21 | Assert.False(configurationPropertyAttribute.IsRequired);
22 | }
23 |
24 | [Fact]
25 | public void ClassSetsColumnNameIsKeyAttributeToTrue()
26 | {
27 | // Arrange + act
28 | var sut = typeof(StandardColumnConfigTraceId);
29 | var columNameProperty = sut.GetProperty("ColumnName");
30 | var configurationPropertyAttribute = (ConfigurationPropertyAttribute)Attribute.GetCustomAttribute(columNameProperty, typeof(ConfigurationPropertyAttribute));
31 |
32 | // Assert
33 | Assert.Equal("ColumnName", configurationPropertyAttribute.Name);
34 | Assert.True(configurationPropertyAttribute.IsKey);
35 | }
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Extensions/StringExtensionsTests.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.MSSqlServer.Extensions;
2 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
3 | using Xunit;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Tests.Extensions
6 | {
7 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
8 | public class StringExtensionsTests
9 | {
10 | [Fact]
11 | public void ReturnNullWhenInputStringIsNull()
12 | {
13 | // Arrange
14 | string inputMessage = null;
15 |
16 | // Act
17 | var nonTruncatedMessage = inputMessage.Truncate(5, "...");
18 |
19 | // Assert
20 | Assert.Null(nonTruncatedMessage);
21 | }
22 |
23 | [Fact]
24 | public void ReturnEmptyWhenInputStringIsEmpty()
25 | {
26 | // Arrange
27 | var inputMessage = "";
28 |
29 | // Act
30 | var nonTruncatedMessage = inputMessage.Truncate(5, "...");
31 |
32 | // Assert
33 | Assert.Equal(inputMessage, nonTruncatedMessage);
34 | }
35 |
36 | [Theory]
37 | [InlineData(0)]
38 | [InlineData(-1)]
39 | [InlineData(-5)]
40 | public void ReturnEmptyStringWhenRequestedMaxValueIsZeroOrSmaller(int maxValue)
41 | {
42 | // Arrange
43 | var inputMessage = "A simple test message";
44 |
45 | // Act
46 | var nonTruncatedMessage = inputMessage.Truncate(maxValue, "...");
47 |
48 | // Assert
49 | Assert.Equal("", nonTruncatedMessage);
50 | }
51 |
52 | [Fact]
53 | public void ReturnTruncatedStringWithSuffix()
54 | {
55 | // Arrange
56 | var inputMessage = "A simple test message";
57 |
58 | // Act
59 | var truncatedMessage = inputMessage.Truncate(15, "...");
60 |
61 | // Assert
62 | Assert.Equal("A simple tes...", truncatedMessage);
63 | }
64 |
65 | [Theory]
66 | [InlineData("Abc")]
67 | [InlineData("Ab")]
68 | [InlineData("X")]
69 | [Trait("Bugfix", "#505")]
70 | public void ReturnNonTruncatedShortStringWhenMaxLengthIsLessOrEqualToSuffixLength(string inputMessage)
71 | {
72 | // Act
73 | var nonTruncatedMessage = inputMessage.Truncate(3, "...");
74 |
75 | // Assert
76 | Assert.Equal(inputMessage, nonTruncatedMessage);
77 | }
78 |
79 | [Fact]
80 | public void ReturnTruncatedStringWithEmptySuffix()
81 | {
82 | // Arrange
83 | var inputMessage = "A simple test message";
84 |
85 | // Act
86 | var truncatedMessage = inputMessage.Truncate(15, "");
87 |
88 | // Assert
89 | Assert.Equal("A simple test m", truncatedMessage);
90 | }
91 |
92 | [Fact]
93 | public void ReturnTruncatedStringWithNullSuffix()
94 | {
95 | // Arrange
96 | var inputMessage = "A simple test message";
97 |
98 | // Act
99 | var truncatedMessage = inputMessage.Truncate(15, null);
100 |
101 | // Assert
102 | Assert.Equal("A simple test m", truncatedMessage);
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 | // This file is used by Code Analysis to maintain SuppressMessage
2 | // attributes that are applied to this project.
3 | // Project-level suppressions either have no target or are given
4 | // a specific target and scoped to a namespace, type, member, etc.
5 |
6 | using System.Diagnostics.CodeAnalysis;
7 |
8 | [assembly: SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Supplying string literals and not using resources is accepted within this project.", Scope = "namespaceanddescendants", Target = "Serilog.Sinks.MSSqlServer.Tests")]
9 | [assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Member names must match SQL server master DB objects.", Scope = "type", Target = "~T:Serilog.Sinks.MSSqlServer.Tests.TestUtils.sp_pkey")]
10 | [assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Member names must match SQL server master DB objects.", Scope = "type", Target = "~T:Serilog.Sinks.MSSqlServer.Tests.TestUtils.SysIndex_CCI")]
11 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Misc/OpenTelemetryColumnsTests.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Globalization;
3 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
4 | using Xunit;
5 | using Xunit.Abstractions;
6 |
7 | namespace Serilog.Sinks.MSSqlServer.Tests.Misc
8 | {
9 | [Trait(TestCategory.TraitName, TestCategory.Integration)]
10 | public class OpenTelemetryColumnsTests : DatabaseTestsBase
11 | {
12 | public OpenTelemetryColumnsTests(ITestOutputHelper output) : base(output)
13 | {
14 | }
15 |
16 | [Fact]
17 | public void OpenTelemetryActivityTraceIdAndSpanIdAreStoredInColumns()
18 | {
19 | // Arrange
20 | var expectedTraceId = string.Empty;
21 | var expectedSpanId = string.Empty;
22 | var columnOptions = new MSSqlServer.ColumnOptions();
23 | columnOptions.Store.Add(StandardColumn.TraceId);
24 | columnOptions.Store.Add(StandardColumn.SpanId);
25 |
26 | Log.Logger = new LoggerConfiguration()
27 | .WriteTo.MSSqlServer
28 | (
29 | connectionString: DatabaseFixture.LogEventsConnectionString,
30 | new MSSqlServerSinkOptions
31 | {
32 | TableName = DatabaseFixture.LogTableName,
33 | AutoCreateSqlTable = true
34 | },
35 | columnOptions: columnOptions,
36 | formatProvider: CultureInfo.InvariantCulture
37 | )
38 | .CreateLogger();
39 |
40 | // Act
41 | using (var testActivity = new Activity("OpenTelemetryColumnsTests"))
42 | {
43 | testActivity.SetIdFormat(ActivityIdFormat.W3C);
44 | testActivity.Start();
45 | expectedTraceId = testActivity.TraceId.ToString();
46 | expectedSpanId = testActivity.SpanId.ToString();
47 |
48 |
49 | Log.Logger.Information("Logging message");
50 | Log.CloseAndFlush();
51 | }
52 |
53 | // Assert
54 | VerifyStringColumnWritten("TraceId", expectedTraceId);
55 | VerifyStringColumnWritten("SpanId", expectedSpanId);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Misc/PropertiesColumnFilteringTests.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using FluentAssertions;
3 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
4 | using Xunit;
5 | using Xunit.Abstractions;
6 |
7 | namespace Serilog.Sinks.MSSqlServer.Tests.Misc
8 | {
9 | [Trait(TestCategory.TraitName, TestCategory.Integration)]
10 | public class PropertiesColumnFilteringTests : DatabaseTestsBase
11 | {
12 | public PropertiesColumnFilteringTests(ITestOutputHelper output) : base(output)
13 | {
14 | }
15 |
16 | [Fact]
17 | public void FilteredProperties()
18 | {
19 | // Arrange
20 | var columnOptions = new MSSqlServer.ColumnOptions();
21 | columnOptions.Properties.PropertiesFilter = (propName) => propName == "A";
22 |
23 | Log.Logger = new LoggerConfiguration()
24 | .WriteTo.MSSqlServer
25 | (
26 | connectionString: DatabaseFixture.LogEventsConnectionString,
27 | new MSSqlServerSinkOptions
28 | {
29 | TableName = DatabaseFixture.LogTableName,
30 | AutoCreateSqlTable = true,
31 | },
32 | columnOptions: columnOptions,
33 | formatProvider: CultureInfo.InvariantCulture
34 | )
35 | .CreateLogger();
36 |
37 | // Act
38 | Log.Logger
39 | .ForContext("A", "AValue")
40 | .ForContext("B", "BValue")
41 | .Information("Logging message");
42 |
43 | Log.CloseAndFlush();
44 |
45 | // Assert
46 | VerifyCustomQuery($"SELECT Properties from {DatabaseFixture.LogTableName}",
47 | e => e.Should().Contain(l => l.Properties.Contains("AValue")));
48 | VerifyCustomQuery($"SELECT Properties from {DatabaseFixture.LogTableName}",
49 | e => e.Should().NotContain(l => l.Properties.Contains("BValue")));
50 | }
51 |
52 | [Fact]
53 | public void FilteredPropertiesWhenAuditing()
54 | {
55 | // Arrange
56 | var columnOptions = new MSSqlServer.ColumnOptions();
57 | columnOptions.Properties.PropertiesFilter = (propName) => propName == "A";
58 |
59 | Log.Logger = new LoggerConfiguration()
60 | .AuditTo.MSSqlServer
61 | (
62 | connectionString: DatabaseFixture.LogEventsConnectionString,
63 | new MSSqlServerSinkOptions
64 | {
65 | TableName = DatabaseFixture.LogTableName,
66 | AutoCreateSqlTable = true
67 | },
68 | columnOptions: columnOptions,
69 | formatProvider: CultureInfo.InvariantCulture
70 | )
71 | .CreateLogger();
72 |
73 | // Act
74 | Log.Logger
75 | .ForContext("A", "AValue")
76 | .ForContext("B", "BValue")
77 | .Information("Logging message");
78 |
79 | Log.CloseAndFlush();
80 |
81 | // Assert
82 | VerifyCustomQuery($"SELECT Properties from {DatabaseFixture.LogTableName}",
83 | e => e.Should().Contain(l => l.Properties.Contains("AValue")));
84 | VerifyCustomQuery($"SELECT Properties from {DatabaseFixture.LogTableName}",
85 | e => e.Should().NotContain(l => l.Properties.Contains("BValue")));
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Misc/SqlBulkCopyTests.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
3 | using Xunit;
4 | using Xunit.Abstractions;
5 |
6 | namespace Serilog.Sinks.MSSqlServer.Tests.Misc
7 | {
8 | [Trait(TestCategory.TraitName, TestCategory.Integration)]
9 | public class SqlBulkCopyTests : DatabaseTestsBase
10 | {
11 | public SqlBulkCopyTests(ITestOutputHelper output) : base(output)
12 | {
13 | }
14 |
15 | [Fact]
16 | public void UseSqlBulkCopySetToTrue()
17 | {
18 | // Arrange
19 | Log.Logger = new LoggerConfiguration()
20 | .WriteTo.MSSqlServer
21 | (
22 | connectionString: DatabaseFixture.LogEventsConnectionString,
23 | new MSSqlServerSinkOptions
24 | {
25 | TableName = DatabaseFixture.LogTableName,
26 | AutoCreateSqlTable = true,
27 | UseSqlBulkCopy = true
28 | },
29 | formatProvider: CultureInfo.InvariantCulture
30 | )
31 | .CreateLogger();
32 |
33 | // Act
34 | Log.Logger.Information("Logging message 1");
35 | Log.Logger.Information("Logging message 2");
36 | Log.CloseAndFlush();
37 |
38 | // Assert
39 | VerifyLogMessageWasWritten("Logging message 1");
40 | VerifyLogMessageWasWritten("Logging message 2");
41 | }
42 |
43 | [Fact]
44 | public void UseSqlBulkCopySetToFalse()
45 | {
46 | // Arrange
47 | Log.Logger = new LoggerConfiguration()
48 | .WriteTo.MSSqlServer
49 | (
50 | connectionString: DatabaseFixture.LogEventsConnectionString,
51 | new MSSqlServerSinkOptions
52 | {
53 | TableName = DatabaseFixture.LogTableName,
54 | AutoCreateSqlTable = true,
55 | UseSqlBulkCopy = false
56 | },
57 | formatProvider: CultureInfo.InvariantCulture
58 | )
59 | .CreateLogger();
60 |
61 | // Act
62 | Log.Logger.Information("Logging message 1");
63 | Log.Logger.Information("Logging message 2");
64 | Log.CloseAndFlush();
65 |
66 | // Assert
67 | VerifyLogMessageWasWritten("Logging message 1");
68 | VerifyLogMessageWasWritten("Logging message 2");
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Misc/StructuredSubType.cs:
--------------------------------------------------------------------------------
1 | namespace Serilog.Sinks.MSSqlServer.Tests.Misc
2 | {
3 | internal class StructuredSubType
4 | {
5 | public int SubSubProperty1 { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Misc/StructuredType.cs:
--------------------------------------------------------------------------------
1 | namespace Serilog.Sinks.MSSqlServer.Tests.Misc
2 | {
3 | internal class StructuredType
4 | {
5 | public string SubProperty1 { get; set; }
6 | public StructuredSubType SubProperty2 { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Misc/TransactionTests.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using System.Transactions;
3 | using FluentAssertions;
4 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
5 | using Xunit;
6 | using Xunit.Abstractions;
7 |
8 | namespace Serilog.Sinks.MSSqlServer.Tests.Misc
9 | {
10 | [Trait(TestCategory.TraitName, TestCategory.Integration)]
11 | public class TransactionTests : DatabaseTestsBase
12 | {
13 | public TransactionTests(ITestOutputHelper output) : base(output)
14 | {
15 | }
16 |
17 | [Fact]
18 | public void LogsAreNotAffectedByTransactionsByDefault()
19 | {
20 | // Arrange
21 | Log.Logger = new LoggerConfiguration()
22 | .WriteTo.MSSqlServer
23 | (
24 | connectionString: DatabaseFixture.LogEventsConnectionString,
25 | new MSSqlServerSinkOptions
26 | {
27 | TableName = DatabaseFixture.LogTableName,
28 | AutoCreateSqlTable = true
29 | },
30 | formatProvider: CultureInfo.InvariantCulture
31 | )
32 | .CreateLogger();
33 |
34 | using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
35 | {
36 | // Act
37 | Log.Logger.Information("Logging message");
38 |
39 | // Flush message so it is written on foreground thread instead of timer
40 | // So we can test if it is affected by transaction
41 | Log.CloseAndFlush();
42 | }
43 |
44 | // Assert after rollback, the message should still be persisted
45 | VerifyCustomQuery($"SELECT Id from {DatabaseFixture.LogTableName}",
46 | e => e.Should().HaveCount(1));
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Serilog.Sinks.MSSqlServer.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net462;net472;net8.0
5 | true
6 | Serilog.Sinks.MSSqlServer.Tests
7 | ../../assets/Serilog.snk
8 | true
9 | true
10 | Serilog.Sinks.MSSqlServer.Tests
11 | true
12 | true
13 | AnyCPU
14 | 6.0-recommended
15 | True
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | runtime; build; native; contentfiles; analyzers; buildtransitive
50 | all
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | Always
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/ColumnOptions/ColumnOptionsTests.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
2 | using Xunit;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Tests.ColumnOptions
5 | {
6 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
7 | public class ColumnOptionsTests
8 | {
9 | [Fact]
10 | public void GetStandardColumnOptionsReturnsTraceIdOptions()
11 | {
12 | // Arrange
13 | var sut = new MSSqlServer.ColumnOptions();
14 |
15 | // Act
16 | var result = sut.GetStandardColumnOptions(StandardColumn.TraceId);
17 |
18 | // Assert
19 | Assert.Same(sut.TraceId, result);
20 | }
21 |
22 | [Fact]
23 | public void GetStandardColumnOptionsReturnsSpanIdOptions()
24 | {
25 | // Arrange
26 | var sut = new MSSqlServer.ColumnOptions();
27 |
28 | // Act
29 | var result = sut.GetStandardColumnOptions(StandardColumn.SpanId);
30 |
31 | // Assert
32 | Assert.Same(sut.SpanId, result);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/ColumnOptions/SpanIdColumnOptionsTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
4 | using Xunit;
5 |
6 | namespace Serilog.Sinks.MSSqlServer.Tests.ColumnOptions
7 | {
8 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
9 | public class SpanIdColumnOptionsTests
10 | {
11 | [Fact]
12 | public void CanSetDataTypeNVarChar()
13 | {
14 | // Arrange
15 | var options = new MSSqlServer.ColumnOptions();
16 |
17 | // Act - should not throw
18 | options.SpanId.DataType = SqlDbType.NVarChar;
19 | }
20 |
21 | [Fact]
22 | public void CanSetDataTypeVarChar()
23 | {
24 | // Arrange
25 | var options = new MSSqlServer.ColumnOptions();
26 |
27 | // Act - should not throw
28 | options.SpanId.DataType = SqlDbType.VarChar;
29 | }
30 |
31 | [Fact]
32 | public void CannotSetDataTypeBigInt()
33 | {
34 | // Arrange
35 | var options = new MSSqlServer.ColumnOptions();
36 |
37 | // Act and assert - should throw
38 | Assert.Throws(() => options.SpanId.DataType = SqlDbType.BigInt);
39 | }
40 |
41 | [Fact]
42 | public void CannotSetDataTypeNChar()
43 | {
44 | // Arrange
45 | var options = new MSSqlServer.ColumnOptions();
46 |
47 | // Act and assert - should throw
48 | Assert.Throws(() => options.SpanId.DataType = SqlDbType.NChar);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/ColumnOptions/TimeStampColumnOptionsTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
4 | using Xunit;
5 |
6 | namespace Serilog.Sinks.MSSqlServer.Tests.ColumnOptions
7 | {
8 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
9 | public class TimeStampColumnOptionsTests
10 | {
11 | [Trait("Bugfix", "#187")]
12 | [Fact]
13 | public void CanSetDataTypeDateTime()
14 | {
15 | // Arrange
16 | var options = new Serilog.Sinks.MSSqlServer.ColumnOptions();
17 |
18 | // Act - should not throw
19 | options.TimeStamp.DataType = SqlDbType.DateTime;
20 | }
21 |
22 | [Trait("Bugfix", "#187")]
23 | [Fact]
24 | public void CanSetDataTypeDateTimeOffset()
25 | {
26 | // Arrange
27 | var options = new Serilog.Sinks.MSSqlServer.ColumnOptions();
28 |
29 | // Act - should not throw
30 | options.TimeStamp.DataType = SqlDbType.DateTimeOffset;
31 | }
32 |
33 | [Trait("Bugfix", "#187")]
34 | [Fact]
35 | public void CannotSetDataTypeNVarChar()
36 | {
37 | // Arrange
38 | var options = new Serilog.Sinks.MSSqlServer.ColumnOptions();
39 |
40 | // Act and assert - should throw
41 | Assert.Throws(() => options.TimeStamp.DataType = SqlDbType.NVarChar);
42 | }
43 |
44 | [Trait("Feature", "#300")]
45 | [Fact]
46 | public void CanSetDataTypeDateTime2()
47 | {
48 | // Arrange
49 | var options = new Serilog.Sinks.MSSqlServer.ColumnOptions();
50 |
51 | // Act - should not throw
52 | options.TimeStamp.DataType = SqlDbType.DateTime2;
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/ColumnOptions/TraceIdColumnOptionsTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
4 | using Xunit;
5 |
6 | namespace Serilog.Sinks.MSSqlServer.Tests.ColumnOptions
7 | {
8 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
9 | public class TraceIdColumnOptionsTests
10 | {
11 | [Fact]
12 | public void CanSetDataTypeNVarChar()
13 | {
14 | // Arrange
15 | var options = new MSSqlServer.ColumnOptions();
16 |
17 | // Act - should not throw
18 | options.TraceId.DataType = SqlDbType.NVarChar;
19 | }
20 |
21 | [Fact]
22 | public void CanSetDataTypeVarChar()
23 | {
24 | // Arrange
25 | var options = new MSSqlServer.ColumnOptions();
26 |
27 | // Act - should not throw
28 | options.TraceId.DataType = SqlDbType.VarChar;
29 | }
30 |
31 | [Fact]
32 | public void CannotSetDataTypeBigInt()
33 | {
34 | // Arrange
35 | var options = new MSSqlServer.ColumnOptions();
36 |
37 | // Act and assert - should throw
38 | Assert.Throws(() => options.TraceId.DataType = SqlDbType.BigInt);
39 | }
40 |
41 | [Fact]
42 | public void CannotSetDataTypeNChar()
43 | {
44 | // Arrange
45 | var options = new MSSqlServer.ColumnOptions();
46 |
47 | // Act and assert - should throw
48 | Assert.Throws(() => options.TraceId.DataType = SqlDbType.NChar);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Dependencies/SinkDependenciesFactoryTests.cs:
--------------------------------------------------------------------------------
1 | using Moq;
2 | using System;
3 | using Serilog.Sinks.MSSqlServer.Dependencies;
4 | using Serilog.Sinks.MSSqlServer.Platform;
5 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
6 | using Xunit;
7 | using Microsoft.Data.SqlClient;
8 |
9 | namespace Serilog.Sinks.MSSqlServer.Tests.Dependencies
10 | {
11 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
12 | public class SinkDependenciesFactoryTests
13 | {
14 | private const string _connectionString = "Server=localhost;Database=LogTest;Integrated Security=SSPI;Encrypt=False;";
15 | private readonly MSSqlServerSinkOptions _sinkOptions;
16 | private readonly MSSqlServer.ColumnOptions _columnOptions;
17 |
18 | public SinkDependenciesFactoryTests()
19 | {
20 | _sinkOptions = new MSSqlServerSinkOptions { TableName = "LogEvents" };
21 | _columnOptions = new MSSqlServer.ColumnOptions();
22 | }
23 |
24 | [Fact]
25 | public void CreatesSinkDependenciesWithSqlDatabaseCreator()
26 | {
27 | // Act
28 | var result = SinkDependenciesFactory.Create(_connectionString, _sinkOptions, null, _columnOptions, null);
29 |
30 | // Assert
31 | Assert.NotNull(result.SqlDatabaseCreator);
32 | Assert.IsType(result.SqlDatabaseCreator);
33 | }
34 |
35 | [Fact]
36 | public void CreatesSinkDependenciesWithSqlTableCreator()
37 | {
38 | // Act
39 | var result = SinkDependenciesFactory.Create(_connectionString, _sinkOptions, null, _columnOptions, null);
40 |
41 | // Assert
42 | Assert.NotNull(result.SqlTableCreator);
43 | Assert.IsType(result.SqlTableCreator);
44 | }
45 |
46 | [Fact]
47 | public void CreatesSinkDependenciesWithSqlBulkBatchWriter()
48 | {
49 | // Act
50 | var result = SinkDependenciesFactory.Create(_connectionString, _sinkOptions, null, _columnOptions, null);
51 |
52 | // Assert
53 | Assert.NotNull(result.SqlBulkBatchWriter);
54 | Assert.IsType(result.SqlBulkBatchWriter);
55 | }
56 |
57 | [Fact]
58 | public void CreatesSinkDependenciesWithSqlLogEventWriter()
59 | {
60 | // Act
61 | var result = SinkDependenciesFactory.Create(_connectionString, _sinkOptions, null, _columnOptions, null);
62 |
63 | // Assert
64 | Assert.NotNull(result.SqlLogEventWriter);
65 | Assert.IsType(result.SqlLogEventWriter);
66 | }
67 |
68 | [Fact]
69 | public void DefaultsColumnOptionsIfNull()
70 | {
71 | // Act (should not throw)
72 | SinkDependenciesFactory.Create(_connectionString, _sinkOptions, null, null, null);
73 | }
74 |
75 | [Fact]
76 | public void CreatesSinkDependenciesWithSqlConnectionConfiguration()
77 | {
78 | // Arrange
79 | var mockConfigurationAction = new Mock>();
80 | var sinkOptions = new MSSqlServerSinkOptions { TableName = "LogEvents", ConnectionConfiguration = mockConfigurationAction.Object };
81 |
82 | // Act
83 | var result = SinkDependenciesFactory.Create(_connectionString, sinkOptions, null, _columnOptions, null);
84 |
85 | // Assert
86 | Assert.NotNull(result.SqlDatabaseCreator);
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/MSSqlServerSinkOptionsTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
3 | using Xunit;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Tests
6 | {
7 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
8 | public class MSSqlServerSinkOptionsTests
9 | {
10 | [Fact]
11 | public void InitializesDefaultedPropertiesWithDefaultsWhenCalledWithoutParameters()
12 | {
13 | // Act
14 | var sut = new MSSqlServerSinkOptions();
15 |
16 | // Assert
17 | Assert.Equal(MSSqlServerSink.DefaultSchemaName, sut.SchemaName);
18 | Assert.Equal(MSSqlServerSink.DefaultBatchPostingLimit, sut.BatchPostingLimit);
19 | Assert.Equal(MSSqlServerSink.DefaultPeriod, sut.BatchPeriod);
20 | }
21 |
22 | [Fact]
23 | public void InitializesDefaultedPropertiesWithDefaultsWhenCalledWithParameters()
24 | {
25 | // Act
26 | var sut = new MSSqlServerSinkOptions("TestTableName", null, null, true, null);
27 |
28 | // Assert
29 | Assert.Equal(MSSqlServerSink.DefaultSchemaName, sut.SchemaName);
30 | Assert.Equal(MSSqlServerSink.DefaultBatchPostingLimit, sut.BatchPostingLimit);
31 | Assert.Equal(MSSqlServerSink.DefaultPeriod, sut.BatchPeriod);
32 | }
33 |
34 | [Fact]
35 | public void InitializesPropertiesWithParameterValues()
36 | {
37 | // Arrange
38 | const string tableName = "TestTableName";
39 | const int batchPostingLimit = 23;
40 | const string schemaName = "TestSchemaName";
41 | var batchPeriod = new TimeSpan(0, 3, 23);
42 |
43 | // Act
44 | var sut = new MSSqlServerSinkOptions(tableName, batchPostingLimit, batchPeriod, true, schemaName);
45 |
46 | // Assert
47 | Assert.Equal(tableName, sut.TableName);
48 | Assert.Equal(batchPostingLimit, sut.BatchPostingLimit);
49 | Assert.Equal(batchPeriod, sut.BatchPeriod);
50 | Assert.True(sut.AutoCreateSqlTable);
51 | Assert.Equal(schemaName, sut.SchemaName);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Options/SinkOptionsTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
3 | using Xunit;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Tests.Options
6 | {
7 | [Obsolete("Backwards compatibility tests for old SinkOptions class", error: false)]
8 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
9 | public class SinkOptionsTests
10 | {
11 | [Fact]
12 | public void InitializesDefaultedPropertiesWithDefaultsWhenCalledWithoutParameters()
13 | {
14 | // Act
15 | var sut = new Serilog.Sinks.MSSqlServer.Sinks.MSSqlServer.Options.SinkOptions();
16 |
17 | // Assert
18 | Assert.Equal(MSSqlServerSink.DefaultSchemaName, sut.SchemaName);
19 | Assert.Equal(MSSqlServerSink.DefaultBatchPostingLimit, sut.BatchPostingLimit);
20 | Assert.Equal(MSSqlServerSink.DefaultPeriod, sut.BatchPeriod);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Output/ColumnSimplePropertyValueResolverTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Data;
3 | using Serilog.Events;
4 | using Serilog.Sinks.MSSqlServer.Output;
5 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
6 | using Xunit;
7 |
8 | namespace Serilog.Sinks.MSSqlServer.Tests.Output
9 | {
10 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
11 | public class ColumnSimplePropertyValueResolverTests
12 | {
13 | private readonly Dictionary _properties;
14 | private readonly ColumnSimplePropertyValueResolver _sut;
15 |
16 | public ColumnSimplePropertyValueResolverTests()
17 | {
18 | _properties = new Dictionary();
19 | _sut = new ColumnSimplePropertyValueResolver();
20 | }
21 |
22 | [Fact]
23 | public void GetPropertyValueForColumnDefaultIfPropertyNotFound()
24 | {
25 | // Arrange
26 | _properties.Add("Property1", new ScalarValue("Value1"));
27 | _properties.Add("Property2", new ScalarValue("Value2"));
28 | _properties.Add("Property3", new ScalarValue("Value3"));
29 |
30 | // Act
31 | var result = _sut.GetPropertyValueForColumn(new SqlColumn("NotFoundProperty", SqlDbType.NVarChar), _properties);
32 |
33 | // Assert
34 | Assert.Equal(default, result);
35 | }
36 |
37 | [Fact]
38 | public void GetPropertyValueForColumnReturnsPropertyValue()
39 | {
40 | // Arrange
41 | const string property2Key = "Property2";
42 | _properties.Add("Property1", new ScalarValue("Value1"));
43 | _properties.Add(property2Key, new ScalarValue("Value2"));
44 | _properties.Add("Property3", new ScalarValue("Value3"));
45 |
46 | // Act
47 | var result = _sut.GetPropertyValueForColumn(new SqlColumn(property2Key, SqlDbType.NVarChar), _properties);
48 |
49 | // Assert
50 | Assert.Equal(property2Key, result.Key);
51 | Assert.Equal(_properties[property2Key], result.Value);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlClient/SqlBulkCopyWrapperTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Data.SqlClient;
3 | using Xunit;
4 | using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
5 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
6 |
7 | namespace Serilog.Sinks.MSSqlServer.Tests.Platform.SqlClient
8 | {
9 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
10 | public class SqlBulkCopyWrapperTests
11 | {
12 | [Fact]
13 | public void InitializeThrowsIfSqlBulkCopyIsNull()
14 | {
15 | Assert.Throws(() => new SqlBulkCopyWrapper(null));
16 | }
17 |
18 | [Fact]
19 | public void AddSqlBulkCopyColumnMappingDoesNotThrow()
20 | {
21 | // Arrange
22 | using (var connection = new SqlConnection())
23 | {
24 | using (var sqlBulkCopy = new SqlBulkCopy(connection))
25 | {
26 | using (var sut = new SqlBulkCopyWrapper(sqlBulkCopy))
27 | {
28 | // Act (should not throw)
29 | sut.AddSqlBulkCopyColumnMapping("Column", "Column");
30 | }
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlClient/SqlCommandWrapperTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Data.SqlClient;
3 | using Xunit;
4 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
5 | using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
6 |
7 | namespace Serilog.Sinks.MSSqlServer.Tests.Platform.SqlClient
8 | {
9 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
10 | public class SqlCommandWrapperTests
11 | {
12 | [Fact]
13 | public void InitializeThrowsIfSqlCommandIsNull()
14 | {
15 | // Arrange + act
16 | Assert.Throws(() => new SqlCommandWrapper(null));
17 | }
18 |
19 | [Fact]
20 | public void AddParameterDoesNotThrow()
21 | {
22 | // Arrange
23 | using (var sqlConnection = new SqlConnection())
24 | {
25 | using (var sqlCommand = new SqlCommand("SELECT * FROM Table WHERE Id = @Parameter", sqlConnection))
26 | {
27 | using (var sut = new SqlCommandWrapper(sqlCommand))
28 | {
29 | // Act (should not throw)
30 | sut.AddParameter("Parameter", "Value");
31 | }
32 | }
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlClient/SqlConnectionStringBuilderWrapperTests.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 | using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
3 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Tests.Platform.SqlClient
6 | {
7 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
8 | public class SqlConnectionStringBuilderWrapperTests
9 | {
10 | [Fact]
11 | public void ChangeEnlistFalseToTrueIfEnlistPropertyIsSetToTrue()
12 | {
13 | // Arrange + act
14 | var sut = new SqlConnectionStringBuilderWrapper(DatabaseFixture.LogEventsConnectionString + ";Enlist=False", true);
15 |
16 | // Assert
17 | Assert.Equal(DatabaseFixture.LogEventsConnectionString + ";Enlist=True", sut.ConnectionString);
18 | }
19 |
20 | [Fact]
21 | public void ChangeEnlistTrueToFalseIfEnlistPropertyIsSetToFalse()
22 | {
23 | // Arrange
24 | var sut = new SqlConnectionStringBuilderWrapper(DatabaseFixture.LogEventsConnectionString + ";Enlist=True", false);
25 |
26 | // Assert
27 | Assert.Equal(DatabaseFixture.LogEventsConnectionString + ";Enlist=False", sut.ConnectionString);
28 | }
29 |
30 | [Fact]
31 | public void AddsEnlistFalseIfEnlistPropertySetToFalse()
32 | {
33 | // Arrange
34 | var sut = new SqlConnectionStringBuilderWrapper(DatabaseFixture.LogEventsConnectionString, false);
35 |
36 | // Assert
37 | Assert.Equal(DatabaseFixture.LogEventsConnectionString + ";Enlist=False", sut.ConnectionString);
38 | }
39 |
40 | [Fact]
41 | public void AddsEnlistTrueIfEnlistPropertySetToTrue()
42 | {
43 | // Arrange
44 | var sut = new SqlConnectionStringBuilderWrapper(DatabaseFixture.LogEventsConnectionString, true);
45 |
46 | // Assert
47 | Assert.Equal(DatabaseFixture.LogEventsConnectionString + ";Enlist=True", sut.ConnectionString);
48 | }
49 |
50 | [Fact]
51 | public void DoesNotDuplicateEnlistIfEnlistFalseIsPresentAndEnlistPropertySetToFalse()
52 | {
53 | // Arrange
54 | var sut = new SqlConnectionStringBuilderWrapper("Enlist = false ; " + DatabaseFixture.LogEventsConnectionString, false);
55 |
56 | // Assert
57 | Assert.Equal(DatabaseFixture.LogEventsConnectionString + ";Enlist=False", sut.ConnectionString);
58 | }
59 |
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlClient/SqlConnectionWrapperTests.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
3 | using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Tests.Platform.SqlClient
6 | {
7 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
8 | public class SqlConnectionWrapperTests
9 | {
10 | [Fact]
11 | public void CreateSqlBulkCopyReturnsSqlBulkCopyWrapper()
12 | {
13 | // Arrange
14 | using (var sut = new SqlConnectionWrapper(DatabaseFixture.LogEventsConnectionString))
15 | {
16 | // Act
17 | var result = sut.CreateSqlBulkCopy(false, "DestinationTableName");
18 |
19 | // Assert
20 | Assert.NotNull(result);
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlConnectionFactoryTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Data.SqlClient;
3 | using Moq;
4 | using Serilog.Sinks.MSSqlServer.Platform;
5 | using Serilog.Sinks.MSSqlServer.Platform.SqlClient;
6 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
7 | using Xunit;
8 |
9 | namespace Serilog.Sinks.MSSqlServer.Tests.Platform
10 | {
11 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
12 | public class SqlConnectionFactoryTests
13 | {
14 | private readonly Mock _sqlConnectionStringBuilderWrapperMock;
15 |
16 | public SqlConnectionFactoryTests()
17 | {
18 | _sqlConnectionStringBuilderWrapperMock = new Mock();
19 | _sqlConnectionStringBuilderWrapperMock.SetupAllProperties();
20 | }
21 |
22 | [Fact]
23 | public void IntializeThrowsIfSqlConnectionStringBuilderWrapperIsNull()
24 | {
25 | Assert.Throws(() => new SqlConnectionFactory(null));
26 | }
27 |
28 | [Fact]
29 | public void CreateConnectionReturnsConnectionWrapper()
30 | {
31 | // Arrange
32 | var sut = new SqlConnectionFactory(_sqlConnectionStringBuilderWrapperMock.Object);
33 |
34 | // Act
35 | using (var connection = sut.Create())
36 | {
37 | // Assert
38 | Assert.NotNull(connection);
39 | Assert.IsAssignableFrom(connection);
40 | }
41 | }
42 |
43 |
44 | [Fact]
45 | public void CreateConnectionCallsCustomConfigurationAction()
46 | {
47 | // Arrange
48 | var mockAction = new Mock>();
49 | var sut = new SqlConnectionFactory(_sqlConnectionStringBuilderWrapperMock.Object, mockAction.Object);
50 |
51 | // Act
52 | using (var connection = sut.Create())
53 | {
54 | // Assert
55 | Assert.NotNull(connection);
56 | Assert.IsAssignableFrom(connection);
57 | mockAction.Verify(m => m.Invoke(connection.SqlConnection), Times.Once);
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlCreateDatabaseWriterTests.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.MSSqlServer.Platform;
2 | using Serilog.Sinks.MSSqlServer.Tests.TestUtils;
3 | using Xunit;
4 |
5 | namespace Serilog.Sinks.MSSqlServer.Tests.Platform
6 | {
7 | [Trait(TestCategory.TraitName, TestCategory.Unit)]
8 | public class SqlCreateDatabaseWriterTests
9 | {
10 | [Fact]
11 | public void GetSqlWritesCorrectCommand()
12 | {
13 | // Arrange
14 | const string databaseName = "LogDatabase";
15 | const string expectedResult = "IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'LogDatabase')\r\nBEGIN\r\nCREATE DATABASE [LogDatabase]\r\nEND\r\n";
16 | var sut = new SqlCreateDatabaseWriter(databaseName);
17 |
18 | // Act
19 | var result = sut.GetSql();
20 |
21 | // Assert
22 | Assert.Equal(expectedResult, result);
23 | }
24 |
25 | [Fact]
26 | public void GetSqlWritesCorrectCommandForDatabaseNameWithSpaces()
27 | {
28 | // Arrange
29 | const string databaseName = "Log Data Base";
30 | const string expectedResult = "IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'Log Data Base')\r\nBEGIN\r\nCREATE DATABASE [Log Data Base]\r\nEND\r\n";
31 | var sut = new SqlCreateDatabaseWriter(databaseName);
32 |
33 | // Act
34 | var result = sut.GetSql();
35 |
36 | // Assert
37 | Assert.Equal(expectedResult, result);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/TestableSqlCommandExecutor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog.Debugging;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Platform
5 | {
6 | internal class TestableSqlCommandExecutor : SqlCommandExecutor
7 | {
8 | public TestableSqlCommandExecutor(
9 | ISqlWriter sqlWriter,
10 | ISqlConnectionFactory sqlConnectionFactory,
11 | ISqlCommandFactory sqlCommandFactory) : base(sqlWriter, sqlConnectionFactory, sqlCommandFactory)
12 | {
13 | }
14 |
15 | public Action HandleExceptionCallback { get; set; }
16 |
17 | protected override void HandleException(Exception ex)
18 | {
19 | HandleExceptionCallback?.Invoke(ex);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/TestUtils/DapperQueryTemplates.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog.Events;
3 |
4 | namespace Serilog.Sinks.MSSqlServer.Tests.TestUtils
5 | {
6 | public class CustomStandardLogColumns
7 | {
8 | public int CustomId { get; set; }
9 | public string CustomMessage { get; set; }
10 | public string CustomMessageTemplate { get; set; }
11 | public string CustomLevel { get; set; }
12 | public DateTime CustomTimeStamp { get; set; }
13 | public string CustomException { get; set; }
14 | public string CustomProperties { get; set; }
15 | }
16 |
17 | public class DefaultStandardLogColumns
18 | {
19 | public string Message { get; set; }
20 | public LogEventLevel Level { get; set; }
21 | }
22 |
23 | public class InfoSchema
24 | {
25 | public string ColumnName { get; set; }
26 | public string SchemaName { get; set; }
27 | public string DataType { get; set; }
28 | public string DataLength { get; set; }
29 | public string AllowNull { get; set; }
30 | }
31 |
32 | public class sp_pkey
33 | {
34 | public string COLUMN_NAME { get; set; }
35 | public string PK_NAME { get; set; }
36 | }
37 |
38 | public class SysIndex_CCI
39 | {
40 | public string name { get; set; }
41 | }
42 |
43 | public class IdentityQuery
44 | {
45 | public int IsIdentity { get; set; }
46 | }
47 |
48 | public class LogEventColumn
49 | {
50 | public string LogEvent { get; set; }
51 | }
52 |
53 | public class SysObjectQuery
54 | {
55 | public int IndexType { get; set; }
56 | }
57 |
58 | public class PropertiesColumns
59 | {
60 | public string Properties { get; set; }
61 | }
62 |
63 | public class EnumLevelStandardLogColumns
64 | {
65 | public string Message { get; set; }
66 | public byte Level { get; set; }
67 | }
68 |
69 | public class StringLevelStandardLogColumns
70 | {
71 | public string Message { get; set; }
72 | public string Level { get; set; }
73 | }
74 |
75 | public class TestTimeStampDateTimeOffsetEntry
76 | {
77 | public DateTimeOffset TimeStamp { get; set; }
78 | }
79 |
80 | public class TestTimeStampDateTimeEntry
81 | {
82 | public DateTime TimeStamp { get; set; }
83 | }
84 |
85 | public class TestTriggerEntry
86 | {
87 | public Guid Id { get; set; }
88 | public string Data { get; set; }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/TestUtils/DatabaseFixture.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Linq;
4 | using Dapper;
5 | using Microsoft.Data.SqlClient;
6 | using static System.FormattableString;
7 |
8 | namespace Serilog.Sinks.MSSqlServer.Tests.TestUtils
9 | {
10 | public sealed class DatabaseFixture : IDisposable
11 | {
12 |
13 | private const string _masterConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Master;Integrated Security=True;Connect Timeout=120";
14 | private const string _createLogEventsDatabase = @"
15 | EXEC ('CREATE DATABASE [{0}] ON PRIMARY
16 | (NAME = [{0}],
17 | FILENAME =''{1}'',
18 | SIZE = 25MB,
19 | MAXSIZE = 50MB,
20 | FILEGROWTH = 5MB )')";
21 |
22 | private static readonly string _databaseFileNameQuery = Invariant($@"SELECT CONVERT(VARCHAR(255), SERVERPROPERTY('instancedefaultdatapath')) + '{Database}.mdf' AS Name");
23 | private static readonly string _dropLogEventsDatabase = Invariant($@"
24 | ALTER DATABASE [{Database}]
25 | SET SINGLE_USER
26 | WITH ROLLBACK IMMEDIATE
27 | DROP DATABASE [{Database}]
28 | ");
29 |
30 | public static string Database => "LogTest";
31 | public static string LogTableName => "LogEvents";
32 | public static string LogEventsConnectionString => Invariant($@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog={Database};Integrated Security=True");
33 |
34 | public DatabaseFixture()
35 | {
36 | CreateDatabase();
37 | }
38 |
39 | public void Dispose()
40 | {
41 | DeleteDatabase();
42 | }
43 |
44 | public static void DropTable(string tableName = null)
45 | {
46 | using (var conn = new SqlConnection(LogEventsConnectionString))
47 | {
48 | var actualTableName = string.IsNullOrEmpty(tableName) ? LogTableName : tableName;
49 | conn.Execute(Invariant($"IF OBJECT_ID('{actualTableName}', 'U') IS NOT NULL DROP TABLE {actualTableName};"));
50 | }
51 | }
52 |
53 | private static void DeleteDatabase()
54 | {
55 | using (var conn = new SqlConnection(_masterConnectionString))
56 | {
57 | conn.Open();
58 | var databases = conn.Query("select name from sys.databases");
59 |
60 | if (databases.Any(d => d.name == Database)) conn.Execute(_dropLogEventsDatabase);
61 | }
62 | }
63 |
64 | private static void CreateDatabase()
65 | {
66 | DeleteDatabase();
67 |
68 | using (var conn = new SqlConnection(_masterConnectionString))
69 | {
70 | conn.Open();
71 | // ReSharper disable once PossibleNullReferenceException
72 | var filename = conn.Query(_databaseFileNameQuery).FirstOrDefault().Name;
73 | var createDatabase = string.Format(CultureInfo.InvariantCulture, _createLogEventsDatabase, Database, filename);
74 |
75 | conn.Execute(createDatabase);
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/TestUtils/Filename.cs:
--------------------------------------------------------------------------------
1 | namespace Serilog.Sinks.MSSqlServer.Tests.TestUtils
2 | {
3 | public class FileName
4 | {
5 | public string Name { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/TestUtils/PatientSecureFixture.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace Serilog.Sinks.MSSqlServer.Tests.TestUtils
4 | {
5 | [CollectionDefinition("DatabaseTests", DisableParallelization = true)]
6 | public class PatientSecureFixture : ICollectionFixture { }
7 | }
8 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/TestUtils/TestCategory.cs:
--------------------------------------------------------------------------------
1 | namespace Serilog.Sinks.MSSqlServer.Tests.TestUtils
2 | {
3 | public static class TestCategory
4 | {
5 | public const string TraitName = "Category";
6 |
7 | public const string Integration = nameof(Integration);
8 | public const string Unit = nameof(Unit);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/test/Serilog.Sinks.MSSqlServer.Tests/TestUtils/TestLogEventHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Serilog.Events;
4 | using Serilog.Parsing;
5 |
6 | namespace Serilog.Sinks.MSSqlServer.Tests.TestUtils
7 | {
8 | internal static class TestLogEventHelper
9 | {
10 | public static LogEvent CreateLogEvent()
11 | {
12 | return new LogEvent(
13 | new DateTimeOffset(2020, 1, 1, 0, 0, 0, 0, TimeSpan.Zero),
14 | LogEventLevel.Debug, null, new MessageTemplate(new List()),
15 | new List());
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------