├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Build.ps1 ├── CHANGES.md ├── Directory.Build.props ├── Directory.Version.props ├── LICENSE ├── README.md ├── assets ├── Serilog.snk └── icon.png ├── global.json ├── samples ├── SimpleServiceSample │ ├── PrintTimeService.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── SimpleServiceSample.csproj │ ├── appsettings.Development.json │ └── appsettings.json └── WebApplicationSample │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── WebApplicationSample.csproj │ ├── appsettings.Development.json │ └── appsettings.json ├── serilog-extensions-hosting.sln ├── serilog-extensions-hosting.sln.DotSettings ├── src └── Serilog.Extensions.Hosting │ ├── Extensions │ └── Hosting │ │ ├── AmbientDiagnosticContextCollector.cs │ │ ├── CachingReloadableLogger.cs │ │ ├── DiagnosticContext.cs │ │ ├── DiagnosticContextCollector.cs │ │ ├── FixedPropertyEnricher.cs │ │ ├── IReloadableLogger.cs │ │ ├── InjectedLoggerSettings.cs │ │ ├── LoggerBase.cs │ │ ├── NullEnricher.cs │ │ └── ReloadableLogger.cs │ ├── IDiagnosticContext.cs │ ├── LoggerConfigurationExtensions.cs │ ├── LoggerSettingsConfigurationExtensions.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── Serilog.Extensions.Hosting.csproj │ ├── SerilogHostBuilderExtensions.cs │ └── SerilogServiceCollectionExtensions.cs └── test └── Serilog.Extensions.Hosting.Tests ├── DiagnosticContextTests.cs ├── LoggerSettingsConfigurationExtensionsTests.cs ├── ReloadableLoggerTests.cs ├── Serilog.Extensions.Hosting.Tests.csproj ├── SerilogHostBuilderExtensionsTests.cs ├── SerilogServiceCollectionExtensionsTests.cs └── Support ├── ListSink.cs └── Some.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | 3 | * text=auto 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # If this file is renamed, the incrementing run attempt number will be reset. 2 | 3 | name: CI 4 | 5 | on: 6 | push: 7 | branches: [ "dev", "main" ] 8 | pull_request: 9 | branches: [ "dev", "main" ] 10 | 11 | env: 12 | CI_BUILD_NUMBER_BASE: ${{ github.run_number }} 13 | CI_TARGET_BRANCH: ${{ github.head_ref || github.ref_name }} 14 | 15 | jobs: 16 | build: 17 | 18 | # The build must run on Windows so that .NET Framework targets can be built and tested. 19 | runs-on: windows-latest 20 | 21 | permissions: 22 | contents: write 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | - name: Setup 27 | uses: actions/setup-dotnet@v4 28 | with: 29 | dotnet-version: 9.0.x 30 | - name: Compute build number 31 | shell: bash 32 | run: | 33 | echo "CI_BUILD_NUMBER=$(($CI_BUILD_NUMBER_BASE+2300))" >> $GITHUB_ENV 34 | - name: Build and Publish 35 | env: 36 | DOTNET_CLI_TELEMETRY_OPTOUT: true 37 | NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} 38 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | shell: pwsh 40 | run: | 41 | ./Build.ps1 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | 50 | *_i.c 51 | *_p.c 52 | *_i.h 53 | *.ilk 54 | *.meta 55 | *.obj 56 | *.pch 57 | *.pdb 58 | *.pgc 59 | *.pgd 60 | *.rsp 61 | *.sbr 62 | *.tlb 63 | *.tli 64 | *.tlh 65 | *.tmp 66 | *.tmp_proj 67 | *.log 68 | *.vspscc 69 | *.vssscc 70 | .builds 71 | *.pidb 72 | *.svclog 73 | *.scc 74 | 75 | # Chutzpah Test files 76 | _Chutzpah* 77 | 78 | # Visual C++ cache files 79 | ipch/ 80 | *.aps 81 | *.ncb 82 | *.opendb 83 | *.opensdf 84 | *.sdf 85 | *.cachefile 86 | *.VC.db 87 | *.VC.VC.opendb 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | *.sap 94 | 95 | # TFS 2012 Local Workspace 96 | $tf/ 97 | 98 | # Guidance Automation Toolkit 99 | *.gpState 100 | 101 | # ReSharper is a .NET coding add-in 102 | _ReSharper*/ 103 | *.[Rr]e[Ss]harper 104 | *.DotSettings.user 105 | 106 | # JustCode is a .NET coding add-in 107 | .JustCode 108 | 109 | # TeamCity is a build add-in 110 | _TeamCity* 111 | 112 | # DotCover is a Code Coverage Tool 113 | *.dotCover 114 | 115 | # Visual Studio code coverage results 116 | *.coverage 117 | *.coveragexml 118 | 119 | # NCrunch 120 | _NCrunch_* 121 | .*crunch*.local.xml 122 | nCrunchTemp_* 123 | 124 | # MightyMoose 125 | *.mm.* 126 | AutoTest.Net/ 127 | 128 | # Web workbench (sass) 129 | .sass-cache/ 130 | 131 | # Installshield output folder 132 | [Ee]xpress/ 133 | 134 | # DocProject is a documentation generator add-in 135 | DocProject/buildhelp/ 136 | DocProject/Help/*.HxT 137 | DocProject/Help/*.HxC 138 | DocProject/Help/*.hhc 139 | DocProject/Help/*.hhk 140 | DocProject/Help/*.hhp 141 | DocProject/Help/Html2 142 | DocProject/Help/html 143 | 144 | # Click-Once directory 145 | publish/ 146 | 147 | # Publish Web Output 148 | *.[Pp]ublish.xml 149 | *.azurePubxml 150 | # TODO: Comment the next line if you want to checkin your web deploy settings 151 | # but database connection strings (with potential passwords) will be unencrypted 152 | *.pubxml 153 | *.publishproj 154 | 155 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 156 | # checkin your Azure Web App publish settings, but sensitive information contained 157 | # in these scripts will be unencrypted 158 | PublishScripts/ 159 | 160 | # NuGet Packages 161 | *.nupkg 162 | # The packages folder can be ignored because of Package Restore 163 | **/packages/* 164 | # except build/, which is used as an MSBuild target. 165 | !**/packages/build/ 166 | # Uncomment if necessary however generally it will be regenerated when needed 167 | #!**/packages/repositories.config 168 | # NuGet v3's project.json files produces more ignorable files 169 | *.nuget.props 170 | *.nuget.targets 171 | 172 | # Microsoft Azure Build Output 173 | csx/ 174 | *.build.csdef 175 | 176 | # Microsoft Azure Emulator 177 | ecf/ 178 | rcf/ 179 | 180 | # Windows Store app package directories and files 181 | AppPackages/ 182 | BundleArtifacts/ 183 | Package.StoreAssociation.xml 184 | _pkginfo.txt 185 | 186 | # Visual Studio cache files 187 | # files ending in .cache can be ignored 188 | *.[Cc]ache 189 | # but keep track of directories ending in .cache 190 | !*.[Cc]ache/ 191 | 192 | # Others 193 | ClientBin/ 194 | ~$* 195 | *~ 196 | *.dbmdl 197 | *.dbproj.schemaview 198 | *.jfm 199 | *.pfx 200 | *.publishsettings 201 | orleans.codegen.cs 202 | 203 | # Since there are multiple workflows, uncomment next line to ignore bower_components 204 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 205 | #bower_components/ 206 | 207 | # RIA/Silverlight projects 208 | Generated_Code/ 209 | 210 | # Backup & report files from converting an old project file 211 | # to a newer Visual Studio version. Backup files are not needed, 212 | # because we have git ;-) 213 | _UpgradeReport_Files/ 214 | Backup*/ 215 | UpgradeLog*.XML 216 | UpgradeLog*.htm 217 | 218 | # SQL Server files 219 | *.mdf 220 | *.ldf 221 | *.ndf 222 | 223 | # Business Intelligence projects 224 | *.rdl.data 225 | *.bim.layout 226 | *.bim_*.settings 227 | 228 | # Microsoft Fakes 229 | FakesAssemblies/ 230 | 231 | # GhostDoc plugin setting file 232 | *.GhostDoc.xml 233 | 234 | # Node.js Tools for Visual Studio 235 | .ntvs_analysis.dat 236 | node_modules/ 237 | 238 | # Typescript v1 declaration files 239 | typings/ 240 | 241 | # Visual Studio 6 build log 242 | *.plg 243 | 244 | # Visual Studio 6 workspace options file 245 | *.opt 246 | 247 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 248 | *.vbw 249 | 250 | # Visual Studio LightSwitch build output 251 | **/*.HTMLClient/GeneratedArtifacts 252 | **/*.DesktopClient/GeneratedArtifacts 253 | **/*.DesktopClient/ModelManifest.xml 254 | **/*.Server/GeneratedArtifacts 255 | **/*.Server/ModelManifest.xml 256 | _Pvt_Extensions 257 | 258 | # Paket dependency manager 259 | .paket/paket.exe 260 | paket-files/ 261 | 262 | # FAKE - F# Make 263 | .fake/ 264 | 265 | # JetBrains Rider 266 | .idea/ 267 | *.sln.iml 268 | 269 | # CodeRush 270 | .cr/ 271 | 272 | # Python Tools for Visual Studio (PTVS) 273 | __pycache__/ 274 | *.pyc 275 | 276 | # Cake - Uncomment if you are using it 277 | # tools/** 278 | # !tools/packages.config 279 | 280 | # Telerik's JustMock configuration file 281 | *.jmconfig 282 | 283 | # BizTalk build output 284 | *.btp.cs 285 | *.btm.cs 286 | *.odx.cs 287 | *.xsd.cs 288 | /samples/WebApplicationSample/logs/ 289 | 290 | .DS_Store 291 | 292 | -------------------------------------------------------------------------------- /Build.ps1: -------------------------------------------------------------------------------- 1 | Write-Output "build: Tool versions follow" 2 | 3 | dotnet --version 4 | dotnet --list-sdks 5 | 6 | Write-Output "build: Build started" 7 | 8 | Push-Location $PSScriptRoot 9 | try { 10 | if(Test-Path .\artifacts) { 11 | Write-Output "build: Cleaning ./artifacts" 12 | Remove-Item ./artifacts -Force -Recurse 13 | } 14 | 15 | & dotnet restore --no-cache 16 | 17 | $dbp = [Xml] (Get-Content .\Directory.Version.props) 18 | $versionPrefix = $dbp.Project.PropertyGroup.VersionPrefix 19 | 20 | Write-Output "build: Package version prefix is $versionPrefix" 21 | 22 | $branch = @{ $true = $env:CI_TARGET_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$NULL -ne $env:CI_TARGET_BRANCH]; 23 | $revision = @{ $true = "{0:00000}" -f [convert]::ToInt32("0" + $env:CI_BUILD_NUMBER, 10); $false = "local" }[$NULL -ne $env:CI_BUILD_NUMBER]; 24 | $suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)) -replace '([^a-zA-Z0-9\-]*)', '')-$revision"}[$branch -eq "main" -and $revision -ne "local"] 25 | $commitHash = $(git rev-parse --short HEAD) 26 | $buildSuffix = @{ $true = "$($suffix)-$($commitHash)"; $false = "$($branch)-$($commitHash)" }[$suffix -ne ""] 27 | 28 | Write-Output "build: Package version suffix is $suffix" 29 | Write-Output "build: Build version suffix is $buildSuffix" 30 | 31 | & dotnet build -c Release --version-suffix=$buildSuffix /p:ContinuousIntegrationBuild=true 32 | if($LASTEXITCODE -ne 0) { throw "Build failed" } 33 | 34 | foreach ($src in Get-ChildItem src/*) { 35 | Push-Location $src 36 | 37 | Write-Output "build: Packaging project in $src" 38 | 39 | if ($suffix) { 40 | & dotnet pack -c Release --no-build --no-restore -o ../../artifacts --version-suffix=$suffix 41 | } else { 42 | & dotnet pack -c Release --no-build --no-restore -o ../../artifacts 43 | } 44 | if($LASTEXITCODE -ne 0) { throw "Packaging failed" } 45 | 46 | Pop-Location 47 | } 48 | 49 | foreach ($test in Get-ChildItem test/*.Tests) { 50 | Push-Location $test 51 | 52 | Write-Output "build: Testing project in $test" 53 | 54 | & dotnet test -c Release --no-build --no-restore 55 | if($LASTEXITCODE -ne 0) { throw "Testing failed" } 56 | 57 | Pop-Location 58 | } 59 | 60 | if ($env:NUGET_API_KEY) { 61 | # GitHub Actions will only supply this to branch builds and not PRs. We publish 62 | # builds from any branch this action targets (i.e. main and dev). 63 | 64 | Write-Output "build: Publishing NuGet packages" 65 | 66 | foreach ($nupkg in Get-ChildItem artifacts/*.nupkg) { 67 | & dotnet nuget push -k $env:NUGET_API_KEY -s https://api.nuget.org/v3/index.json "$nupkg" 68 | if($LASTEXITCODE -ne 0) { throw "Publishing failed" } 69 | } 70 | 71 | if (!($suffix)) { 72 | Write-Output "build: Creating release for version $versionPrefix" 73 | 74 | iex "gh release create v$versionPrefix --title v$versionPrefix --generate-notes $(get-item ./artifacts/*.nupkg) $(get-item ./artifacts/*.snupkg)" 75 | } 76 | } 77 | } finally { 78 | Pop-Location 79 | } 80 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | 2.0.0 2 | 3 | * Initial version for ASP.NET Core 2.1 RC1. 4 | 5 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | latest 7 | True 8 | 9 | true 10 | $(MSBuildThisFileDirectory)assets/Serilog.snk 11 | false 12 | enable 13 | enable 14 | true 15 | true 16 | true 17 | true 18 | snupkg 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Directory.Version.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9.0.1 5 | 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Serilog.Extensions.Hosting [![Build status](https://github.com/serilog/serilog-extensions-hosting/actions/workflows/ci.yml/badge.svg?branch=dev)](https://github.com/serilog/serilog-extensions-hosting/actions) [![NuGet Version](http://img.shields.io/nuget/v/Serilog.Extensions.Hosting.svg?style=flat)](https://www.nuget.org/packages/Serilog.Extensions.Hosting/) 2 | 3 | Serilog logging for _Microsoft.Extensions.Hosting_. This package routes framework log messages through Serilog, so you can get information about the framework's internal operations written to the same Serilog sinks as your application events. 4 | 5 | **Versioning:** This package tracks the versioning and target framework support of its 6 | [_Microsoft.Extensions.Hosting_](https://nuget.org/packages/Microsoft.Extensions.Hosting) dependency. Most users should choose the version of _Serilog.Extensions.Hosting_ that matches 7 | their application's target framework. I.e. if you're targeting .NET 7.x, choose a 7.x version of _Serilog.Extensions.Hosting_. If 8 | you're targeting .NET 8.x, choose an 8.x _Serilog.Extensions.Hosting_ version, and so on. 9 | 10 | ### Instructions 11 | 12 | **First**, install the _Serilog.Extensions.Hosting_ [NuGet package](https://www.nuget.org/packages/Serilog.Extensions.Hosting) into your app. You will need a way to view the log messages - _Serilog.Sinks.Console_ writes these to the console; there are [many more sinks available](https://www.nuget.org/packages?q=Tags%3A%22serilog%22) on NuGet. 13 | 14 | ```powershell 15 | dotnet add package Serilog.Extensions.Hosting 16 | dotnet add package Serilog.Sinks.Console 17 | ``` 18 | 19 | **Next**, in your application's _Program.cs_ file, configure Serilog first. A `try`/`catch` block will ensure any configuration issues are appropriately logged. Call `AddSerilog()` on the host application builder: 20 | 21 | ```csharp 22 | using Serilog; 23 | 24 | Log.Logger = new LoggerConfiguration() 25 | .Enrich.FromLogContext() 26 | .WriteTo.Console() 27 | .CreateLogger(); 28 | 29 | try 30 | { 31 | Log.Information("Starting host"); 32 | 33 | var builder = Host.CreateApplicationBuilder(args); 34 | builder.Services.AddHostedService(); 35 | builder.Services.AddSerilog(); 36 | 37 | var app = builder.Build(); 38 | 39 | await app.RunAsync(); 40 | return 0; 41 | } 42 | catch (Exception ex) 43 | { 44 | Log.Fatal(ex, "Host terminated unexpectedly"); 45 | return 1; 46 | } 47 | finally 48 | { 49 | await Log.CloseAndFlushAsync(); 50 | } 51 | ``` 52 | 53 | **Finally**, clean up by removing the remaining `"Logging"` section from _appsettings.json_ files (this can be replaced with [Serilog configuration](https://github.com/serilog/serilog-settings-configuration) as shown in [this example](https://github.com/serilog/serilog-extensions-hosting/blob/dev/samples/SimpleServiceSample/Program.cs), if required) 54 | 55 | That's it! You will see log output like: 56 | 57 | ``` 58 | [22:10:39 INF] Getting the motors running... 59 | [22:10:39 INF] The current time is: 12/05/2018 10:10:39 +00:00 60 | ``` 61 | 62 | A more complete example, showing _appsettings.json_ configuration, can be found in [the sample project here](https://github.com/serilog/serilog-extensions-hosting/tree/dev/samples/SimpleServiceSample). 63 | 64 | ### Using the package 65 | 66 | With _Serilog.Extensions.Hosting_ installed and configured, you can write log messages directly through Serilog or any `ILogger` interface injected by .NET. All loggers will use the same underlying implementation, levels, and destinations. 67 | 68 | ### Inline initialization 69 | 70 | You can alternatively configure Serilog using a delegate as shown below: 71 | 72 | ```csharp 73 | // dotnet add package Serilog.Settings.Configuration 74 | builder.Services.AddSerilog((services, loggerConfiguration) => loggerConfiguration 75 | .ReadFrom.Configuration(builder.Configuration) 76 | .Enrich.FromLogContext() 77 | .WriteTo.Console()) 78 | ``` 79 | 80 | This has the advantage of making `builder`'s `Configuration` object available for configuration of the logger, but at the expense of ignoring `Exception`s raised earlier in program startup. 81 | 82 | If this method is used, `Log.Logger` is assigned implicitly, and closed when the app is shut down. 83 | -------------------------------------------------------------------------------- /assets/Serilog.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serilog/serilog-extensions-hosting/962e7214eb7dd5fb3b2e4347189baf5c9d260d9a/assets/Serilog.snk -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serilog/serilog-extensions-hosting/962e7214eb7dd5fb3b2e4347189baf5c9d260d9a/assets/icon.png -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "9.0.100", 4 | "allowPrerelease": false, 5 | "rollForward": "latestFeature" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /samples/SimpleServiceSample/PrintTimeService.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 SimpleServiceSample; 8 | 9 | public class PrintTimeService : BackgroundService 10 | { 11 | private readonly ILogger _logger; 12 | 13 | public PrintTimeService(ILogger logger) 14 | { 15 | _logger = logger; 16 | } 17 | 18 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 19 | { 20 | while (!stoppingToken.IsCancellationRequested) 21 | { 22 | _logger.LogInformation("The current time is: {CurrentTime}", DateTimeOffset.UtcNow); 23 | await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/SimpleServiceSample/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Hosting; 4 | using Serilog; 5 | using SimpleServiceSample; 6 | 7 | Log.Logger = new LoggerConfiguration() 8 | .Enrich.FromLogContext() 9 | .WriteTo.Console() 10 | .CreateBootstrapLogger(); 11 | 12 | try 13 | { 14 | Log.Information("Getting the motors running..."); 15 | 16 | var builder = Host.CreateApplicationBuilder(args); 17 | 18 | builder.Services.AddHostedService(); 19 | 20 | builder.Services.AddSerilog((services, loggerConfiguration) => loggerConfiguration 21 | .ReadFrom.Configuration(builder.Configuration) 22 | .ReadFrom.Services(services) 23 | .Enrich.FromLogContext() 24 | .WriteTo.Console()); 25 | 26 | var app = builder.Build(); 27 | 28 | await app.RunAsync(); 29 | 30 | return 0; 31 | } 32 | catch (Exception ex) 33 | { 34 | Log.Fatal(ex, "Host terminated unexpectedly"); 35 | return 1; 36 | } 37 | finally 38 | { 39 | await Log.CloseAndFlushAsync(); 40 | } 41 | -------------------------------------------------------------------------------- /samples/SimpleServiceSample/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "SimpleServiceSample": { 4 | "commandName": "Project", 5 | "environmentVariables": { 6 | "ASPNETCORE_ENVIRONMENT": "Development" 7 | } 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /samples/SimpleServiceSample/SimpleServiceSample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /samples/SimpleServiceSample/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "MinimumLevel": { 4 | "Default": "Debug" 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /samples/SimpleServiceSample/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "MinimumLevel": { 4 | "Default": "Debug", 5 | "Override": { 6 | "Microsoft": "Information", 7 | "System": "Warning" 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /samples/WebApplicationSample/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.Extensions.Hosting; 6 | using Serilog; 7 | 8 | 9 | Log.Logger = new LoggerConfiguration() 10 | .WriteTo.Console() 11 | .CreateBootstrapLogger(); 12 | 13 | Log.Information("Starting up!"); 14 | 15 | try 16 | { 17 | var builder = WebApplication.CreateBuilder(); 18 | 19 | builder.Services.AddSerilog((services, loggerConfiguration) => loggerConfiguration 20 | .WriteTo.Console() 21 | .ReadFrom.Configuration(builder.Configuration) 22 | .ReadFrom.Services(services)); 23 | 24 | var app = builder.Build(); 25 | 26 | app.MapGet("/", () => 27 | { 28 | Log.Information("Saying hello"); 29 | return "Hello World!"; 30 | }); 31 | 32 | await app.RunAsync(); 33 | 34 | Log.Information("Stopped cleanly"); 35 | return 0; 36 | } 37 | catch (Exception ex) 38 | { 39 | Log.Fatal(ex, "An unhandled exception occured during bootstrapping"); 40 | return 1; 41 | } 42 | finally 43 | { 44 | await Log.CloseAndFlushAsync(); 45 | } 46 | -------------------------------------------------------------------------------- /samples/WebApplicationSample/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:15670", 7 | "sslPort": 44322 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "WebApplicationSample": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /samples/WebApplicationSample/WebApplicationSample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /samples/WebApplicationSample/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | } 3 | -------------------------------------------------------------------------------- /samples/WebApplicationSample/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "MinimumLevel": { 4 | "Default": "Information", 5 | "Override": { 6 | "Microsoft.AspNetCore.Hosting": "Information", 7 | "Microsoft.AspNetCore.Mvc": "Warning", 8 | "Microsoft.AspNetCore.Routing": "Warning" 9 | } 10 | }, 11 | "WriteTo": [ 12 | { 13 | "Name": "File", 14 | "Args": { "path": "./logs/log-.txt", "rollingInterval": "Day" } 15 | } 16 | ] 17 | }, 18 | "AllowedHosts": "*" 19 | } 20 | -------------------------------------------------------------------------------- /serilog-extensions-hosting.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26730.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A1893BD1-333D-4DFE-A0F0-DDBB2FE526E0}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E30F638E-BBBE-4AD1-93CE-48CC69CFEFE1}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{F2407211-6043-439C-8E06-3641634332E7}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{9C21B9DF-AEDD-4AA6-BEA4-912DEF3E5B8E}" 13 | ProjectSection(SolutionItems) = preProject 14 | README.md = README.md 15 | assets\Serilog.snk = assets\Serilog.snk 16 | Build.ps1 = Build.ps1 17 | global.json = global.json 18 | Directory.Version.props = Directory.Version.props 19 | Directory.Build.props = Directory.Build.props 20 | EndProjectSection 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Extensions.Hosting", "src\Serilog.Extensions.Hosting\Serilog.Extensions.Hosting.csproj", "{0549D23F-986B-4FB2-BACE-16FD7A7BC9EF}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Extensions.Hosting.Tests", "test\Serilog.Extensions.Hosting.Tests\Serilog.Extensions.Hosting.Tests.csproj", "{AD51759B-CD58-473F-9620-0B0E56A123A1}" 25 | EndProject 26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleServiceSample", "samples\SimpleServiceSample\SimpleServiceSample.csproj", "{E5A82756-4619-4E6B-8B26-6D83E00E99F0}" 27 | EndProject 28 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApplicationSample", "samples\WebApplicationSample\WebApplicationSample.csproj", "{1ACDCA67-F404-45AB-9348-98E55E03CB8C}" 29 | EndProject 30 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{5C68E1CE-D650-4500-B32C-21EDD0AAA0A7}" 31 | EndProject 32 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{10168223-6AEF-4B08-B6FD-50C6FD3B6E84}" 33 | ProjectSection(SolutionItems) = preProject 34 | .github\workflows\ci.yml = .github\workflows\ci.yml 35 | EndProjectSection 36 | EndProject 37 | Global 38 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 39 | Debug|Any CPU = Debug|Any CPU 40 | Release|Any CPU = Release|Any CPU 41 | EndGlobalSection 42 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 43 | {0549D23F-986B-4FB2-BACE-16FD7A7BC9EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {0549D23F-986B-4FB2-BACE-16FD7A7BC9EF}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {0549D23F-986B-4FB2-BACE-16FD7A7BC9EF}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {0549D23F-986B-4FB2-BACE-16FD7A7BC9EF}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {AD51759B-CD58-473F-9620-0B0E56A123A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {AD51759B-CD58-473F-9620-0B0E56A123A1}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {AD51759B-CD58-473F-9620-0B0E56A123A1}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {AD51759B-CD58-473F-9620-0B0E56A123A1}.Release|Any CPU.Build.0 = Release|Any CPU 51 | {E5A82756-4619-4E6B-8B26-6D83E00E99F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {E5A82756-4619-4E6B-8B26-6D83E00E99F0}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {E5A82756-4619-4E6B-8B26-6D83E00E99F0}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {E5A82756-4619-4E6B-8B26-6D83E00E99F0}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {1ACDCA67-F404-45AB-9348-98E55E03CB8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {1ACDCA67-F404-45AB-9348-98E55E03CB8C}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {1ACDCA67-F404-45AB-9348-98E55E03CB8C}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {1ACDCA67-F404-45AB-9348-98E55E03CB8C}.Release|Any CPU.Build.0 = Release|Any CPU 59 | EndGlobalSection 60 | GlobalSection(SolutionProperties) = preSolution 61 | HideSolutionNode = FALSE 62 | EndGlobalSection 63 | GlobalSection(NestedProjects) = preSolution 64 | {0549D23F-986B-4FB2-BACE-16FD7A7BC9EF} = {A1893BD1-333D-4DFE-A0F0-DDBB2FE526E0} 65 | {AD51759B-CD58-473F-9620-0B0E56A123A1} = {E30F638E-BBBE-4AD1-93CE-48CC69CFEFE1} 66 | {E5A82756-4619-4E6B-8B26-6D83E00E99F0} = {F2407211-6043-439C-8E06-3641634332E7} 67 | {1ACDCA67-F404-45AB-9348-98E55E03CB8C} = {F2407211-6043-439C-8E06-3641634332E7} 68 | {10168223-6AEF-4B08-B6FD-50C6FD3B6E84} = {5C68E1CE-D650-4500-B32C-21EDD0AAA0A7} 69 | EndGlobalSection 70 | GlobalSection(ExtensibilityGlobals) = postSolution 71 | SolutionGuid = {811E61C5-3871-4633-AFAE-B35B619C8A10} 72 | EndGlobalSection 73 | EndGlobal 74 | -------------------------------------------------------------------------------- /serilog-extensions-hosting.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True 5 | True 6 | True 7 | True -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/Extensions/Hosting/AmbientDiagnosticContextCollector.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | namespace Serilog.Extensions.Hosting; 16 | 17 | class AmbientDiagnosticContextCollector : IDisposable 18 | { 19 | static readonly AsyncLocal AmbientCollector = new(); 20 | 21 | // The indirection here ensures that completing collection cleans up the collector in all 22 | // execution contexts. Via @benaadams' addition to `HttpContextAccessor` :-) 23 | DiagnosticContextCollector? _collector; 24 | 25 | public static DiagnosticContextCollector? Current => AmbientCollector.Value?._collector; 26 | 27 | public static DiagnosticContextCollector Begin() 28 | { 29 | var value = new AmbientDiagnosticContextCollector(); 30 | value._collector = new DiagnosticContextCollector(value); 31 | AmbientCollector.Value = value; 32 | return value._collector; 33 | } 34 | 35 | public void Dispose() 36 | { 37 | _collector = null; 38 | if (AmbientCollector.Value == this) 39 | AmbientCollector.Value = null; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/Extensions/Hosting/CachingReloadableLogger.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 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.Diagnostics.CodeAnalysis; 16 | using Serilog.Core; 17 | using Serilog.Events; 18 | 19 | namespace Serilog.Extensions.Hosting; 20 | 21 | class CachingReloadableLogger : LoggerBase, ILogger, IReloadableLogger 22 | { 23 | readonly ReloadableLogger _reloadableLogger; 24 | readonly Func _configure; 25 | readonly IReloadableLogger _parent; 26 | 27 | ILogger _root; 28 | ILogger? _cached; 29 | bool _frozen; 30 | 31 | public CachingReloadableLogger(ReloadableLogger reloadableLogger, ILogger root, IReloadableLogger parent, Func configure) 32 | { 33 | _reloadableLogger = reloadableLogger; 34 | _parent = parent; 35 | _configure = configure; 36 | _root = root; 37 | _cached = null; 38 | _frozen = false; 39 | } 40 | 41 | public ILogger ReloadLogger() 42 | { 43 | return _configure(_parent.ReloadLogger()); 44 | } 45 | 46 | public ILogger ForContext(ILogEventEnricher enricher) 47 | { 48 | if (enricher == null!) return this; 49 | 50 | if (_frozen) 51 | return _cached!.ForContext(enricher); 52 | 53 | if (_reloadableLogger.CreateChild( 54 | _root, 55 | this, 56 | _cached, 57 | p => p.ForContext(enricher), 58 | out var child, 59 | out var newRoot, 60 | out var newCached, 61 | out var frozen)) 62 | { 63 | Update(newRoot, newCached, frozen); 64 | } 65 | 66 | return child; 67 | } 68 | 69 | public ILogger ForContext(IEnumerable enrichers) 70 | { 71 | if (enrichers == null!) return this; 72 | 73 | if (_frozen) 74 | return _cached!.ForContext(enrichers); 75 | 76 | if (_reloadableLogger.CreateChild( 77 | _root, 78 | this, 79 | _cached, 80 | p => p.ForContext(enrichers), 81 | out var child, 82 | out var newRoot, 83 | out var newCached, 84 | out var frozen)) 85 | { 86 | Update(newRoot, newCached, frozen); 87 | } 88 | 89 | return child; 90 | } 91 | 92 | public ILogger ForContext(string propertyName, object? value, bool destructureObjects = false) 93 | { 94 | if (propertyName == null!) return this; 95 | 96 | if (_frozen) 97 | return _cached!.ForContext(propertyName, value, destructureObjects); 98 | 99 | ILogger child; 100 | if (value == null || value is string || value.GetType().IsPrimitive || value.GetType().IsEnum) 101 | { 102 | // Safe to extend the lifetime of `value` by closing over it. 103 | // This ensures `SourceContext` is passed through appropriately and triggers minimum level overrides. 104 | if (_reloadableLogger.CreateChild( 105 | _root, 106 | this, 107 | _cached, 108 | p => p.ForContext(propertyName, value, destructureObjects), 109 | out child, 110 | out var newRoot, 111 | out var newCached, 112 | out var frozen)) 113 | { 114 | Update(newRoot, newCached, frozen); 115 | } 116 | } 117 | else 118 | { 119 | // It's not safe to extend the lifetime of `value` or pass it unexpectedly between threads. 120 | // Changes to destructuring configuration won't be picked up by the cached logger. 121 | var eager = ReloadLogger(); 122 | if (!eager.BindProperty(propertyName, value, destructureObjects, out var property)) 123 | return this; 124 | 125 | var enricher = new FixedPropertyEnricher(property); 126 | 127 | if (_reloadableLogger.CreateChild( 128 | _root, 129 | this, 130 | _cached, 131 | p => p.ForContext(enricher), 132 | out child, 133 | out var newRoot, 134 | out var newCached, 135 | out var frozen)) 136 | { 137 | Update(newRoot, newCached, frozen); 138 | } 139 | } 140 | 141 | return child; 142 | } 143 | 144 | public ILogger ForContext() 145 | { 146 | if (_frozen) 147 | return _cached!.ForContext(); 148 | 149 | if (_reloadableLogger.CreateChild( 150 | _root, 151 | this, 152 | _cached, 153 | p => p.ForContext(), 154 | out var child, 155 | out var newRoot, 156 | out var newCached, 157 | out var frozen)) 158 | { 159 | Update(newRoot, newCached, frozen); 160 | } 161 | 162 | return child; 163 | } 164 | 165 | public ILogger ForContext(Type source) 166 | { 167 | if (_frozen) 168 | return _cached!.ForContext(source); 169 | 170 | if (_reloadableLogger.CreateChild( 171 | _root, 172 | this, 173 | _cached, 174 | p => p.ForContext(source), 175 | out var child, 176 | out var newRoot, 177 | out var newCached, 178 | out var frozen)) 179 | { 180 | Update(newRoot, newCached, frozen); 181 | } 182 | 183 | return child; 184 | } 185 | 186 | void Update(ILogger newRoot, ILogger? newCached, bool frozen) 187 | { 188 | _root = newRoot; 189 | _cached = newCached; 190 | 191 | // https://github.com/dotnet/runtime/issues/20500#issuecomment-284774431 192 | // Publish `_cached` and then `_frozen`. This is useful here because it means that once the logger is frozen - which 193 | // we always expect - reads don't require any synchronization/interlocked instructions. 194 | #if FEATURE_MBPW 195 | Interlocked.MemoryBarrierProcessWide(); 196 | #else 197 | Thread.MemoryBarrier(); 198 | #endif 199 | _frozen = frozen; 200 | 201 | #if FEATURE_MBPW 202 | Interlocked.MemoryBarrierProcessWide(); 203 | #else 204 | Thread.MemoryBarrier(); 205 | #endif 206 | } 207 | 208 | public override void Write(LogEvent logEvent) 209 | { 210 | if (_frozen) 211 | { 212 | _cached!.Write(logEvent); 213 | return; 214 | } 215 | 216 | if (_reloadableLogger.InvokeWrite( 217 | _root, 218 | _cached, 219 | this, 220 | logEvent, 221 | out var newRoot, 222 | out var newCached, 223 | out var frozen)) 224 | { 225 | Update(newRoot, newCached, frozen); 226 | } 227 | } 228 | 229 | public override void Write(LogEventLevel level, string messageTemplate) 230 | { 231 | if (_frozen) 232 | { 233 | _cached!.Write(level, messageTemplate); 234 | return; 235 | } 236 | 237 | if (_reloadableLogger.InvokeWrite( 238 | _root, 239 | _cached, 240 | this, 241 | level, 242 | messageTemplate, 243 | out var newRoot, 244 | out var newCached, 245 | out var frozen)) 246 | { 247 | Update(newRoot, newCached, frozen); 248 | } 249 | } 250 | 251 | public override void Write(LogEventLevel level, string messageTemplate, T propertyValue) 252 | { 253 | if (_frozen) 254 | { 255 | _cached!.Write(level, messageTemplate, propertyValue); 256 | return; 257 | } 258 | 259 | if (_reloadableLogger.InvokeWrite( 260 | _root, 261 | _cached, 262 | this, 263 | level, 264 | messageTemplate, 265 | propertyValue, 266 | out var newRoot, 267 | out var newCached, 268 | out var frozen)) 269 | { 270 | Update(newRoot, newCached, frozen); 271 | } 272 | } 273 | 274 | public override void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1) 275 | { 276 | if (_frozen) 277 | { 278 | _cached!.Write(level, messageTemplate, propertyValue0, propertyValue1); 279 | return; 280 | } 281 | 282 | if (_reloadableLogger.InvokeWrite( 283 | _root, 284 | _cached, 285 | this, 286 | level, 287 | messageTemplate, 288 | propertyValue0, 289 | propertyValue1, 290 | out var newRoot, 291 | out var newCached, 292 | out var frozen)) 293 | { 294 | Update(newRoot, newCached, frozen); 295 | } 296 | } 297 | 298 | public override void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1, 299 | T2 propertyValue2) 300 | { 301 | if (_frozen) 302 | { 303 | _cached!.Write(level, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 304 | return; 305 | } 306 | 307 | if (_reloadableLogger.InvokeWrite( 308 | _root, 309 | _cached, 310 | this, 311 | level, 312 | messageTemplate, 313 | propertyValue0, 314 | propertyValue1, 315 | propertyValue2, 316 | out var newRoot, 317 | out var newCached, 318 | out var frozen)) 319 | { 320 | Update(newRoot, newCached, frozen); 321 | } 322 | } 323 | 324 | public override void Write(LogEventLevel level, string messageTemplate, params object?[]? propertyValues) 325 | { 326 | if (_frozen) 327 | { 328 | _cached!.Write(level, messageTemplate, propertyValues); 329 | return; 330 | } 331 | 332 | if (_reloadableLogger.InvokeWrite( 333 | _root, 334 | _cached, 335 | this, 336 | level, 337 | messageTemplate, 338 | propertyValues, 339 | out var newRoot, 340 | out var newCached, 341 | out var frozen)) 342 | { 343 | Update(newRoot, newCached, frozen); 344 | } 345 | } 346 | 347 | public override void Write(LogEventLevel level, Exception? exception, string messageTemplate) 348 | { 349 | if (_frozen) 350 | { 351 | _cached!.Write(level, exception, messageTemplate); 352 | return; 353 | } 354 | 355 | if (_reloadableLogger.InvokeWrite( 356 | _root, 357 | _cached, 358 | this, 359 | level, 360 | exception, 361 | messageTemplate, 362 | out var newRoot, 363 | out var newCached, 364 | out var frozen)) 365 | { 366 | Update(newRoot, newCached, frozen); 367 | } 368 | } 369 | 370 | public override void Write(LogEventLevel level, Exception? exception, string messageTemplate, T propertyValue) 371 | { 372 | if (_frozen) 373 | { 374 | _cached!.Write(level, exception, messageTemplate, propertyValue); 375 | return; 376 | } 377 | 378 | if (_reloadableLogger.InvokeWrite( 379 | _root, 380 | _cached, 381 | this, 382 | level, 383 | exception, 384 | messageTemplate, 385 | propertyValue, 386 | out var newRoot, 387 | out var newCached, 388 | out var frozen)) 389 | { 390 | Update(newRoot, newCached, frozen); 391 | } 392 | } 393 | 394 | public override void Write(LogEventLevel level, Exception? exception, string messageTemplate, T0 propertyValue0, 395 | T1 propertyValue1) 396 | { 397 | if (_frozen) 398 | { 399 | _cached!.Write(level, exception, messageTemplate, propertyValue0, propertyValue1); 400 | return; 401 | } 402 | 403 | if (_reloadableLogger.InvokeWrite( 404 | _root, 405 | _cached, 406 | this, 407 | level, 408 | exception, 409 | messageTemplate, 410 | propertyValue0, 411 | propertyValue1, 412 | out var newRoot, 413 | out var newCached, 414 | out var frozen)) 415 | { 416 | Update(newRoot, newCached, frozen); 417 | } 418 | } 419 | 420 | public override void Write(LogEventLevel level, Exception? exception, string messageTemplate, T0 propertyValue0, 421 | T1 propertyValue1, T2 propertyValue2) 422 | { 423 | if (_frozen) 424 | { 425 | _cached!.Write(level, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 426 | return; 427 | } 428 | 429 | if (_reloadableLogger.InvokeWrite( 430 | _root, 431 | _cached, 432 | this, 433 | level, 434 | exception, 435 | messageTemplate, 436 | propertyValue0, 437 | propertyValue1, 438 | propertyValue2, 439 | out var newRoot, 440 | out var newCached, 441 | out var frozen)) 442 | { 443 | Update(newRoot, newCached, frozen); 444 | } 445 | } 446 | 447 | public override void Write(LogEventLevel level, Exception? exception, string messageTemplate, params object?[]? propertyValues) 448 | { 449 | if (_frozen) 450 | { 451 | _cached!.Write(level, exception, messageTemplate, propertyValues); 452 | return; 453 | } 454 | 455 | if (_reloadableLogger.InvokeWrite( 456 | _root, 457 | _cached, 458 | this, 459 | level, 460 | exception, 461 | messageTemplate, 462 | propertyValues, 463 | out var newRoot, 464 | out var newCached, 465 | out var frozen)) 466 | { 467 | Update(newRoot, newCached, frozen); 468 | } 469 | } 470 | 471 | public bool IsEnabled(LogEventLevel level) 472 | { 473 | if (_frozen) 474 | { 475 | return _cached!.IsEnabled(level); 476 | } 477 | 478 | if (_reloadableLogger.InvokeIsEnabled( 479 | _root, 480 | _cached, 481 | this, 482 | level, 483 | out var isEnabled, 484 | out var newRoot, 485 | out var newCached, 486 | out var frozen)) 487 | { 488 | Update(newRoot, newCached, frozen); 489 | } 490 | 491 | return isEnabled; 492 | } 493 | 494 | public bool BindMessageTemplate(string messageTemplate, object?[]? propertyValues, 495 | [NotNullWhen(true)] 496 | out MessageTemplate? parsedTemplate, 497 | [NotNullWhen(true)] 498 | out IEnumerable? boundProperties) 499 | { 500 | if (_frozen) 501 | { 502 | return _cached!.BindMessageTemplate(messageTemplate, propertyValues, out parsedTemplate, out boundProperties); 503 | } 504 | 505 | if (_reloadableLogger.InvokeBindMessageTemplate( 506 | _root, 507 | _cached, 508 | this, 509 | messageTemplate, 510 | propertyValues, 511 | out parsedTemplate, 512 | out boundProperties, 513 | out var canBind, 514 | out var newRoot, 515 | out var newCached, 516 | out var frozen)) 517 | { 518 | Update(newRoot, newCached, frozen); 519 | } 520 | 521 | return canBind; 522 | } 523 | 524 | public bool BindProperty(string? propertyName, object? value, bool destructureObjects, 525 | [NotNullWhen(true)] 526 | out LogEventProperty? property) 527 | { 528 | if (_frozen) 529 | { 530 | return _cached!.BindProperty(propertyName, value, destructureObjects, out property); 531 | } 532 | 533 | if (_reloadableLogger.InvokeBindProperty( 534 | _root, 535 | _cached, 536 | this, 537 | propertyName, 538 | value, 539 | destructureObjects, 540 | out property, 541 | out var canBind, 542 | out var newRoot, 543 | out var newCached, 544 | out var frozen)) 545 | { 546 | Update(newRoot, newCached, frozen); 547 | } 548 | 549 | return canBind; 550 | } 551 | } 552 | -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/Extensions/Hosting/DiagnosticContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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; 16 | using System.Threading; 17 | 18 | namespace Serilog.Extensions.Hosting; 19 | 20 | /// 21 | /// Implements an ambient diagnostic context using . 22 | /// 23 | /// Consumers should use to set context properties. 24 | public sealed class DiagnosticContext : IDiagnosticContext 25 | { 26 | readonly ILogger? _logger; 27 | 28 | /// 29 | /// Construct a . 30 | /// 31 | /// A logger for binding properties in the context, or null to use . 32 | public DiagnosticContext(ILogger? logger) 33 | { 34 | _logger = logger; 35 | } 36 | 37 | /// 38 | /// Start collecting properties to associate with the current diagnostic context. This will replace 39 | /// the active collector, if any. 40 | /// 41 | /// A collector that will receive properties added in the current diagnostic context. 42 | public DiagnosticContextCollector BeginCollection() 43 | { 44 | return AmbientDiagnosticContextCollector.Begin(); 45 | } 46 | 47 | /// 48 | public void Set(string propertyName, object? value, bool destructureObjects = false) 49 | { 50 | if (propertyName == null) throw new ArgumentNullException(nameof(propertyName)); 51 | 52 | var collector = AmbientDiagnosticContextCollector.Current; 53 | if (collector != null && 54 | (_logger ?? Log.Logger).BindProperty(propertyName, value, destructureObjects, out var property)) 55 | { 56 | collector.AddOrUpdate(property); 57 | } 58 | } 59 | 60 | /// 61 | public void SetException(Exception exception) 62 | { 63 | var collector = AmbientDiagnosticContextCollector.Current; 64 | collector?.SetException(exception); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/Extensions/Hosting/DiagnosticContextCollector.cs: -------------------------------------------------------------------------------- 1 | using Serilog.Events; 2 | 3 | namespace Serilog.Extensions.Hosting; 4 | 5 | /// 6 | /// A container that receives properties added to a diagnostic context. 7 | /// 8 | public sealed class DiagnosticContextCollector : IDisposable 9 | { 10 | readonly IDisposable _chainedDisposable; 11 | readonly object _propertiesLock = new(); 12 | Exception? _exception; 13 | Dictionary? _properties = new(); 14 | 15 | /// 16 | /// Construct a . 17 | /// 18 | /// An object that will be disposed to signal completion/disposal of 19 | /// the collector. 20 | public DiagnosticContextCollector(IDisposable chainedDisposable) 21 | { 22 | _chainedDisposable = chainedDisposable ?? throw new ArgumentNullException(nameof(chainedDisposable)); 23 | } 24 | 25 | /// 26 | /// Add the property to the context. 27 | /// 28 | /// The property to add. 29 | public void AddOrUpdate(LogEventProperty property) 30 | { 31 | if (property == null) throw new ArgumentNullException(nameof(property)); 32 | 33 | lock (_propertiesLock) 34 | { 35 | if (_properties == null) return; 36 | _properties[property.Name] = property; 37 | } 38 | } 39 | 40 | /// 41 | /// Set the exception associated with the current diagnostic context. 42 | /// 43 | /// 44 | /// Passing an exception to the diagnostic context is useful when unhandled exceptions are handled before reaching Serilog's 45 | /// RequestLoggingMiddleware. One example is using https://www.nuget.org/packages/Hellang.Middleware.ProblemDetails to transform 46 | /// exceptions to ProblemDetails responses. 47 | /// 48 | /// 49 | /// If an unhandled exception reaches Serilog's RequestLoggingMiddleware, then the unhandled exception takes precedence.
50 | /// If null is given, it clears any previously assigned exception. 51 | ///
52 | /// The exception to log. 53 | public void SetException(Exception exception) 54 | { 55 | lock (_propertiesLock) 56 | { 57 | if (_properties == null) return; 58 | _exception = exception; 59 | } 60 | } 61 | 62 | /// 63 | /// Complete the context and retrieve the properties added to it, if any. This will 64 | /// stop collection and remove the collector from the original execution context and 65 | /// any of its children. 66 | /// 67 | /// The collected properties, or null if no collection is active. 68 | /// True if properties could be collected. 69 | /// 70 | [Obsolete("Replaced by TryComplete(out IEnumerable properties, out Exception exception).")] 71 | public bool TryComplete(out IEnumerable? properties) 72 | { 73 | return TryComplete(out properties, out _); 74 | } 75 | 76 | /// 77 | /// Complete the context and retrieve the properties and exception added to it, if any. This will 78 | /// stop collection and remove the collector from the original execution context and 79 | /// any of its children. 80 | /// 81 | /// The collected properties, or null if no collection is active. 82 | /// The collected exception, or null if none has been collected or if no collection is active. 83 | /// True if properties could be collected. 84 | /// 85 | /// 86 | public bool TryComplete(out IEnumerable? properties, out Exception? exception) 87 | { 88 | lock (_propertiesLock) 89 | { 90 | properties = _properties?.Values; 91 | exception = _exception; 92 | _properties = null; 93 | _exception = null; 94 | Dispose(); 95 | return properties != null; 96 | } 97 | } 98 | 99 | /// 100 | public void Dispose() 101 | { 102 | _chainedDisposable.Dispose(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/Extensions/Hosting/FixedPropertyEnricher.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 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 Serilog.Core; 16 | using Serilog.Events; 17 | 18 | namespace Serilog.Extensions.Hosting; 19 | 20 | class FixedPropertyEnricher : ILogEventEnricher 21 | { 22 | readonly LogEventProperty _property; 23 | 24 | public FixedPropertyEnricher(LogEventProperty property) 25 | { 26 | _property = property; 27 | } 28 | 29 | public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) 30 | { 31 | logEvent.AddPropertyIfAbsent(_property); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/Extensions/Hosting/IReloadableLogger.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 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 | namespace Serilog.Extensions.Hosting; 16 | 17 | interface IReloadableLogger 18 | { 19 | ILogger ReloadLogger(); 20 | } -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/Extensions/Hosting/InjectedLoggerSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Serilog.Configuration; 4 | using Serilog.Core; 5 | 6 | namespace Serilog.Extensions.Hosting; 7 | 8 | class InjectedLoggerSettings : ILoggerSettings 9 | { 10 | readonly IServiceProvider _services; 11 | 12 | public InjectedLoggerSettings(IServiceProvider services) 13 | { 14 | _services = services ?? throw new ArgumentNullException(nameof(services)); 15 | } 16 | 17 | public void Configure(LoggerConfiguration loggerConfiguration) 18 | { 19 | var levelSwitch = _services.GetService(); 20 | if (levelSwitch != null) 21 | loggerConfiguration.MinimumLevel.ControlledBy(levelSwitch); 22 | 23 | foreach (var settings in _services.GetServices()) 24 | loggerConfiguration.ReadFrom.Settings(settings); 25 | 26 | foreach (var policy in _services.GetServices()) 27 | loggerConfiguration.Destructure.With(policy); 28 | 29 | foreach (var enricher in _services.GetServices()) 30 | loggerConfiguration.Enrich.With(enricher); 31 | 32 | foreach (var filter in _services.GetServices()) 33 | loggerConfiguration.Filter.With(filter); 34 | 35 | foreach (var sink in _services.GetServices()) 36 | loggerConfiguration.WriteTo.Sink(sink); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/Extensions/Hosting/LoggerBase.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 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 Serilog.Core; 16 | using Serilog.Events; 17 | 18 | namespace Serilog.Extensions.Hosting; 19 | 20 | /// 21 | /// Implements default methods for caching/reloadable loggers. 22 | /// 23 | public abstract class LoggerBase 24 | { 25 | static readonly object[] NoPropertyValues = []; 26 | 27 | internal LoggerBase() 28 | { 29 | } 30 | 31 | /// 32 | /// Write an event to the log. 33 | /// 34 | /// The event to write. 35 | public abstract void Write(LogEvent logEvent); 36 | 37 | /// 38 | /// Write a log event with the specified level. 39 | /// 40 | /// The level of the event. 41 | /// Message template describing the event. 42 | [MessageTemplateFormatMethod("messageTemplate")] 43 | public abstract void Write(LogEventLevel level, string messageTemplate); 44 | 45 | /// 46 | /// Write a log event with the specified level. 47 | /// 48 | /// The level of the event. 49 | /// Message template describing the event. 50 | /// Object positionally formatted into the message template. 51 | [MessageTemplateFormatMethod("messageTemplate")] 52 | public abstract void Write(LogEventLevel level, string messageTemplate, T propertyValue); 53 | 54 | /// 55 | /// Write a log event with the specified level. 56 | /// 57 | /// The level of the event. 58 | /// Message template describing the event. 59 | /// Object positionally formatted into the message template. 60 | /// Object positionally formatted into the message template. 61 | [MessageTemplateFormatMethod("messageTemplate")] 62 | public abstract void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1); 63 | 64 | /// 65 | /// Write a log event with the specified level. 66 | /// 67 | /// The level of the event. 68 | /// Message template describing the event. 69 | /// Object positionally formatted into the message template. 70 | /// Object positionally formatted into the message template. 71 | /// Object positionally formatted into the message template. 72 | [MessageTemplateFormatMethod("messageTemplate")] 73 | public abstract void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1, 74 | T2 propertyValue2); 75 | 76 | /// 77 | /// Write a log event with the specified level. 78 | /// 79 | /// The level of the event. 80 | /// 81 | /// 82 | [MessageTemplateFormatMethod("messageTemplate")] 83 | public abstract void Write(LogEventLevel level, string messageTemplate, params object?[]? propertyValues); 84 | 85 | /// 86 | /// Write a log event with the specified level and associated exception. 87 | /// 88 | /// The level of the event. 89 | /// Exception related to the event. 90 | /// Message template describing the event. 91 | [MessageTemplateFormatMethod("messageTemplate")] 92 | public abstract void Write(LogEventLevel level, Exception? exception, string messageTemplate); 93 | 94 | /// 95 | /// Write a log event with the specified level and associated exception. 96 | /// 97 | /// The level of the event. 98 | /// Exception related to the event. 99 | /// Message template describing the event. 100 | /// Object positionally formatted into the message template. 101 | [MessageTemplateFormatMethod("messageTemplate")] 102 | public abstract void Write(LogEventLevel level, Exception? exception, string messageTemplate, T propertyValue); 103 | 104 | /// 105 | /// Write a log event with the specified level and associated exception. 106 | /// 107 | /// The level of the event. 108 | /// Exception related to the event. 109 | /// Message template describing the event. 110 | /// Object positionally formatted into the message template. 111 | /// Object positionally formatted into the message template. 112 | [MessageTemplateFormatMethod("messageTemplate")] 113 | public abstract void Write(LogEventLevel level, Exception? exception, string messageTemplate, T0 propertyValue0, 114 | T1 propertyValue1); 115 | 116 | /// 117 | /// Write a log event with the specified level and associated exception. 118 | /// 119 | /// The level of the event. 120 | /// Exception related to the event. 121 | /// Message template describing the event. 122 | /// Object positionally formatted into the message template. 123 | /// Object positionally formatted into the message template. 124 | /// Object positionally formatted into the message template. 125 | [MessageTemplateFormatMethod("messageTemplate")] 126 | public abstract void Write(LogEventLevel level, Exception? exception, string messageTemplate, 127 | T0 propertyValue0, T1 propertyValue1, T2 propertyValue2); 128 | 129 | /// 130 | /// Write a log event with the specified level and associated exception. 131 | /// 132 | /// The level of the event. 133 | /// Exception related to the event. 134 | /// Message template describing the event. 135 | /// Objects positionally formatted into the message template. 136 | [MessageTemplateFormatMethod("messageTemplate")] 137 | public abstract void Write(LogEventLevel level, Exception? exception, string messageTemplate, params object?[]? propertyValues); 138 | 139 | /// 140 | /// Write a log event with the level. 141 | /// 142 | /// Message template describing the event. 143 | /// 144 | /// Log.Verbose("Staring into space, wondering if we're alone."); 145 | /// 146 | [MessageTemplateFormatMethod("messageTemplate")] 147 | public void Verbose(string messageTemplate) 148 | => Write(LogEventLevel.Verbose, messageTemplate, NoPropertyValues); 149 | 150 | /// 151 | /// Write a log event with the level. 152 | /// 153 | /// Message template describing the event. 154 | /// Object positionally formatted into the message template. 155 | /// 156 | /// Log.Verbose("Staring into space, wondering if we're alone."); 157 | /// 158 | [MessageTemplateFormatMethod("messageTemplate")] 159 | public void Verbose(string messageTemplate, T propertyValue) 160 | => Write(LogEventLevel.Verbose, messageTemplate, propertyValue); 161 | 162 | /// 163 | /// Write a log event with the level. 164 | /// 165 | /// Message template describing the event. 166 | /// Object positionally formatted into the message template. 167 | /// Object positionally formatted into the message template. 168 | /// 169 | /// Log.Verbose("Staring into space, wondering if we're alone."); 170 | /// 171 | [MessageTemplateFormatMethod("messageTemplate")] 172 | public void Verbose(string messageTemplate, T0 propertyValue0, T1 propertyValue1) 173 | => Write(LogEventLevel.Verbose, messageTemplate, propertyValue0, propertyValue1); 174 | 175 | /// 176 | /// Write a log event with the level. 177 | /// 178 | /// Message template describing the event. 179 | /// Object positionally formatted into the message template. 180 | /// Object positionally formatted into the message template. 181 | /// Object positionally formatted into the message template. 182 | /// 183 | /// Log.Verbose("Staring into space, wondering if we're alone."); 184 | /// 185 | [MessageTemplateFormatMethod("messageTemplate")] 186 | public void Verbose(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) 187 | => Write(LogEventLevel.Verbose, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 188 | 189 | /// 190 | /// Write a log event with the level and associated exception. 191 | /// 192 | /// Message template describing the event. 193 | /// Objects positionally formatted into the message template. 194 | /// 195 | /// Log.Verbose("Staring into space, wondering if we're alone."); 196 | /// 197 | [MessageTemplateFormatMethod("messageTemplate")] 198 | public void Verbose(string messageTemplate, params object?[]? propertyValues) 199 | => Verbose((Exception?)null, messageTemplate, propertyValues); 200 | 201 | /// 202 | /// Write a log event with the level and associated exception. 203 | /// 204 | /// Exception related to the event. 205 | /// Message template describing the event. 206 | /// 207 | /// Log.Verbose(ex, "Staring into space, wondering where this comet came from."); 208 | /// 209 | [MessageTemplateFormatMethod("messageTemplate")] 210 | public void Verbose(Exception? exception, string messageTemplate) 211 | => Write(LogEventLevel.Verbose, exception, messageTemplate, NoPropertyValues); 212 | 213 | /// 214 | /// Write a log event with the level and associated exception. 215 | /// 216 | /// Exception related to the event. 217 | /// Message template describing the event. 218 | /// Object positionally formatted into the message template. 219 | /// 220 | /// Log.Verbose(ex, "Staring into space, wondering where this comet came from."); 221 | /// 222 | [MessageTemplateFormatMethod("messageTemplate")] 223 | public void Verbose(Exception? exception, string messageTemplate, T propertyValue) 224 | => Write(LogEventLevel.Verbose, exception, messageTemplate, propertyValue); 225 | 226 | /// 227 | /// Write a log event with the level and associated exception. 228 | /// 229 | /// Exception related to the event. 230 | /// Message template describing the event. 231 | /// Object positionally formatted into the message template. 232 | /// Object positionally formatted into the message template. 233 | /// 234 | /// Log.Verbose(ex, "Staring into space, wondering where this comet came from."); 235 | /// 236 | [MessageTemplateFormatMethod("messageTemplate")] 237 | public void Verbose(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) 238 | => Write(LogEventLevel.Verbose, exception, messageTemplate, propertyValue0, propertyValue1); 239 | 240 | /// 241 | /// Write a log event with the level and associated exception. 242 | /// 243 | /// Exception related to the event. 244 | /// Message template describing the event. 245 | /// Object positionally formatted into the message template. 246 | /// Object positionally formatted into the message template. 247 | /// Object positionally formatted into the message template. 248 | /// 249 | /// Log.Verbose(ex, "Staring into space, wondering where this comet came from."); 250 | /// 251 | [MessageTemplateFormatMethod("messageTemplate")] 252 | public void Verbose(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, 253 | T2 propertyValue2) 254 | => Write(LogEventLevel.Verbose, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 255 | 256 | /// 257 | /// Write a log event with the level and associated exception. 258 | /// 259 | /// Exception related to the event. 260 | /// Message template describing the event. 261 | /// Objects positionally formatted into the message template. 262 | /// 263 | /// Log.Verbose(ex, "Staring into space, wondering where this comet came from."); 264 | /// 265 | [MessageTemplateFormatMethod("messageTemplate")] 266 | public void Verbose(Exception? exception, string messageTemplate, params object?[]? propertyValues) 267 | => Write(LogEventLevel.Verbose, exception, messageTemplate, propertyValues); 268 | 269 | /// 270 | /// Write a log event with the level. 271 | /// 272 | /// Message template describing the event. 273 | /// 274 | /// Log.Debug("Starting up at {StartedAt}.", DateTime.Now); 275 | /// 276 | [MessageTemplateFormatMethod("messageTemplate")] 277 | public void Debug(string messageTemplate) 278 | => Write(LogEventLevel.Debug, messageTemplate, NoPropertyValues); 279 | 280 | /// 281 | /// Write a log event with the level. 282 | /// 283 | /// Message template describing the event. 284 | /// Object positionally formatted into the message template. 285 | /// 286 | /// Log.Debug("Starting up at {StartedAt}.", DateTime.Now); 287 | /// 288 | [MessageTemplateFormatMethod("messageTemplate")] 289 | public void Debug(string messageTemplate, T propertyValue) 290 | => Write(LogEventLevel.Debug, messageTemplate, propertyValue); 291 | 292 | /// 293 | /// Write a log event with the level. 294 | /// 295 | /// Message template describing the event. 296 | /// Object positionally formatted into the message template. 297 | /// Object positionally formatted into the message template. 298 | /// 299 | /// Log.Debug("Starting up at {StartedAt}.", DateTime.Now); 300 | /// 301 | [MessageTemplateFormatMethod("messageTemplate")] 302 | public void Debug(string messageTemplate, T0 propertyValue0, T1 propertyValue1) 303 | => Write(LogEventLevel.Debug, messageTemplate, propertyValue0, propertyValue1); 304 | 305 | /// 306 | /// Write a log event with the level. 307 | /// 308 | /// Message template describing the event. 309 | /// Object positionally formatted into the message template. 310 | /// Object positionally formatted into the message template. 311 | /// Object positionally formatted into the message template. 312 | /// 313 | /// Log.Debug("Starting up at {StartedAt}.", DateTime.Now); 314 | /// 315 | [MessageTemplateFormatMethod("messageTemplate")] 316 | public void Debug(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) 317 | => Write(LogEventLevel.Debug, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 318 | 319 | /// 320 | /// Write a log event with the level and associated exception. 321 | /// 322 | /// Message template describing the event. 323 | /// Objects positionally formatted into the message template. 324 | /// 325 | /// Log.Debug("Starting up at {StartedAt}.", DateTime.Now); 326 | /// 327 | [MessageTemplateFormatMethod("messageTemplate")] 328 | public void Debug(string messageTemplate, params object?[]? propertyValues) 329 | => Debug((Exception?)null, messageTemplate, propertyValues); 330 | 331 | /// 332 | /// Write a log event with the level and associated exception. 333 | /// 334 | /// Exception related to the event. 335 | /// Message template describing the event. 336 | /// 337 | /// Log.Debug(ex, "Swallowing a mundane exception."); 338 | /// 339 | [MessageTemplateFormatMethod("messageTemplate")] 340 | public void Debug(Exception? exception, string messageTemplate) 341 | => Write(LogEventLevel.Debug, exception, messageTemplate, NoPropertyValues); 342 | 343 | /// 344 | /// Write a log event with the level and associated exception. 345 | /// 346 | /// Exception related to the event. 347 | /// Message template describing the event. 348 | /// Object positionally formatted into the message template. 349 | /// 350 | /// Log.Debug(ex, "Swallowing a mundane exception."); 351 | /// 352 | [MessageTemplateFormatMethod("messageTemplate")] 353 | public void Debug(Exception? exception, string messageTemplate, T propertyValue) 354 | => Write(LogEventLevel.Debug, exception, messageTemplate, propertyValue); 355 | 356 | /// 357 | /// Write a log event with the level and associated exception. 358 | /// 359 | /// Exception related to the event. 360 | /// Message template describing the event. 361 | /// Object positionally formatted into the message template. 362 | /// Object positionally formatted into the message template. 363 | /// 364 | /// Log.Debug(ex, "Swallowing a mundane exception."); 365 | /// 366 | [MessageTemplateFormatMethod("messageTemplate")] 367 | public void Debug(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) 368 | => Write(LogEventLevel.Debug, exception, messageTemplate, propertyValue0, propertyValue1); 369 | 370 | /// 371 | /// Write a log event with the level and associated exception. 372 | /// 373 | /// Exception related to the event. 374 | /// Message template describing the event. 375 | /// Object positionally formatted into the message template. 376 | /// Object positionally formatted into the message template. 377 | /// Object positionally formatted into the message template. 378 | /// 379 | /// Log.Debug(ex, "Swallowing a mundane exception."); 380 | /// 381 | [MessageTemplateFormatMethod("messageTemplate")] 382 | public void Debug(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, 383 | T2 propertyValue2) 384 | => Write(LogEventLevel.Debug, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 385 | 386 | /// 387 | /// Write a log event with the level and associated exception. 388 | /// 389 | /// Exception related to the event. 390 | /// Message template describing the event. 391 | /// Objects positionally formatted into the message template. 392 | /// 393 | /// Log.Debug(ex, "Swallowing a mundane exception."); 394 | /// 395 | [MessageTemplateFormatMethod("messageTemplate")] 396 | public void Debug(Exception? exception, string messageTemplate, params object?[]? propertyValues) 397 | => Write(LogEventLevel.Debug, exception, messageTemplate, propertyValues); 398 | 399 | /// 400 | /// Write a log event with the level. 401 | /// 402 | /// Message template describing the event. 403 | /// 404 | /// Log.Information("Processed {RecordCount} records in {TimeMS}.", records.Length, sw.ElapsedMilliseconds); 405 | /// 406 | [MessageTemplateFormatMethod("messageTemplate")] 407 | public void Information(string messageTemplate) 408 | => Write(LogEventLevel.Information, messageTemplate, NoPropertyValues); 409 | 410 | /// 411 | /// Write a log event with the level. 412 | /// 413 | /// Message template describing the event. 414 | /// Object positionally formatted into the message template. 415 | /// 416 | /// Log.Information("Processed {RecordCount} records in {TimeMS}.", records.Length, sw.ElapsedMilliseconds); 417 | /// 418 | [MessageTemplateFormatMethod("messageTemplate")] 419 | public void Information(string messageTemplate, T propertyValue) 420 | => Write(LogEventLevel.Information, messageTemplate, propertyValue); 421 | 422 | /// 423 | /// Write a log event with the level. 424 | /// 425 | /// Message template describing the event. 426 | /// Object positionally formatted into the message template. 427 | /// Object positionally formatted into the message template. 428 | /// 429 | /// Log.Information("Processed {RecordCount} records in {TimeMS}.", records.Length, sw.ElapsedMilliseconds); 430 | /// 431 | [MessageTemplateFormatMethod("messageTemplate")] 432 | public void Information(string messageTemplate, T0 propertyValue0, T1 propertyValue1) 433 | => Write(LogEventLevel.Information, messageTemplate, propertyValue0, propertyValue1); 434 | 435 | /// 436 | /// Write a log event with the level. 437 | /// 438 | /// Message template describing the event. 439 | /// Object positionally formatted into the message template. 440 | /// Object positionally formatted into the message template. 441 | /// Object positionally formatted into the message template. 442 | /// 443 | /// Log.Information("Processed {RecordCount} records in {TimeMS}.", records.Length, sw.ElapsedMilliseconds); 444 | /// 445 | [MessageTemplateFormatMethod("messageTemplate")] 446 | public void Information(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) 447 | => Write(LogEventLevel.Information, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 448 | 449 | /// 450 | /// Write a log event with the level and associated exception. 451 | /// 452 | /// Message template describing the event. 453 | /// Objects positionally formatted into the message template. 454 | /// 455 | /// Log.Information("Processed {RecordCount} records in {TimeMS}.", records.Length, sw.ElapsedMilliseconds); 456 | /// 457 | [MessageTemplateFormatMethod("messageTemplate")] 458 | public void Information(string messageTemplate, params object?[]? propertyValues) 459 | => Information((Exception?)null, messageTemplate, propertyValues); 460 | 461 | /// 462 | /// Write a log event with the level and associated exception. 463 | /// 464 | /// Exception related to the event. 465 | /// Message template describing the event. 466 | /// 467 | /// Log.Information(ex, "Processed {RecordCount} records in {TimeMS}.", records.Length, sw.ElapsedMilliseconds); 468 | /// 469 | [MessageTemplateFormatMethod("messageTemplate")] 470 | public void Information(Exception? exception, string messageTemplate) 471 | => Write(LogEventLevel.Information, exception, messageTemplate, NoPropertyValues); 472 | 473 | /// 474 | /// Write a log event with the level and associated exception. 475 | /// 476 | /// Exception related to the event. 477 | /// Message template describing the event. 478 | /// Object positionally formatted into the message template. 479 | /// 480 | /// Log.Information(ex, "Processed {RecordCount} records in {TimeMS}.", records.Length, sw.ElapsedMilliseconds); 481 | /// 482 | [MessageTemplateFormatMethod("messageTemplate")] 483 | public void Information(Exception? exception, string messageTemplate, T propertyValue) 484 | => Write(LogEventLevel.Information, exception, messageTemplate, propertyValue); 485 | 486 | /// 487 | /// Write a log event with the level and associated exception. 488 | /// 489 | /// Exception related to the event. 490 | /// Message template describing the event. 491 | /// Object positionally formatted into the message template. 492 | /// Object positionally formatted into the message template. 493 | /// 494 | /// Log.Information(ex, "Processed {RecordCount} records in {TimeMS}.", records.Length, sw.ElapsedMilliseconds); 495 | /// 496 | [MessageTemplateFormatMethod("messageTemplate")] 497 | public void Information(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) 498 | => Write(LogEventLevel.Information, exception, messageTemplate, propertyValue0, propertyValue1); 499 | 500 | /// 501 | /// Write a log event with the level and associated exception. 502 | /// 503 | /// Exception related to the event. 504 | /// Message template describing the event. 505 | /// Object positionally formatted into the message template. 506 | /// Object positionally formatted into the message template. 507 | /// Object positionally formatted into the message template. 508 | /// 509 | /// Log.Information(ex, "Processed {RecordCount} records in {TimeMS}.", records.Length, sw.ElapsedMilliseconds); 510 | /// 511 | [MessageTemplateFormatMethod("messageTemplate")] 512 | public void Information(Exception? exception, string messageTemplate, T0 propertyValue0, 513 | T1 propertyValue1, T2 propertyValue2) 514 | => Write(LogEventLevel.Information, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 515 | 516 | /// 517 | /// Write a log event with the level and associated exception. 518 | /// 519 | /// Exception related to the event. 520 | /// Message template describing the event. 521 | /// Objects positionally formatted into the message template. 522 | /// 523 | /// Log.Information(ex, "Processed {RecordCount} records in {TimeMS}.", records.Length, sw.ElapsedMilliseconds); 524 | /// 525 | [MessageTemplateFormatMethod("messageTemplate")] 526 | public void Information(Exception? exception, string messageTemplate, params object?[]? propertyValues) 527 | => Write(LogEventLevel.Information, exception, messageTemplate, propertyValues); 528 | 529 | /// 530 | /// Write a log event with the level. 531 | /// 532 | /// Message template describing the event. 533 | /// 534 | /// Log.Warning("Skipped {SkipCount} records.", skippedRecords.Length); 535 | /// 536 | [MessageTemplateFormatMethod("messageTemplate")] 537 | public void Warning(string messageTemplate) 538 | => Write(LogEventLevel.Warning, messageTemplate, NoPropertyValues); 539 | 540 | /// 541 | /// Write a log event with the level. 542 | /// 543 | /// Message template describing the event. 544 | /// Object positionally formatted into the message template. 545 | /// 546 | /// Log.Warning("Skipped {SkipCount} records.", skippedRecords.Length); 547 | /// 548 | [MessageTemplateFormatMethod("messageTemplate")] 549 | public void Warning(string messageTemplate, T propertyValue) 550 | => Write(LogEventLevel.Warning, messageTemplate, propertyValue); 551 | 552 | /// 553 | /// Write a log event with the level. 554 | /// 555 | /// Message template describing the event. 556 | /// Object positionally formatted into the message template. 557 | /// Object positionally formatted into the message template. 558 | /// 559 | /// Log.Warning("Skipped {SkipCount} records.", skippedRecords.Length); 560 | /// 561 | [MessageTemplateFormatMethod("messageTemplate")] 562 | public void Warning(string messageTemplate, T0 propertyValue0, T1 propertyValue1) 563 | => Write(LogEventLevel.Warning, messageTemplate, propertyValue0, propertyValue1); 564 | 565 | /// 566 | /// Write a log event with the level. 567 | /// 568 | /// Message template describing the event. 569 | /// Object positionally formatted into the message template. 570 | /// Object positionally formatted into the message template. 571 | /// Object positionally formatted into the message template. 572 | /// 573 | /// Log.Warning("Skipped {SkipCount} records.", skippedRecords.Length); 574 | /// 575 | [MessageTemplateFormatMethod("messageTemplate")] 576 | public void Warning(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) 577 | => Write(LogEventLevel.Warning, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 578 | 579 | /// 580 | /// Write a log event with the level and associated exception. 581 | /// 582 | /// Message template describing the event. 583 | /// Objects positionally formatted into the message template. 584 | /// 585 | /// Log.Warning("Skipped {SkipCount} records.", skippedRecords.Length); 586 | /// 587 | [MessageTemplateFormatMethod("messageTemplate")] 588 | public void Warning(string messageTemplate, params object?[]? propertyValues) 589 | => Warning((Exception?)null, messageTemplate, propertyValues); 590 | 591 | /// 592 | /// Write a log event with the level and associated exception. 593 | /// 594 | /// Exception related to the event. 595 | /// Message template describing the event. 596 | /// 597 | /// Log.Warning(ex, "Skipped {SkipCount} records.", skippedRecords.Length); 598 | /// 599 | [MessageTemplateFormatMethod("messageTemplate")] 600 | public void Warning(Exception? exception, string messageTemplate) 601 | => Write(LogEventLevel.Warning, exception, messageTemplate, NoPropertyValues); 602 | 603 | /// 604 | /// Write a log event with the level and associated exception. 605 | /// 606 | /// Exception related to the event. 607 | /// Message template describing the event. 608 | /// Object positionally formatted into the message template. 609 | /// 610 | /// Log.Warning(ex, "Skipped {SkipCount} records.", skippedRecords.Length); 611 | /// 612 | [MessageTemplateFormatMethod("messageTemplate")] 613 | public void Warning(Exception? exception, string messageTemplate, T propertyValue) 614 | => Write(LogEventLevel.Warning, exception, messageTemplate, propertyValue); 615 | 616 | /// 617 | /// Write a log event with the level and associated exception. 618 | /// 619 | /// Exception related to the event. 620 | /// Message template describing the event. 621 | /// Object positionally formatted into the message template. 622 | /// Object positionally formatted into the message template. 623 | /// 624 | /// Log.Warning(ex, "Skipped {SkipCount} records.", skippedRecords.Length); 625 | /// 626 | [MessageTemplateFormatMethod("messageTemplate")] 627 | public void Warning(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) 628 | => Write(LogEventLevel.Warning, exception, messageTemplate, propertyValue0, propertyValue1); 629 | 630 | /// 631 | /// Write a log event with the level and associated exception. 632 | /// 633 | /// Exception related to the event. 634 | /// Message template describing the event. 635 | /// Object positionally formatted into the message template. 636 | /// Object positionally formatted into the message template. 637 | /// Object positionally formatted into the message template. 638 | /// 639 | /// Log.Warning(ex, "Skipped {SkipCount} records.", skippedRecords.Length); 640 | /// 641 | [MessageTemplateFormatMethod("messageTemplate")] 642 | public void Warning(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, 643 | T2 propertyValue2) 644 | => Write(LogEventLevel.Warning, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 645 | 646 | /// 647 | /// Write a log event with the level and associated exception. 648 | /// 649 | /// Exception related to the event. 650 | /// Message template describing the event. 651 | /// Objects positionally formatted into the message template. 652 | /// 653 | /// Log.Warning(ex, "Skipped {SkipCount} records.", skippedRecords.Length); 654 | /// 655 | [MessageTemplateFormatMethod("messageTemplate")] 656 | public void Warning(Exception? exception, string messageTemplate, params object?[]? propertyValues) 657 | => Write(LogEventLevel.Warning, exception, messageTemplate, propertyValues); 658 | 659 | /// 660 | /// Write a log event with the level. 661 | /// 662 | /// Message template describing the event. 663 | /// 664 | /// Log.Error("Failed {ErrorCount} records.", brokenRecords.Length); 665 | /// 666 | [MessageTemplateFormatMethod("messageTemplate")] 667 | public void Error(string messageTemplate) 668 | => Write(LogEventLevel.Error, messageTemplate, NoPropertyValues); 669 | 670 | /// 671 | /// Write a log event with the level. 672 | /// 673 | /// Message template describing the event. 674 | /// Object positionally formatted into the message template. 675 | /// 676 | /// Log.Error("Failed {ErrorCount} records.", brokenRecords.Length); 677 | /// 678 | [MessageTemplateFormatMethod("messageTemplate")] 679 | public void Error(string messageTemplate, T propertyValue) 680 | => Write(LogEventLevel.Error, messageTemplate, propertyValue); 681 | 682 | /// 683 | /// Write a log event with the level. 684 | /// 685 | /// Message template describing the event. 686 | /// Object positionally formatted into the message template. 687 | /// Object positionally formatted into the message template. 688 | /// 689 | /// Log.Error("Failed {ErrorCount} records.", brokenRecords.Length); 690 | /// 691 | [MessageTemplateFormatMethod("messageTemplate")] 692 | public void Error(string messageTemplate, T0 propertyValue0, T1 propertyValue1) 693 | => Write(LogEventLevel.Error, messageTemplate, propertyValue0, propertyValue1); 694 | 695 | /// 696 | /// Write a log event with the level. 697 | /// 698 | /// Message template describing the event. 699 | /// Object positionally formatted into the message template. 700 | /// Object positionally formatted into the message template. 701 | /// Object positionally formatted into the message template. 702 | /// 703 | /// Log.Error("Failed {ErrorCount} records.", brokenRecords.Length); 704 | /// 705 | [MessageTemplateFormatMethod("messageTemplate")] 706 | public void Error(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) 707 | => Write(LogEventLevel.Error, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 708 | 709 | /// 710 | /// Write a log event with the level and associated exception. 711 | /// 712 | /// Message template describing the event. 713 | /// Objects positionally formatted into the message template. 714 | /// 715 | /// Log.Error("Failed {ErrorCount} records.", brokenRecords.Length); 716 | /// 717 | [MessageTemplateFormatMethod("messageTemplate")] 718 | public void Error(string messageTemplate, params object?[]? propertyValues) 719 | => Error((Exception?)null, messageTemplate, propertyValues); 720 | 721 | /// 722 | /// Write a log event with the level and associated exception. 723 | /// 724 | /// Exception related to the event. 725 | /// Message template describing the event. 726 | /// 727 | /// Log.Error(ex, "Failed {ErrorCount} records.", brokenRecords.Length); 728 | /// 729 | [MessageTemplateFormatMethod("messageTemplate")] 730 | public void Error(Exception? exception, string messageTemplate) 731 | => Write(LogEventLevel.Error, exception, messageTemplate, NoPropertyValues); 732 | 733 | /// 734 | /// Write a log event with the level and associated exception. 735 | /// 736 | /// Exception related to the event. 737 | /// Message template describing the event. 738 | /// Object positionally formatted into the message template. 739 | /// 740 | /// Log.Error(ex, "Failed {ErrorCount} records.", brokenRecords.Length); 741 | /// 742 | [MessageTemplateFormatMethod("messageTemplate")] 743 | public void Error(Exception? exception, string messageTemplate, T propertyValue) 744 | => Write(LogEventLevel.Error, exception, messageTemplate, propertyValue); 745 | 746 | /// 747 | /// Write a log event with the level and associated exception. 748 | /// 749 | /// Exception related to the event. 750 | /// Message template describing the event. 751 | /// Object positionally formatted into the message template. 752 | /// Object positionally formatted into the message template. 753 | /// 754 | /// Log.Error(ex, "Failed {ErrorCount} records.", brokenRecords.Length); 755 | /// 756 | [MessageTemplateFormatMethod("messageTemplate")] 757 | public void Error(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) 758 | => Write(LogEventLevel.Error, exception, messageTemplate, propertyValue0, propertyValue1); 759 | 760 | /// 761 | /// Write a log event with the level and associated exception. 762 | /// 763 | /// Exception related to the event. 764 | /// Message template describing the event. 765 | /// Object positionally formatted into the message template. 766 | /// Object positionally formatted into the message template. 767 | /// Object positionally formatted into the message template. 768 | /// 769 | /// Log.Error(ex, "Failed {ErrorCount} records.", brokenRecords.Length); 770 | /// 771 | [MessageTemplateFormatMethod("messageTemplate")] 772 | public void Error(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, 773 | T2 propertyValue2) 774 | => Write(LogEventLevel.Error, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 775 | 776 | /// 777 | /// Write a log event with the level and associated exception. 778 | /// 779 | /// Exception related to the event. 780 | /// Message template describing the event. 781 | /// Objects positionally formatted into the message template. 782 | /// 783 | /// Log.Error(ex, "Failed {ErrorCount} records.", brokenRecords.Length); 784 | /// 785 | [MessageTemplateFormatMethod("messageTemplate")] 786 | public void Error(Exception? exception, string messageTemplate, params object?[]? propertyValues) 787 | => Write(LogEventLevel.Error, exception, messageTemplate, propertyValues); 788 | 789 | /// 790 | /// Write a log event with the level. 791 | /// 792 | /// Message template describing the event. 793 | /// 794 | /// Log.Fatal("Process terminating."); 795 | /// 796 | [MessageTemplateFormatMethod("messageTemplate")] 797 | public void Fatal(string messageTemplate) 798 | => Write(LogEventLevel.Fatal, messageTemplate, NoPropertyValues); 799 | 800 | /// 801 | /// Write a log event with the level. 802 | /// 803 | /// Message template describing the event. 804 | /// Object positionally formatted into the message template. 805 | /// 806 | /// Log.Fatal("Process terminating."); 807 | /// 808 | [MessageTemplateFormatMethod("messageTemplate")] 809 | public void Fatal(string messageTemplate, T propertyValue) 810 | => Write(LogEventLevel.Fatal, messageTemplate, propertyValue); 811 | 812 | /// 813 | /// Write a log event with the level. 814 | /// 815 | /// Message template describing the event. 816 | /// Object positionally formatted into the message template. 817 | /// Object positionally formatted into the message template. 818 | /// 819 | /// Log.Fatal("Process terminating."); 820 | /// 821 | [MessageTemplateFormatMethod("messageTemplate")] 822 | public void Fatal(string messageTemplate, T0 propertyValue0, T1 propertyValue1) 823 | => Write(LogEventLevel.Fatal, messageTemplate, propertyValue0, propertyValue1); 824 | 825 | /// 826 | /// Write a log event with the level. 827 | /// 828 | /// Message template describing the event. 829 | /// Object positionally formatted into the message template. 830 | /// Object positionally formatted into the message template. 831 | /// Object positionally formatted into the message template. 832 | /// 833 | /// Log.Fatal("Process terminating."); 834 | /// 835 | [MessageTemplateFormatMethod("messageTemplate")] 836 | public void Fatal(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) 837 | => Write(LogEventLevel.Fatal, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 838 | 839 | /// 840 | /// Write a log event with the level and associated exception. 841 | /// 842 | /// Message template describing the event. 843 | /// Objects positionally formatted into the message template. 844 | /// 845 | /// Log.Fatal("Process terminating."); 846 | /// 847 | [MessageTemplateFormatMethod("messageTemplate")] 848 | public void Fatal(string messageTemplate, params object?[]? propertyValues) 849 | => Fatal((Exception?)null, messageTemplate, propertyValues); 850 | 851 | /// 852 | /// Write a log event with the level and associated exception. 853 | /// 854 | /// Exception related to the event. 855 | /// Message template describing the event. 856 | /// 857 | /// Log.Fatal(ex, "Process terminating."); 858 | /// 859 | [MessageTemplateFormatMethod("messageTemplate")] 860 | public void Fatal(Exception? exception, string messageTemplate) 861 | => Write(LogEventLevel.Fatal, exception, messageTemplate, NoPropertyValues); 862 | 863 | /// 864 | /// Write a log event with the level and associated exception. 865 | /// 866 | /// Exception related to the event. 867 | /// Message template describing the event. 868 | /// Object positionally formatted into the message template. 869 | /// 870 | /// Log.Fatal(ex, "Process terminating."); 871 | /// 872 | [MessageTemplateFormatMethod("messageTemplate")] 873 | public void Fatal(Exception? exception, string messageTemplate, T propertyValue) 874 | => Write(LogEventLevel.Fatal, exception, messageTemplate, propertyValue); 875 | 876 | /// 877 | /// Write a log event with the level and associated exception. 878 | /// 879 | /// Exception related to the event. 880 | /// Message template describing the event. 881 | /// Object positionally formatted into the message template. 882 | /// Object positionally formatted into the message template. 883 | /// 884 | /// Log.Fatal(ex, "Process terminating."); 885 | /// 886 | [MessageTemplateFormatMethod("messageTemplate")] 887 | public void Fatal(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) 888 | => Write(LogEventLevel.Fatal, exception, messageTemplate, propertyValue0, propertyValue1); 889 | 890 | /// 891 | /// Write a log event with the level and associated exception. 892 | /// 893 | /// Exception related to the event. 894 | /// Message template describing the event. 895 | /// Object positionally formatted into the message template. 896 | /// Object positionally formatted into the message template. 897 | /// Object positionally formatted into the message template. 898 | /// 899 | /// Log.Fatal(ex, "Process terminating."); 900 | /// 901 | [MessageTemplateFormatMethod("messageTemplate")] 902 | public void Fatal(Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, 903 | T2 propertyValue2) 904 | => Write(LogEventLevel.Fatal, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 905 | 906 | /// 907 | /// Write a log event with the level and associated exception. 908 | /// 909 | /// Exception related to the event. 910 | /// Message template describing the event. 911 | /// Objects positionally formatted into the message template. 912 | /// 913 | /// Log.Fatal(ex, "Process terminating."); 914 | /// 915 | [MessageTemplateFormatMethod("messageTemplate")] 916 | public void Fatal(Exception? exception, string messageTemplate, params object?[]? propertyValues) 917 | => Write(LogEventLevel.Fatal, exception, messageTemplate, propertyValues); 918 | } -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/Extensions/Hosting/NullEnricher.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 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 Serilog.Core; 16 | using Serilog.Events; 17 | 18 | namespace Serilog.Extensions.Hosting; 19 | 20 | // Does nothing, but makes it easy to create an `ILogger` from a Serilog `Logger` 21 | // that will not dispose the underlying pipeline when disposed itself. 22 | class NullEnricher : ILogEventEnricher 23 | { 24 | public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) 25 | { 26 | } 27 | } -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/Extensions/Hosting/ReloadableLogger.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 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.Diagnostics.CodeAnalysis; 16 | using System.Runtime.CompilerServices; 17 | using Serilog.Core; 18 | using Serilog.Events; 19 | 20 | // ReSharper disable MemberCanBePrivate.Global 21 | 22 | namespace Serilog.Extensions.Hosting; 23 | 24 | /// 25 | /// A Serilog that can be reconfigured without invalidating existing 26 | /// instances derived from it. 27 | /// 28 | public sealed class ReloadableLogger : LoggerBase, ILogger, IReloadableLogger, IDisposable 29 | { 30 | readonly object _sync = new(); 31 | Logger _logger; 32 | 33 | // One-way; if the value is `true` it can never again be made `false`, allowing "double-checked" reads. If 34 | // `true`, `_logger` is final and a memory barrier ensures the final value is seen by all threads. 35 | bool _frozen; 36 | 37 | // Unsure whether this should be exposed; currently going for minimal API surface. 38 | internal ReloadableLogger(Logger initial) 39 | { 40 | _logger = initial ?? throw new ArgumentNullException(nameof(initial)); 41 | } 42 | 43 | ILogger IReloadableLogger.ReloadLogger() 44 | { 45 | return _logger; 46 | } 47 | 48 | /// 49 | /// Reload the logger using the supplied configuration delegate. 50 | /// 51 | /// A callback in which the logger is reconfigured. 52 | /// is null. 53 | public void Reload(Func configure) 54 | { 55 | if (configure == null) throw new ArgumentNullException(nameof(configure)); 56 | 57 | lock (_sync) 58 | { 59 | _logger.Dispose(); 60 | _logger = configure(new LoggerConfiguration()).CreateLogger(); 61 | } 62 | } 63 | 64 | /// 65 | /// Freeze the logger, so that no further reconfiguration is possible. Once the logger is frozen, logging through 66 | /// new contextual loggers will have no additional cost, and logging directly through this logger will not require 67 | /// any synchronization. 68 | /// 69 | /// The configured with the final settings. 70 | /// The logger is already frozen. 71 | public Logger Freeze() 72 | { 73 | lock (_sync) 74 | { 75 | if (_frozen) 76 | throw new InvalidOperationException("The logger is already frozen."); 77 | 78 | _frozen = true; 79 | 80 | // https://github.com/dotnet/runtime/issues/20500#issuecomment-284774431 81 | // Publish `_logger` and `_frozen`. This is useful here because it means that once the logger is frozen - which 82 | // we always expect - reads don't require any synchronization/interlocked instructions. 83 | #if FEATURE_MBPW 84 | Interlocked.MemoryBarrierProcessWide(); 85 | #else 86 | Thread.MemoryBarrier(); 87 | #endif 88 | 89 | return _logger; 90 | } 91 | } 92 | 93 | /// 94 | public void Dispose() 95 | { 96 | lock (_sync) 97 | _logger.Dispose(); 98 | } 99 | 100 | /// 101 | public ILogger ForContext(ILogEventEnricher enricher) 102 | { 103 | if (enricher == null!) return this; 104 | 105 | if (_frozen) 106 | return _logger.ForContext(enricher); 107 | 108 | lock (_sync) 109 | return new CachingReloadableLogger(this, _logger, this, p => p.ForContext(enricher)); 110 | } 111 | 112 | /// 113 | public ILogger ForContext(IEnumerable enrichers) 114 | { 115 | if (enrichers == null!) return this; 116 | 117 | if (_frozen) 118 | return _logger.ForContext(enrichers); 119 | 120 | lock (_sync) 121 | return new CachingReloadableLogger(this, _logger, this, p => p.ForContext(enrichers)); 122 | } 123 | 124 | /// 125 | public ILogger ForContext(string propertyName, object? value, bool destructureObjects = false) 126 | { 127 | if (propertyName == null!) return this; 128 | 129 | if (_frozen) 130 | return _logger.ForContext(propertyName, value, destructureObjects); 131 | 132 | lock (_sync) 133 | return new CachingReloadableLogger(this, _logger, this, p => p.ForContext(propertyName, value, destructureObjects)); 134 | } 135 | 136 | /// 137 | public ILogger ForContext() 138 | { 139 | if (_frozen) 140 | return _logger.ForContext(); 141 | 142 | lock (_sync) 143 | return new CachingReloadableLogger(this, _logger, this, p => p.ForContext()); 144 | } 145 | 146 | /// 147 | public ILogger ForContext(Type source) 148 | { 149 | if (source == null!) return this; 150 | 151 | if (_frozen) 152 | return _logger.ForContext(source); 153 | 154 | lock (_sync) 155 | return new CachingReloadableLogger(this, _logger, this, p => p.ForContext(source)); 156 | } 157 | 158 | /// 159 | public override void Write(LogEvent logEvent) 160 | { 161 | if (_frozen) 162 | { 163 | _logger.Write(logEvent); 164 | return; 165 | } 166 | 167 | lock (_sync) 168 | { 169 | _logger.Write(logEvent); 170 | } 171 | } 172 | 173 | /// 174 | public override void Write(LogEventLevel level, string messageTemplate) 175 | { 176 | if (_frozen) 177 | { 178 | _logger.Write(level, messageTemplate); 179 | return; 180 | } 181 | 182 | lock (_sync) 183 | { 184 | _logger.Write(level, messageTemplate); 185 | } 186 | } 187 | 188 | /// 189 | public override void Write(LogEventLevel level, string messageTemplate, T propertyValue) 190 | { 191 | if (_frozen) 192 | { 193 | _logger.Write(level, messageTemplate, propertyValue); 194 | return; 195 | } 196 | 197 | lock (_sync) 198 | { 199 | _logger.Write(level, messageTemplate, propertyValue); 200 | } 201 | } 202 | 203 | /// 204 | public override void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1) 205 | { 206 | if (_frozen) 207 | { 208 | _logger.Write(level, messageTemplate, propertyValue0, propertyValue1); 209 | return; 210 | } 211 | 212 | lock (_sync) 213 | { 214 | _logger.Write(level, messageTemplate, propertyValue0, propertyValue1); 215 | } 216 | } 217 | 218 | /// 219 | public override void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1, 220 | T2 propertyValue2) 221 | { 222 | if (_frozen) 223 | { 224 | _logger.Write(level, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 225 | return; 226 | } 227 | 228 | lock (_sync) 229 | { 230 | _logger.Write(level, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 231 | } 232 | } 233 | 234 | /// 235 | public override void Write(LogEventLevel level, string messageTemplate, params object?[]? propertyValues) 236 | { 237 | if (_frozen) 238 | { 239 | _logger.Write(level, messageTemplate, propertyValues); 240 | return; 241 | } 242 | 243 | lock (_sync) 244 | { 245 | _logger.Write(level, messageTemplate, propertyValues); 246 | } 247 | } 248 | 249 | /// 250 | public override void Write(LogEventLevel level, Exception? exception, string messageTemplate) 251 | { 252 | if (_frozen) 253 | { 254 | _logger.Write(level, exception, messageTemplate); 255 | return; 256 | } 257 | 258 | lock (_sync) 259 | { 260 | _logger.Write(level, exception, messageTemplate); 261 | } 262 | } 263 | 264 | /// 265 | public override void Write(LogEventLevel level, Exception? exception, string messageTemplate, T propertyValue) 266 | { 267 | if (_frozen) 268 | { 269 | _logger.Write(level, exception, messageTemplate, propertyValue); 270 | return; 271 | } 272 | 273 | lock (_sync) 274 | { 275 | _logger.Write(level, exception, messageTemplate, propertyValue); 276 | } 277 | } 278 | 279 | /// 280 | public override void Write(LogEventLevel level, Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) 281 | { 282 | if (_frozen) 283 | { 284 | _logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1); 285 | return; 286 | } 287 | 288 | lock (_sync) 289 | { 290 | _logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1); 291 | } 292 | } 293 | 294 | /// 295 | public override void Write(LogEventLevel level, Exception? exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, 296 | T2 propertyValue2) 297 | { 298 | if (_frozen) 299 | { 300 | _logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 301 | return; 302 | } 303 | 304 | lock (_sync) 305 | { 306 | _logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 307 | } 308 | } 309 | 310 | /// 311 | public override void Write(LogEventLevel level, Exception? exception, string messageTemplate, params object?[]? propertyValues) 312 | { 313 | if (_frozen) 314 | { 315 | _logger.Write(level, exception, messageTemplate, propertyValues); 316 | return; 317 | } 318 | 319 | lock (_sync) 320 | { 321 | _logger.Write(level, exception, messageTemplate, propertyValues); 322 | } 323 | } 324 | 325 | /// 326 | public bool IsEnabled(LogEventLevel level) 327 | { 328 | if (_frozen) 329 | { 330 | return _logger.IsEnabled(level); 331 | } 332 | 333 | lock (_sync) 334 | { 335 | return _logger.IsEnabled(level); 336 | } 337 | } 338 | 339 | /// 340 | public bool BindMessageTemplate(string messageTemplate, object?[]? propertyValues, 341 | [NotNullWhen(true)] 342 | out MessageTemplate? parsedTemplate, 343 | [NotNullWhen(true)] 344 | out IEnumerable? boundProperties) 345 | { 346 | if (_frozen) 347 | { 348 | return _logger.BindMessageTemplate(messageTemplate, propertyValues, out parsedTemplate, out boundProperties); 349 | } 350 | 351 | lock (_sync) 352 | { 353 | return _logger.BindMessageTemplate(messageTemplate, propertyValues, out parsedTemplate, out boundProperties); 354 | } 355 | } 356 | 357 | /// 358 | public bool BindProperty(string? propertyName, object? value, bool destructureObjects, [NotNullWhen(true)] out LogEventProperty? property) 359 | { 360 | if (_frozen) 361 | { 362 | return _logger.BindProperty(propertyName, value, destructureObjects, out property); 363 | } 364 | 365 | lock (_sync) 366 | { 367 | return _logger.BindProperty(propertyName, value, destructureObjects, out property); 368 | } 369 | } 370 | 371 | // `newRoot` is null when the second returned tuple argument is `false`, but the signature of the method doesn't currently 372 | // allow this to be expressed. 373 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 374 | (ILogger, bool) UpdateForCaller(ILogger root, ILogger? cached, IReloadableLogger caller, out ILogger newRoot, out ILogger? newCached, out bool frozen) 375 | { 376 | // Synchronization on `_sync` is not required in this method; it will be called without a lock 377 | // if `_frozen` and under a lock if not. 378 | 379 | if (_frozen) 380 | { 381 | // If we're frozen, then the caller hasn't observed this yet and should update. We could optimize a little here 382 | // and only signal an update if the cached logger is stale (as per the next condition below). 383 | newRoot = _logger; 384 | newCached = caller.ReloadLogger(); 385 | frozen = true; 386 | return (newCached, true); 387 | } 388 | 389 | if (cached != null! && root == _logger) 390 | { 391 | newRoot = default!; 392 | newCached = default; 393 | frozen = false; 394 | return (cached, false); 395 | } 396 | 397 | newRoot = _logger; 398 | newCached = caller.ReloadLogger(); 399 | frozen = false; 400 | return (newCached, true); 401 | } 402 | 403 | internal bool InvokeIsEnabled(ILogger root, ILogger? cached, IReloadableLogger caller, LogEventLevel level, out bool isEnabled, out ILogger newRoot, out ILogger? newCached, out bool frozen) 404 | { 405 | if (_frozen) 406 | { 407 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 408 | isEnabled = logger.IsEnabled(level); 409 | return update; 410 | } 411 | 412 | lock (_sync) 413 | { 414 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 415 | isEnabled = logger.IsEnabled(level); 416 | return update; 417 | } 418 | } 419 | 420 | internal bool InvokeBindMessageTemplate(ILogger root, ILogger? cached, IReloadableLogger caller, string messageTemplate, 421 | object?[]? propertyValues, [NotNullWhen(true)] out MessageTemplate? parsedTemplate, [NotNullWhen(true)] out IEnumerable? boundProperties, 422 | out bool canBind, out ILogger newRoot, out ILogger? newCached, out bool frozen) 423 | { 424 | if (_frozen) 425 | { 426 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 427 | canBind = logger.BindMessageTemplate(messageTemplate, propertyValues, out parsedTemplate, out boundProperties); 428 | return update; 429 | } 430 | 431 | lock (_sync) 432 | { 433 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 434 | canBind = logger.BindMessageTemplate(messageTemplate, propertyValues, out parsedTemplate, out boundProperties); 435 | return update; 436 | } 437 | } 438 | 439 | internal bool InvokeBindProperty(ILogger root, ILogger? cached, IReloadableLogger caller, string? propertyName, 440 | object? propertyValue, bool destructureObjects, [NotNullWhen(true)] out LogEventProperty? property, 441 | out bool canBind, out ILogger newRoot, out ILogger? newCached, out bool frozen) 442 | { 443 | if (_frozen) 444 | { 445 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 446 | canBind = logger.BindProperty(propertyName, propertyValue, destructureObjects, out property); 447 | return update; 448 | } 449 | 450 | lock (_sync) 451 | { 452 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 453 | canBind = logger.BindProperty(propertyName, propertyValue, destructureObjects, out property); 454 | return update; 455 | } 456 | } 457 | 458 | internal bool InvokeWrite(ILogger root, ILogger? cached, IReloadableLogger caller, LogEvent logEvent, out ILogger newRoot, out ILogger? newCached, out bool frozen) 459 | { 460 | if (_frozen) 461 | { 462 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 463 | logger.Write(logEvent); 464 | return update; 465 | } 466 | 467 | lock (_sync) 468 | { 469 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 470 | logger.Write(logEvent); 471 | return update; 472 | } 473 | } 474 | 475 | internal bool InvokeWrite(ILogger root, ILogger? cached, IReloadableLogger caller, LogEventLevel level, string messageTemplate, 476 | out ILogger newRoot, out ILogger? newCached, out bool frozen) 477 | { 478 | if (_frozen) 479 | { 480 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 481 | logger.Write(level, messageTemplate); 482 | return update; 483 | } 484 | 485 | lock (_sync) 486 | { 487 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 488 | logger.Write(level, messageTemplate); 489 | return update; 490 | } 491 | } 492 | 493 | internal bool InvokeWrite(ILogger root, ILogger? cached, IReloadableLogger caller, LogEventLevel level, string messageTemplate, 494 | T propertyValue, 495 | out ILogger newRoot, out ILogger? newCached, out bool frozen) 496 | { 497 | if (_frozen) 498 | { 499 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 500 | logger.Write(level, messageTemplate, propertyValue); 501 | return update; 502 | } 503 | 504 | lock (_sync) 505 | { 506 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 507 | logger.Write(level, messageTemplate, propertyValue); 508 | return update; 509 | } 510 | } 511 | 512 | internal bool InvokeWrite(ILogger root, ILogger? cached, IReloadableLogger caller, LogEventLevel level, string messageTemplate, 513 | T0 propertyValue0, T1 propertyValue1, 514 | out ILogger newRoot, out ILogger? newCached, out bool frozen) 515 | { 516 | if (_frozen) 517 | { 518 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 519 | logger.Write(level, messageTemplate, propertyValue0, propertyValue1); 520 | return update; 521 | } 522 | 523 | lock (_sync) 524 | { 525 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 526 | logger.Write(level, messageTemplate, propertyValue0, propertyValue1); 527 | return update; 528 | } 529 | } 530 | 531 | internal bool InvokeWrite(ILogger root, ILogger? cached, IReloadableLogger caller, LogEventLevel level, string messageTemplate, 532 | T0 propertyValue0, T1 propertyValue1, T2 propertyValue2, 533 | out ILogger newRoot, out ILogger? newCached, out bool frozen) 534 | { 535 | if (_frozen) 536 | { 537 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 538 | logger.Write(level, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 539 | return update; 540 | } 541 | 542 | lock (_sync) 543 | { 544 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 545 | logger.Write(level, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 546 | return update; 547 | } 548 | } 549 | 550 | internal bool InvokeWrite(ILogger root, ILogger? cached, IReloadableLogger caller, LogEventLevel level, string messageTemplate, 551 | object?[]? propertyValues, 552 | out ILogger newRoot, out ILogger? newCached, out bool frozen) 553 | { 554 | if (_frozen) 555 | { 556 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 557 | logger.Write(level, messageTemplate, propertyValues); 558 | return update; 559 | } 560 | 561 | lock (_sync) 562 | { 563 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 564 | logger.Write(level, messageTemplate, propertyValues); 565 | return update; 566 | } 567 | } 568 | 569 | internal bool InvokeWrite(ILogger root, ILogger? cached, IReloadableLogger caller, LogEventLevel level, Exception? exception, string messageTemplate, 570 | out ILogger newRoot, out ILogger? newCached, out bool frozen) 571 | { 572 | if (_frozen) 573 | { 574 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 575 | logger.Write(level, exception, messageTemplate); 576 | return update; 577 | } 578 | 579 | lock (_sync) 580 | { 581 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 582 | logger.Write(level, exception, messageTemplate); 583 | return update; 584 | } 585 | } 586 | 587 | internal bool InvokeWrite(ILogger root, ILogger? cached, IReloadableLogger caller, LogEventLevel level, Exception? exception, string messageTemplate, 588 | T propertyValue, 589 | out ILogger newRoot, out ILogger? newCached, out bool frozen) 590 | { 591 | if (_frozen) 592 | { 593 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 594 | logger.Write(level, exception, messageTemplate, propertyValue); 595 | return update; 596 | } 597 | 598 | lock (_sync) 599 | { 600 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 601 | logger.Write(level, exception, messageTemplate, propertyValue); 602 | return update; 603 | } 604 | } 605 | 606 | internal bool InvokeWrite(ILogger root, ILogger? cached, IReloadableLogger caller, LogEventLevel level, Exception? exception, string messageTemplate, 607 | T0 propertyValue0, T1 propertyValue1, 608 | out ILogger newRoot, out ILogger? newCached, out bool frozen) 609 | { 610 | if (_frozen) 611 | { 612 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 613 | logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1); 614 | return update; 615 | } 616 | 617 | lock (_sync) 618 | { 619 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 620 | logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1); 621 | return update; 622 | } 623 | } 624 | 625 | internal bool InvokeWrite(ILogger root, ILogger? cached, IReloadableLogger caller, LogEventLevel level, Exception? exception, string messageTemplate, 626 | T0 propertyValue0, T1 propertyValue1, T2 propertyValue2, 627 | out ILogger newRoot, out ILogger? newCached, out bool frozen) 628 | { 629 | if (_frozen) 630 | { 631 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 632 | logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 633 | return update; 634 | } 635 | 636 | lock (_sync) 637 | { 638 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 639 | logger.Write(level, exception, messageTemplate, propertyValue0, propertyValue1, propertyValue2); 640 | return update; 641 | } 642 | } 643 | 644 | internal bool InvokeWrite(ILogger root, ILogger? cached, IReloadableLogger caller, LogEventLevel level, Exception? exception, string messageTemplate, 645 | object?[]? propertyValues, 646 | out ILogger newRoot, out ILogger? newCached, out bool frozen) 647 | { 648 | if (_frozen) 649 | { 650 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 651 | logger.Write(level, exception, messageTemplate, propertyValues); 652 | return update; 653 | } 654 | 655 | lock (_sync) 656 | { 657 | var (logger, update) = UpdateForCaller(root, cached, caller, out newRoot, out newCached, out frozen); 658 | logger.Write(level, exception, messageTemplate, propertyValues); 659 | return update; 660 | } 661 | } 662 | 663 | internal bool CreateChild( 664 | ILogger root, 665 | IReloadableLogger parent, 666 | ILogger? cachedParent, 667 | Func configureChild, 668 | out ILogger child, 669 | out ILogger newRoot, 670 | out ILogger? newCached, 671 | out bool frozen) 672 | { 673 | if (_frozen) 674 | { 675 | var (logger, _) = UpdateForCaller(root, cachedParent, parent, out newRoot, out newCached, out frozen); 676 | child = configureChild(logger); 677 | return true; // Always an update, since the caller has not observed that the reloadable logger is frozen. 678 | } 679 | 680 | // No synchronization, here - a lot of loggers are created and thrown away again without ever being used, 681 | // so we just return a lazy wrapper. 682 | child = new CachingReloadableLogger(this, root, parent, configureChild); 683 | newRoot = default!; 684 | newCached = default; 685 | frozen = default; 686 | return false; 687 | } 688 | } 689 | -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/IDiagnosticContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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; 16 | 17 | namespace Serilog; 18 | 19 | /// 20 | /// Collects diagnostic information for packaging into wide events. 21 | /// 22 | public interface IDiagnosticContext 23 | { 24 | /// 25 | /// Set the specified property on the current diagnostic context. The property will be collected 26 | /// and attached to the event emitted at the completion of the context. 27 | /// 28 | /// The name of the property. Must be non-empty. 29 | /// The property value. 30 | /// If true, the value will be serialized as structured 31 | /// data if possible; if false, the object will be recorded as a scalar or simple array. 32 | void Set(string propertyName, object? value, bool destructureObjects = false); 33 | 34 | /// 35 | /// Set the specified exception on the current diagnostic context. 36 | /// 37 | /// 38 | /// This method is useful when unhandled exceptions do not reach Serilog.AspNetCore.RequestLoggingMiddleware, 39 | /// such as when using Hellang.Middleware.ProblemDetails 40 | /// to transform exceptions to ProblemDetails responses. 41 | /// 42 | /// The exception to log. If null is given, it clears any previously assigned exception. 43 | void SetException(Exception exception); 44 | } 45 | -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/LoggerConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 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 | #if !NO_RELOADABLE_LOGGER 16 | 17 | using Microsoft.Extensions.Hosting; 18 | using Serilog.Extensions.Hosting; 19 | using System; 20 | 21 | namespace Serilog; 22 | 23 | /// 24 | /// Extends . 25 | /// 26 | public static class LoggerConfigurationExtensions 27 | { 28 | /// 29 | /// Create a for use during host bootstrapping. The 30 | /// 31 | /// configuration overload will detect when is set to a instance, and 32 | /// reconfigure/freeze it so that s created during host bootstrapping continue to work once 33 | /// logger configuration (with access to host services) is completed. 34 | /// 35 | /// 36 | /// 37 | public static ReloadableLogger CreateBootstrapLogger(this LoggerConfiguration loggerConfiguration) 38 | { 39 | return new ReloadableLogger(loggerConfiguration.CreateLogger()); 40 | } 41 | } 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/LoggerSettingsConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 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; 16 | using Serilog.Configuration; 17 | using Serilog.Core; 18 | using Serilog.Extensions.Hosting; 19 | 20 | namespace Serilog; 21 | 22 | /// 23 | /// Extends with methods for consuming host services. 24 | /// 25 | public static class LoggerSettingsConfigurationExtensions 26 | { 27 | /// 28 | /// Configure the logger using components from the . If present, the logger will 29 | /// receive implementations/instances of , , 30 | /// , , , and 31 | /// . 32 | /// 33 | /// The `ReadFrom` configuration object. 34 | /// A from which services will be requested. 35 | /// A to support method chaining. 36 | public static LoggerConfiguration Services( 37 | this LoggerSettingsConfiguration loggerSettingsConfiguration, 38 | IServiceProvider services) 39 | { 40 | return loggerSettingsConfiguration.Settings(new InjectedLoggerSettings(services)); 41 | } 42 | } -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Serilog.Extensions.Hosting.Tests, PublicKey=" + 4 | "0024000004800000940000000602000000240000525341310004000001000100fb8d13fd344a1c" + 5 | "6fe0fe83ef33c1080bf30690765bc6eb0df26ebfdf8f21670c64265b30db09f73a0dea5b3db4c9" + 6 | "d18dbf6d5a25af5ce9016f281014d79dc3b4201ac646c451830fc7e61a2dfd633d34c39f87b818" + 7 | "94191652df5ac63cc40c77f3542f702bda692e6e8a9158353df189007a49da0f3cfd55eb250066" + 8 | "b19485ec")] 9 | -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/Serilog.Extensions.Hosting.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Serilog support for .NET Core logging in hosted services 5 | Microsoft;Serilog Contributors 6 | 8 | net462;netstandard2.0;netstandard2.1;net8.0;net9.0 9 | serilog;aspnet;aspnetcore;hosting 10 | icon.png 11 | https://github.com/serilog/serilog-extensions-hosting 12 | Apache-2.0 13 | Serilog 14 | README.md 15 | 16 | 17 | 18 | $(DefineConstants);FEATURE_MBPW 19 | 20 | 21 | 22 | $(DefineConstants);FEATURE_MBPW 23 | 24 | 25 | 26 | $(DefineConstants);FEATURE_MBPW 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 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; 16 | using Microsoft.Extensions.Hosting; 17 | using Microsoft.Extensions.Logging; 18 | using Serilog.Extensions.Logging; 19 | // ReSharper disable MemberCanBePrivate.Global 20 | 21 | namespace Serilog; 22 | 23 | /// 24 | /// Extends with Serilog configuration methods. 25 | /// 26 | public static class SerilogHostBuilderExtensions 27 | { 28 | /// 29 | /// Sets Serilog as the logging provider. 30 | /// 31 | /// The host builder to configure. 32 | /// The Serilog logger; if not supplied, the static will be used. 33 | /// When true, dispose when the framework disposes the provider. If the 34 | /// logger is not specified but is true, the method will be 35 | /// called on the static class instead. 36 | /// A registered in the Serilog pipeline using the 37 | /// WriteTo.Providers() configuration method, enabling other s to receive events. By 38 | /// default, only Serilog sinks will receive events. 39 | /// The host builder. 40 | public static IHostBuilder UseSerilog( 41 | this IHostBuilder builder, 42 | ILogger? logger = null, 43 | bool dispose = false, 44 | LoggerProviderCollection? providers = null) 45 | { 46 | if (builder == null) throw new ArgumentNullException(nameof(builder)); 47 | 48 | builder.ConfigureServices((_, collection) => 49 | { 50 | collection.AddSerilog(logger, dispose, providers); 51 | }); 52 | 53 | return builder; 54 | } 55 | 56 | /// Sets Serilog as the logging provider. 57 | /// 58 | /// A is supplied so that configuration and hosting information can be used. 59 | /// The logger will be shut down when application services are disposed. 60 | /// 61 | /// The host builder to configure. 62 | /// The delegate for configuring the that will be used to construct a . 63 | /// Indicates whether to preserve the value of . 64 | /// By default, Serilog does not write events to s registered through 65 | /// the Microsoft.Extensions.Logging API. Normally, equivalent Serilog sinks are used in place of providers. Specify 66 | /// true to write events to all providers. 67 | /// The host builder. 68 | public static IHostBuilder UseSerilog( 69 | this IHostBuilder builder, 70 | Action configureLogger, 71 | bool preserveStaticLogger = false, 72 | bool writeToProviders = false) 73 | { 74 | if (builder == null) throw new ArgumentNullException(nameof(builder)); 75 | if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger)); 76 | return UseSerilog( 77 | builder, 78 | (hostBuilderContext, services, loggerConfiguration) => 79 | configureLogger(hostBuilderContext, loggerConfiguration), 80 | preserveStaticLogger: preserveStaticLogger, 81 | writeToProviders: writeToProviders); 82 | } 83 | 84 | /// Sets Serilog as the logging provider. 85 | /// 86 | /// A is supplied so that configuration and hosting information can be used. 87 | /// The logger will be shut down when application services are disposed. 88 | /// 89 | /// The host builder to configure. 90 | /// The delegate for configuring the that will be used to construct a . 91 | /// Indicates whether to preserve the value of . 92 | /// By default, Serilog does not write events to s registered through 93 | /// the Microsoft.Extensions.Logging API. Normally, equivalent Serilog sinks are used in place of providers. Specify 94 | /// true to write events to all providers. 95 | /// If the static is a bootstrap logger (see 96 | /// LoggerConfigurationExtensions.CreateBootstrapLogger()), and is 97 | /// not specified, the the bootstrap logger will be reconfigured through the supplied delegate, rather than being 98 | /// replaced entirely or ignored. 99 | /// The host builder. 100 | public static IHostBuilder UseSerilog( 101 | this IHostBuilder builder, 102 | Action configureLogger, 103 | bool preserveStaticLogger = false, 104 | bool writeToProviders = false) 105 | { 106 | if (builder == null) throw new ArgumentNullException(nameof(builder)); 107 | if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger)); 108 | 109 | builder.ConfigureServices((context, collection) => 110 | { 111 | collection.AddSerilog( 112 | (services, loggerConfiguration) => 113 | configureLogger(context, services, loggerConfiguration), 114 | preserveStaticLogger: preserveStaticLogger, 115 | writeToProviders: writeToProviders); 116 | }); 117 | 118 | return builder; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/Serilog.Extensions.Hosting/SerilogServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 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 Microsoft.Extensions.DependencyInjection; 16 | using Microsoft.Extensions.Logging; 17 | using Serilog.Extensions.Hosting; 18 | using Serilog.Extensions.Logging; 19 | // ReSharper disable MemberCanBePrivate.Global 20 | 21 | namespace Serilog; 22 | 23 | /// 24 | /// Extends with Serilog configuration methods. 25 | /// 26 | public static class SerilogServiceCollectionExtensions 27 | { 28 | // Used internally to pass information through the container. We need to do this because if `logger` is the 29 | // root logger, registering it as a singleton may lead to disposal along with the container by MEDI. This isn't 30 | // always desirable, i.e. we may be handed a logger and `dispose: false`, so wrapping it keeps us in control 31 | // of when the logger is disposed. 32 | class RegisteredLogger 33 | { 34 | public RegisteredLogger(ILogger logger) 35 | { 36 | Logger = logger; 37 | } 38 | 39 | public ILogger Logger { get; } 40 | } 41 | 42 | /// 43 | /// Sets Serilog as the logging provider. 44 | /// 45 | /// The service collection to use. 46 | /// The Serilog logger; if not supplied, the static will be used. 47 | /// When true, dispose when the framework disposes the provider. If the 48 | /// logger is not specified but is true, the method will be 49 | /// called on the static class instead. 50 | /// A registered in the Serilog pipeline using the 51 | /// WriteTo.Providers() configuration method, enabling other s to receive events. By 52 | /// default, only Serilog sinks will receive events. 53 | /// The service collection. 54 | public static IServiceCollection AddSerilog( 55 | this IServiceCollection collection, 56 | ILogger? logger = null, 57 | bool dispose = false, 58 | LoggerProviderCollection? providers = null) 59 | { 60 | if (collection == null) throw new ArgumentNullException(nameof(collection)); 61 | 62 | if (providers != null) 63 | { 64 | collection.AddSingleton(services => 65 | { 66 | var factory = new SerilogLoggerFactory(logger, dispose, providers); 67 | 68 | foreach (var provider in services.GetServices()) 69 | factory.AddProvider(provider); 70 | 71 | return factory; 72 | }); 73 | } 74 | else 75 | { 76 | collection.AddSingleton(_ => new SerilogLoggerFactory(logger, dispose)); 77 | } 78 | 79 | if (logger != null) 80 | { 81 | // This won't (and shouldn't) take ownership of the logger. 82 | collection.AddSingleton(logger); 83 | 84 | // Still need to use RegisteredLogger as it is used by ConfigureDiagnosticContext. 85 | collection.AddSingleton(new RegisteredLogger(logger)); 86 | } 87 | bool useRegisteredLogger = logger != null; 88 | ConfigureDiagnosticContext(collection, useRegisteredLogger); 89 | 90 | return collection; 91 | } 92 | 93 | /// Sets Serilog as the logging provider. 94 | /// The service collection to use. 95 | /// The delegate for configuring the that will be used to construct a . 96 | /// Indicates whether to preserve the value of . 97 | /// By default, Serilog does not write events to s registered through 98 | /// the Microsoft.Extensions.Logging API. Normally, equivalent Serilog sinks are used in place of providers. Specify 99 | /// true to write events to all providers. 100 | /// The service collection. 101 | public static IServiceCollection AddSerilog( 102 | this IServiceCollection collection, 103 | Action configureLogger, 104 | bool preserveStaticLogger = false, 105 | bool writeToProviders = false) 106 | { 107 | if (collection == null) throw new ArgumentNullException(nameof(collection)); 108 | if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger)); 109 | return AddSerilog( 110 | collection, 111 | (_, loggerConfiguration) => 112 | configureLogger(loggerConfiguration), 113 | preserveStaticLogger: preserveStaticLogger, 114 | writeToProviders: writeToProviders); 115 | } 116 | 117 | /// Sets Serilog as the logging provider. 118 | /// The service collection to use. 119 | /// The delegate for configuring the that will be used to construct a . 120 | /// Indicates whether to preserve the value of . 121 | /// By default, Serilog does not write events to s registered through 122 | /// the Microsoft.Extensions.Logging API. Normally, equivalent Serilog sinks are used in place of providers. Specify 123 | /// true to write events to all providers. 124 | /// If the static is a bootstrap logger (see 125 | /// LoggerConfigurationExtensions.CreateBootstrapLogger()), and is 126 | /// not specified, the the bootstrap logger will be reconfigured through the supplied delegate, rather than being 127 | /// replaced entirely or ignored. 128 | /// The service collection. 129 | public static IServiceCollection AddSerilog( 130 | this IServiceCollection collection, 131 | Action configureLogger, 132 | bool preserveStaticLogger = false, 133 | bool writeToProviders = false) 134 | { 135 | if (collection == null) throw new ArgumentNullException(nameof(collection)); 136 | if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger)); 137 | 138 | // This check is eager; replacing the bootstrap logger after calling this method is not supported. 139 | var reloadable = Log.Logger as ReloadableLogger; 140 | var useReload = reloadable != null && !preserveStaticLogger; 141 | 142 | LoggerProviderCollection? loggerProviders = null; 143 | if (writeToProviders) 144 | { 145 | loggerProviders = new LoggerProviderCollection(); 146 | } 147 | 148 | collection.AddSingleton(services => 149 | { 150 | ILogger logger; 151 | if (useReload) 152 | { 153 | reloadable!.Reload(cfg => 154 | { 155 | if (loggerProviders != null) 156 | cfg.WriteTo.Providers(loggerProviders); 157 | 158 | configureLogger(services, cfg); 159 | return cfg; 160 | }); 161 | 162 | logger = reloadable.Freeze(); 163 | } 164 | else 165 | { 166 | var loggerConfiguration = new LoggerConfiguration(); 167 | 168 | if (loggerProviders != null) 169 | loggerConfiguration.WriteTo.Providers(loggerProviders); 170 | 171 | configureLogger(services, loggerConfiguration); 172 | logger = loggerConfiguration.CreateLogger(); 173 | } 174 | 175 | return new RegisteredLogger(logger); 176 | }); 177 | 178 | collection.AddSingleton(services => 179 | { 180 | // How can we register the logger, here, but not have MEDI dispose it? 181 | // Using the `NullEnricher` hack to prevent disposal. 182 | var logger = services.GetRequiredService().Logger; 183 | return logger.ForContext(new NullEnricher()); 184 | }); 185 | 186 | collection.AddSingleton(services => 187 | { 188 | var logger = services.GetRequiredService().Logger; 189 | 190 | ILogger? registeredLogger = null; 191 | if (preserveStaticLogger) 192 | { 193 | registeredLogger = logger; 194 | } 195 | else 196 | { 197 | // Passing a `null` logger to `SerilogLoggerFactory` results in disposal via 198 | // `Log.CloseAndFlush()`, which additionally replaces the static logger with a no-op. 199 | Log.Logger = logger; 200 | } 201 | 202 | var factory = new SerilogLoggerFactory(registeredLogger, !useReload, loggerProviders); 203 | 204 | if (writeToProviders) 205 | { 206 | foreach (var provider in services.GetServices()) 207 | factory.AddProvider(provider); 208 | } 209 | 210 | return factory; 211 | }); 212 | 213 | ConfigureDiagnosticContext(collection, preserveStaticLogger); 214 | 215 | return collection; 216 | } 217 | 218 | static void ConfigureDiagnosticContext(IServiceCollection collection, bool useRegisteredLogger) 219 | { 220 | if (collection == null) throw new ArgumentNullException(nameof(collection)); 221 | 222 | // Registered to provide two services... 223 | // Consumed by e.g. middleware 224 | collection.AddSingleton(services => 225 | { 226 | ILogger? logger = useRegisteredLogger ? services.GetRequiredService().Logger : null; 227 | return new DiagnosticContext(logger); 228 | }); 229 | // Consumed by user code 230 | collection.AddSingleton(services => services.GetRequiredService()); 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /test/Serilog.Extensions.Hosting.Tests/DiagnosticContextTests.cs: -------------------------------------------------------------------------------- 1 | using Serilog.Events; 2 | using Serilog.Extensions.Hosting.Tests.Support; 3 | using Xunit; 4 | 5 | // ReSharper disable PossibleNullReferenceException 6 | 7 | namespace Serilog.Extensions.Hosting.Tests; 8 | 9 | public class DiagnosticContextTests 10 | { 11 | [Fact] 12 | public void SetIsSafeWhenNoContextIsActive() 13 | { 14 | var dc = new DiagnosticContext(Some.Logger()); 15 | dc.Set(Some.String("name"), Some.Int32()); 16 | } 17 | 18 | [Fact] 19 | public void SetExceptionIsSafeWhenNoContextIsActive() 20 | { 21 | var dc = new DiagnosticContext(Some.Logger()); 22 | dc.SetException(new Exception("test")); 23 | } 24 | 25 | [Fact] 26 | public async Task PropertiesAreCollectedInAnActiveContext() 27 | { 28 | var dc = new DiagnosticContext(Some.Logger()); 29 | 30 | var collector = dc.BeginCollection(); 31 | dc.Set(Some.String("first"), Some.Int32()); 32 | await Task.Delay(TimeSpan.FromMilliseconds(10)); 33 | dc.Set(Some.String("second"), Some.Int32()); 34 | 35 | Assert.True(collector.TryComplete(out var properties, out var exception)); 36 | Assert.Equal(2, properties!.Count()); 37 | Assert.Null(exception); 38 | 39 | Assert.False(collector.TryComplete(out _, out _)); 40 | 41 | collector.Dispose(); 42 | 43 | dc.Set(Some.String("third"), Some.Int32()); 44 | Assert.False(collector.TryComplete(out _, out _)); 45 | } 46 | 47 | [Fact] 48 | public void ExceptionIsCollectedInAnActiveContext() 49 | { 50 | var dc = new DiagnosticContext(Some.Logger()); 51 | var collector = dc.BeginCollection(); 52 | 53 | var setException = new Exception("before collect"); 54 | dc.SetException(setException); 55 | 56 | Assert.True(collector.TryComplete(out _, out var collectedException)); 57 | Assert.Same(setException, collectedException); 58 | } 59 | 60 | [Fact] 61 | public void ExceptionIsNotCollectedAfterTryComplete() 62 | { 63 | var dc = new DiagnosticContext(Some.Logger()); 64 | var collector = dc.BeginCollection(); 65 | collector.TryComplete(out _, out _); 66 | dc.SetException(new Exception(Some.String("after collect"))); 67 | 68 | var tryComplete2 = collector.TryComplete(out _, out var collectedException2); 69 | 70 | Assert.False(tryComplete2); 71 | Assert.Null(collectedException2); 72 | } 73 | 74 | [Fact] 75 | public void ExceptionIsNotCollectedAfterDispose() 76 | { 77 | var dc = new DiagnosticContext(Some.Logger()); 78 | var collector = dc.BeginCollection(); 79 | collector.Dispose(); 80 | 81 | dc.SetException(new Exception("after dispose")); 82 | 83 | var tryComplete = collector.TryComplete(out _, out var collectedException); 84 | 85 | Assert.True(tryComplete); 86 | Assert.Null(collectedException); 87 | } 88 | 89 | [Fact] 90 | public void ExistingPropertiesCanBeUpdated() 91 | { 92 | var dc = new DiagnosticContext(Some.Logger()); 93 | 94 | var collector = dc.BeginCollection(); 95 | dc.Set("name", 10); 96 | dc.Set("name", 20); 97 | 98 | Assert.True(collector.TryComplete(out var properties, out var exception)); 99 | var prop = Assert.Single(properties!); 100 | var scalar = Assert.IsType(prop.Value); 101 | Assert.Equal(20, scalar.Value); 102 | Assert.Null(exception); 103 | } 104 | 105 | [Fact] 106 | public void ExistingExceptionCanBeUpdated() 107 | { 108 | var dc = new DiagnosticContext(Some.Logger()); 109 | var collector = dc.BeginCollection(); 110 | 111 | dc.SetException(new Exception("ex1")); 112 | dc.SetException(new Exception("ex2")); 113 | 114 | Assert.True(collector.TryComplete(out _, out var collectedException)); 115 | Assert.Equal("ex2", collectedException!.Message); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /test/Serilog.Extensions.Hosting.Tests/LoggerSettingsConfigurationExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Serilog.Core; 4 | using Serilog.Events; 5 | using Serilog.Extensions.Hosting.Tests.Support; 6 | using Xunit; 7 | 8 | namespace Serilog.Extensions.Hosting.Tests; 9 | 10 | public class LoggerSettingsConfigurationExtensionsTests 11 | { 12 | [Fact] 13 | public void SinksAreInjectedFromTheServiceProvider() 14 | { 15 | var emittedEvents = new List(); 16 | 17 | var serviceCollection = new ServiceCollection(); 18 | serviceCollection.AddSingleton(new ListSink(emittedEvents)); 19 | using var services = serviceCollection.BuildServiceProvider(); 20 | 21 | using var logger = new LoggerConfiguration() 22 | .ReadFrom.Services(services) 23 | .CreateLogger(); 24 | 25 | logger.Information("Hello, world!"); 26 | 27 | var evt = Assert.Single(emittedEvents); 28 | Assert.Equal("Hello, world!", evt!.MessageTemplate.Text); 29 | } 30 | } -------------------------------------------------------------------------------- /test/Serilog.Extensions.Hosting.Tests/ReloadableLoggerTests.cs: -------------------------------------------------------------------------------- 1 | using Serilog.Core; 2 | using Serilog.Events; 3 | using Serilog.Extensions.Hosting.Tests.Support; 4 | using Xunit; 5 | 6 | namespace Serilog.Extensions.Hosting.Tests; 7 | 8 | public class ReloadableLoggerTests 9 | { 10 | [Fact] 11 | public void AFrozenLoggerYieldsSerilogLoggers() 12 | { 13 | var logger = new ReloadableLogger(new LoggerConfiguration().CreateLogger()); 14 | var contextual = logger.ForContext(); 15 | 16 | var nested = contextual.ForContext("test", "test"); 17 | Assert.IsNotType(nested); 18 | 19 | logger.Freeze(); 20 | 21 | nested = contextual.ForContext("test", "test"); 22 | Assert.IsType(nested); 23 | } 24 | 25 | [Fact] 26 | public void CachingReloadableLoggerRemainsUsableAfterFreezing() 27 | { 28 | var logger = new LoggerConfiguration().CreateBootstrapLogger(); 29 | var contextual = logger.ForContext(); 30 | contextual.Information("First"); 31 | logger.Reload(c => c); 32 | logger.Freeze(); 33 | contextual.Information("Second"); 34 | contextual.Information("Third"); 35 | contextual.Information("Fourth"); // No crash :-) 36 | } 37 | 38 | [Fact] 39 | public void ReloadableLoggerRespectsMinimumLevelOverrides() 40 | { 41 | var emittedEvents = new List(); 42 | var logger = new LoggerConfiguration() 43 | .MinimumLevel.Override("Test", LogEventLevel.Warning) 44 | .WriteTo.Sink(new ListSink(emittedEvents)) 45 | .CreateBootstrapLogger(); 46 | 47 | var limited = logger 48 | .ForContext("X", 1) 49 | .ForContext(Constants.SourceContextPropertyName, "Test.Stuff"); 50 | 51 | var notLimited = logger.ForContext(); 52 | 53 | foreach (var context in new[] { limited, notLimited }) 54 | { 55 | // Suppressed by both sinks 56 | context.Debug("First"); 57 | 58 | // Suppressed by the limited logger 59 | context.Information("Second"); 60 | 61 | // Emitted by both loggers 62 | context.Warning("Third"); 63 | } 64 | 65 | Assert.Equal(3, emittedEvents.Count); 66 | Assert.Equal(2, emittedEvents.Count(le => le.Level == LogEventLevel.Warning)); 67 | } 68 | 69 | [Fact] 70 | public void ReloadableLoggersRecordEnrichment() 71 | { 72 | var emittedEvents = new List(); 73 | 74 | var logger = new LoggerConfiguration() 75 | .WriteTo.Sink(new ListSink(emittedEvents)) 76 | .CreateBootstrapLogger(); 77 | 78 | var outer = logger 79 | .ForContext("A", new object()); 80 | var inner = outer.ForContext("B", "test"); 81 | 82 | inner.Information("First"); 83 | 84 | logger.Reload(lc => lc.WriteTo.Sink(new ListSink(emittedEvents))); 85 | 86 | inner.Information("Second"); 87 | 88 | logger.Freeze(); 89 | 90 | inner.Information("Third"); 91 | 92 | outer.ForContext("B", "test").Information("Fourth"); 93 | 94 | logger.ForContext("A", new object()) 95 | .ForContext("B", "test") 96 | .Information("Fifth"); 97 | 98 | Assert.Equal(5, emittedEvents.Count); 99 | Assert.All(emittedEvents, e => Assert.Equal(2, e.Properties.Count)); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /test/Serilog.Extensions.Hosting.Tests/Serilog.Extensions.Hosting.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 6 | net4.8;net6.0;net8.0;net9.0 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/Serilog.Extensions.Hosting.Tests/SerilogHostBuilderExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | using Xunit; 6 | 7 | namespace Serilog.Extensions.Hosting.Tests; 8 | 9 | public class SerilogHostBuilderExtensionsTests 10 | { 11 | [Fact] 12 | public void ServicesAreRegisteredWhenCallingUseSerilog() 13 | { 14 | // Arrange 15 | var collection = new ServiceCollection(); 16 | IHostBuilder builder = new FakeHostBuilder(collection); 17 | 18 | // Act 19 | builder.UseSerilog(); 20 | 21 | // Assert 22 | IServiceProvider provider = collection.BuildServiceProvider(); 23 | provider.GetRequiredService(); 24 | provider.GetRequiredService(); 25 | } 26 | 27 | [Fact] 28 | public void ServicesAreRegisteredWhenCallingUseSerilogWithLogger() 29 | { 30 | // Arrange 31 | var collection = new ServiceCollection(); 32 | IHostBuilder builder = new FakeHostBuilder(collection); 33 | ILogger logger = new LoggerConfiguration().CreateLogger(); 34 | 35 | // Act 36 | builder.UseSerilog(logger); 37 | 38 | // Assert 39 | IServiceProvider provider = collection.BuildServiceProvider(); 40 | provider.GetRequiredService(); 41 | provider.GetRequiredService(); 42 | provider.GetRequiredService(); 43 | } 44 | 45 | [Fact] 46 | public void ServicesAreRegisteredWhenCallingUseSerilogWithConfigureDelegate() 47 | { 48 | // Arrange 49 | var collection = new ServiceCollection(); 50 | IHostBuilder builder = new FakeHostBuilder(collection); 51 | 52 | // Act 53 | builder.UseSerilog((_, _) => { }); 54 | 55 | // Assert 56 | IServiceProvider provider = collection.BuildServiceProvider(); 57 | provider.GetRequiredService(); 58 | provider.GetRequiredService(); 59 | provider.GetRequiredService(); 60 | } 61 | 62 | class FakeHostBuilder : IHostBuilder 63 | { 64 | readonly IServiceCollection _collection; 65 | 66 | public FakeHostBuilder(IServiceCollection collection) => _collection = collection; 67 | 68 | public IHostBuilder ConfigureHostConfiguration(Action configureDelegate) 69 | { 70 | throw new NotImplementedException(); 71 | } 72 | 73 | public IHostBuilder ConfigureAppConfiguration(Action configureDelegate) 74 | { 75 | throw new NotImplementedException(); 76 | } 77 | 78 | public IHostBuilder ConfigureServices(Action configureDelegate) 79 | { 80 | configureDelegate(null!, _collection); 81 | return this; 82 | } 83 | 84 | public IHostBuilder UseServiceProviderFactory(IServiceProviderFactory factory) 85 | where TContainerBuilder: notnull 86 | { 87 | throw new NotImplementedException(); 88 | } 89 | 90 | public IHostBuilder UseServiceProviderFactory(Func> factory) 91 | where TContainerBuilder: notnull 92 | { 93 | throw new NotImplementedException(); 94 | } 95 | 96 | public IHostBuilder ConfigureContainer(Action configureDelegate) 97 | { 98 | throw new NotImplementedException(); 99 | } 100 | 101 | public IHost Build() 102 | { 103 | throw new NotImplementedException(); 104 | } 105 | 106 | public IDictionary Properties { get; } = new Dictionary(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /test/Serilog.Extensions.Hosting.Tests/SerilogServiceCollectionExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Logging; 4 | using Xunit; 5 | 6 | namespace Serilog.Extensions.Hosting.Tests; 7 | 8 | public class SerilogServiceCollectionExtensionsTests 9 | { 10 | [Fact] 11 | public void ServicesAreRegisteredWhenCallingAddSerilog() 12 | { 13 | // Arrange 14 | var collection = new ServiceCollection(); 15 | 16 | // Act 17 | collection.AddSerilog(); 18 | 19 | // Assert 20 | using var provider = collection.BuildServiceProvider(); 21 | provider.GetRequiredService(); 22 | provider.GetRequiredService(); 23 | } 24 | 25 | [Fact] 26 | public void ServicesAreRegisteredWhenCallingAddSerilogWithLogger() 27 | { 28 | // Arrange 29 | var collection = new ServiceCollection(); 30 | ILogger logger = new LoggerConfiguration().CreateLogger(); 31 | 32 | // Act 33 | collection.AddSerilog(logger); 34 | 35 | // Assert 36 | using var provider = collection.BuildServiceProvider(); 37 | provider.GetRequiredService(); 38 | provider.GetRequiredService(); 39 | provider.GetRequiredService(); 40 | } 41 | 42 | [Fact] 43 | public void ServicesAreRegisteredWhenCallingAddSerilogWithConfigureDelegate() 44 | { 45 | // Arrange 46 | var collection = new ServiceCollection(); 47 | 48 | // Act 49 | collection.AddSerilog(_ => { }); 50 | 51 | // Assert 52 | using var provider = collection.BuildServiceProvider(); 53 | provider.GetRequiredService(); 54 | provider.GetRequiredService(); 55 | provider.GetRequiredService(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/Serilog.Extensions.Hosting.Tests/Support/ListSink.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Collections.Generic; 5 | using Serilog.Core; 6 | using Serilog.Events; 7 | 8 | namespace Serilog.Extensions.Hosting.Tests.Support; 9 | 10 | public class ListSink : ILogEventSink 11 | { 12 | readonly List _list; 13 | 14 | public ListSink(List list) 15 | { 16 | _list = list; 17 | } 18 | 19 | public void Emit(LogEvent logEvent) 20 | { 21 | _list.Add(logEvent); 22 | } 23 | } -------------------------------------------------------------------------------- /test/Serilog.Extensions.Hosting.Tests/Support/Some.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using Serilog.Events; 6 | 7 | namespace Serilog.Extensions.Hosting.Tests.Support; 8 | 9 | static class Some 10 | { 11 | static int _next; 12 | 13 | public static int Int32() => Interlocked.Increment(ref _next); 14 | 15 | public static string String(string? tag = null) => $"s_{tag}{Int32()}"; 16 | 17 | public static LogEventProperty LogEventProperty() => new LogEventProperty(String("name"), new ScalarValue(Int32())); 18 | 19 | public static ILogger Logger() => new LoggerConfiguration().CreateLogger(); 20 | } 21 | --------------------------------------------------------------------------------