├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md ├── dependabot.yml ├── ranger.yml ├── release-drafter.yml └── workflows │ └── changelog.yml ├── .gitignore ├── CHANGELOG.MD ├── LICENSE ├── NLog.Extensions.Logging.sln ├── NLog.Extensions.Logging.sln.DotSettings ├── README.md ├── appveyor.yml ├── build.ps1 ├── examples └── NetCore2 │ ├── ConsoleExample │ ├── ConsoleExample.csproj │ ├── Program.cs │ └── nlog.config │ ├── ConsoleExampleJsonConfig │ ├── ConsoleExampleJsonConfig.csproj │ ├── Program.cs │ └── appsettings.json │ └── HostingExample │ ├── HostingExample.csproj │ ├── Program.cs │ └── nlog.config ├── run-sonar.ps1 ├── run-tests.ps1 ├── src ├── NLog.Extensions.Hosting │ ├── Config │ │ └── SetupExtensionsBuilderExtensions.cs │ ├── Extensions │ │ └── ConfigureExtensions.cs │ ├── LayoutRenderers │ │ ├── HostAppNameLayoutRenderer.cs │ │ ├── HostEnvironmentLayoutRenderer.cs │ │ └── HostRootDirLayoutRenderer.cs │ ├── NLog.Extensions.Hosting.csproj │ ├── Properties │ │ └── AssemblyInfo.cs │ └── README.md ├── NLog.Extensions.Logging │ ├── Config │ │ ├── NLogLoggingConfiguration.cs │ │ ├── SetupBuilderExtensions.cs │ │ └── SetupExtensionsBuilderExtensions.cs │ ├── Extensions │ │ └── ConfigureExtensions.cs │ ├── Internal │ │ ├── Guard.cs │ │ ├── RegisterNLogLoggingProvider.cs │ │ └── StringExtensions.cs │ ├── LayoutRenderers │ │ ├── ConfigSettingLayoutRenderer.cs │ │ └── MicrosoftConsoleLayoutRenderer.cs │ ├── Layouts │ │ └── MicrosoftConsoleJsonLayout.cs │ ├── Logging │ │ ├── ActivityExtensions.cs │ │ ├── EventIdCaptureType.cs │ │ ├── NLogBeginScopeParser.cs │ │ ├── NLogLogger.cs │ │ ├── NLogLoggerFactory.cs │ │ ├── NLogLoggerProvider.cs │ │ ├── NLogMessageParameterList.cs │ │ └── NLogProviderOptions.cs │ ├── NLog.Extensions.Logging.csproj │ ├── NLog.Extensions.Logging.csproj.DotSettings │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── README.md │ └── Targets │ │ └── MicrosoftILoggerTarget.cs └── NLog.snk └── test ├── NLog.Extensions.Hosting.Tests ├── ExtensionMethodTests.cs ├── NLog.Extensions.Hosting.Tests.csproj └── Properties │ └── AssemblyInfo.cs └── NLog.Extensions.Logging.Tests ├── ApiTests.cs ├── ConfigSettingLayoutRendererTests.cs ├── CustomBeginScopeTest.cs ├── CustomLoggerCallSiteTest.cs ├── CustomLoggerPropertyTest.cs ├── Extensions └── ConfigureExtensionsTests.cs ├── LoggerTests.cs ├── Logging ├── NLogLoggerFactoryTests.cs ├── NLogLoggerProviderTests.cs └── NLogMessageParameterListTests.cs ├── MicrosoftConsoleJsonLayoutTests.cs ├── MicrosoftConsoleLayoutRendererTest.cs ├── MicrosoftILoggerTargetTests.cs ├── NLog.Extensions.Logging.Tests.csproj ├── NLogLoggingConfigurationTests.cs ├── NLogMessageParameterListTests.cs ├── NLogTestBase.cs └── Properties └── AssemblyInfo.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | # All files: detect if file is text automatically 2 | 3 | * text=auto 4 | 5 | # Text files that should be normalized to LF 6 | 7 | *.cs text diff=csharp 8 | *.config text 9 | *.sln text 10 | *.csproj text 11 | *.md text 12 | *.sh text 13 | *.ps1 text 14 | *.cmd text 15 | *.bat text 16 | *.markdown text 17 | *.msbuild text 18 | 19 | # Binary files that should not be normalized or diffed 20 | 21 | *.exe binary 22 | *.dll binary 23 | *.pdb binary 24 | *.pfx binary 25 | *.snk binary 26 | 27 | *.png binary 28 | *.gif binary 29 | *.jpg binary 30 | *.bmp binary 31 | *.ico binary 32 | 33 | *.chm binary 34 | *.7z binary 35 | *.zip binary -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Hi! Thanks for reporting this feature/bug/question! 2 | 3 | Please keep / fill in the relevant info from this template so that we can help you as best as possible. 4 | 5 | QUESTIONS are preferred on StackOverflow. You could expect a faster response there (but do include all the info below). Use the tag "nlog" https://stackoverflow.com/questions/ask 6 | 7 | Please only post issues here related to NLog.Extensions.Logging. If unsure, please post on https://github.com/NLog/NLog/issues/new 8 | 9 | ----- 10 | 11 | **Type** (choose one): 12 | 13 | - Bug 14 | - Feature request 15 | - Question 16 | 17 | 18 | **NLog version**: (e.g. 4.5.6) 19 | 20 | **NLog.Extensions.Logging version**: (e.g. 1.2.3) 21 | 22 | **NLog.Web.AspNetCore version**: (e.g. 4.5.6) (or not used) 23 | 24 | **Platform**: .Net 4.6.1 / .NET Core 1 / .NET Core 2 / .NET Core 3.1 / .NET 6.0 25 | 26 | **Current NLog config** (xml or C#, if relevant) 27 | 28 | ```xml 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | ``` 37 | 38 | In case of a BUG: 39 | 40 | - What is the current result? 41 | - What is the expected result? 42 | - Did you checked the [Internal log](https://github.com/NLog/NLog/wiki/Internal-Logging)? 43 | - Please post full exception details (message, stacktrace, inner exceptions) 44 | - Are there any work arrounds? yes/no 45 | - Is there a version in which it did worked? 46 | - Can you help us by writing an unit test? 47 | 48 | 49 | in case of a FEATURE REQUEST: 50 | 51 | - Why do we need it? 52 | - An example of the XML config, if relevant. 53 | 54 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: nuget 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: Microsoft.Extensions.Configuration.Abstractions 10 | - dependency-name: Microsoft.Extensions.Configuration.Json 11 | - dependency-name: Microsoft.Extensions.DependencyInjection 12 | - dependency-name: Microsoft.Extensions.Hosting 13 | - dependency-name: Microsoft.Extensions.Hosting.Abstractions 14 | - dependency-name: Microsoft.Extensions.Logging 15 | - dependency-name: Microsoft.Extensions.Logging.Abstractions 16 | -------------------------------------------------------------------------------- /.github/ranger.yml: -------------------------------------------------------------------------------- 1 | merges: 2 | - action: delete_branch 3 | # - action: tag 4 | labels: 5 | 'needs info': 6 | action: close 7 | delay: 7 days 8 | comment: "Please add the requested info, so we could help you better! (This issue will be closed in 7 days)" 9 | duplicate: close 10 | wontfix: close 11 | invalid: close 12 | "squash when passing": merge 13 | "rebase when passing": merge 14 | "merge when passing": merge 15 | #comments: 16 | # - action: delete_comment 17 | # pattern: "+1" # can also be a regular expression 18 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'Version $NEXT_PATCH_VERSION' 2 | tag-template: 'v$NEXT_PATCH_VERSION' 3 | categories: 4 | - title: '🚀 Features' 5 | labels: 6 | - 'feature' 7 | - title: '👍 Enhancements' 8 | labels: 9 | - 'enhancement' 10 | - 'documentation' 11 | - title: '🐛 Bug Fixes' 12 | labels: 13 | - 'fix' 14 | - 'bugfix' 15 | - 'bug' 16 | - title: '⏩ Performance' 17 | labels: 18 | - 'performance' 19 | - title: '🔧 Maintenance' 20 | labels: 21 | - 'refactor' 22 | - 'test' 23 | - 'tests' 24 | - 'build' 25 | - title: '↜ Dependencies' 26 | labels: 27 | - 'dependencies' 28 | - 'dependency' 29 | exclude-labels: 30 | - 'skip-changelog' 31 | - 'release' 32 | version-resolver: 33 | major: 34 | labels: 35 | - 'Major version' 36 | minor: 37 | labels: 38 | - 'Minor version' 39 | patch: 40 | labels: 41 | - 'Patch version' 42 | default: patch 43 | change-template: '- [#$NUMBER]($URL): $TITLE (@$AUTHOR)' 44 | template: | 45 | $CHANGES 46 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - master 8 | 9 | jobs: 10 | update_release_draft: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # Drafts your next Release notes as Pull Requests are merged into "master" 14 | - uses: release-drafter/release-drafter@v5 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/visualstudio 3 | 4 | ### VisualStudio ### 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | 8 | # User-specific files 9 | *.suo 10 | *.user 11 | *.userosscache 12 | *.sln.docstates 13 | 14 | # User-specific files (MonoDevelop/Xamarin Studio) 15 | *.userprefs 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Dd]ebugPublic/ 20 | [Rr]elease/ 21 | [Rr]eleases/ 22 | x64/ 23 | x86/ 24 | bld/ 25 | [Bb]in/ 26 | [Oo]bj/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.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 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | *.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Microsoft Azure ApplicationInsights config file 170 | ApplicationInsights.config 171 | 172 | # Windows Store app package directory 173 | AppPackages/ 174 | BundleArtifacts/ 175 | 176 | # Visual Studio cache files 177 | # files ending in .cache can be ignored 178 | *.[Cc]ache 179 | # but keep track of directories ending in .cache 180 | !*.[Cc]ache/ 181 | 182 | # Others 183 | ClientBin/ 184 | ~$* 185 | *~ 186 | *.dbmdl 187 | *.dbproj.schemaview 188 | *.pfx 189 | *.publishsettings 190 | node_modules/ 191 | orleans.codegen.cs 192 | 193 | # RIA/Silverlight projects 194 | Generated_Code/ 195 | 196 | # Backup & report files from converting an old project file 197 | # to a newer Visual Studio version. Backup files are not needed, 198 | # because we have git ;-) 199 | _UpgradeReport_Files/ 200 | Backup*/ 201 | UpgradeLog*.XML 202 | UpgradeLog*.htm 203 | 204 | # SQL Server files 205 | *.mdf 206 | *.ldf 207 | 208 | # Business Intelligence projects 209 | *.rdl.data 210 | *.bim.layout 211 | *.bim_*.settings 212 | 213 | # Microsoft Fakes 214 | FakesAssemblies/ 215 | 216 | # GhostDoc plugin setting file 217 | *.GhostDoc.xml 218 | 219 | # Node.js Tools for Visual Studio 220 | .ntvs_analysis.dat 221 | 222 | # Visual Studio 6 build log 223 | *.plg 224 | 225 | # Visual Studio 6 workspace options file 226 | *.opt 227 | 228 | # Visual Studio LightSwitch build output 229 | **/*.HTMLClient/GeneratedArtifacts 230 | **/*.DesktopClient/GeneratedArtifacts 231 | **/*.DesktopClient/ModelManifest.xml 232 | **/*.Server/GeneratedArtifacts 233 | **/*.Server/ModelManifest.xml 234 | _Pvt_Extensions 235 | 236 | # Paket dependency manager 237 | .paket/paket.exe 238 | 239 | # FAKE - F# Make 240 | .fake/ 241 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, NLog 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /NLog.Extensions.Logging.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.7.34009.444 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C21FD102-21B1-46DB-AD62-86692558AD01}" 6 | EndProject 7 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BD106966-02BE-4137-B9DC-4ECE56B4C204}" 8 | ProjectSection(SolutionItems) = preProject 9 | appveyor.yml = appveyor.yml 10 | build.ps1 = build.ps1 11 | CHANGELOG.MD = CHANGELOG.MD 12 | README.md = README.md 13 | run-sonar.ps1 = run-sonar.ps1 14 | run-tests.ps1 = run-tests.ps1 15 | EndProjectSection 16 | EndProject 17 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Extensions.Logging", "src\NLog.Extensions.Logging\NLog.Extensions.Logging.csproj", "{6A236D76-C9D9-4B1D-8DDE-F6978D110288}" 18 | EndProject 19 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{FBD2E07B-F25B-4D2F-AEF6-6D1E10F1E523}" 20 | EndProject 21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleExample", "Examples\NetCore2\ConsoleExample\ConsoleExample.csproj", "{CF2D8DDE-5876-4EF0-B99D-3C536E887E18}" 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Extensions.Hosting", "src\NLog.Extensions.Hosting\NLog.Extensions.Hosting.csproj", "{548E65CE-0378-4812-AE00-B173F1251D3C}" 24 | EndProject 25 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Extensions.Hosting.Tests", "test\NLog.Extensions.Hosting.Tests\NLog.Extensions.Hosting.Tests.csproj", "{0DC000BA-2DF8-48E5-A7BC-D76CB9D3FC61}" 26 | EndProject 27 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Extensions.Logging.Tests", "test\NLog.Extensions.Logging.Tests\NLog.Extensions.Logging.Tests.csproj", "{DC42BF57-6316-4FCA-AD33-48FFDAFB4712}" 28 | EndProject 29 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostingExample", "examples\NetCore2\HostingExample\HostingExample.csproj", "{07D358DF-D77A-434B-B034-95785DF7106F}" 30 | EndProject 31 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleExampleJsonConfig", "examples\NetCore2\ConsoleExampleJsonConfig\ConsoleExampleJsonConfig.csproj", "{7C28B706-21F3-45EE-A9C3-B39AC5BB8AB5}" 32 | EndProject 33 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{4C63B329-66F2-473F-ABC3-B1AFE0990F47}" 34 | EndProject 35 | Global 36 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 37 | Debug|Any CPU = Debug|Any CPU 38 | Release|Any CPU = Release|Any CPU 39 | EndGlobalSection 40 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 41 | {6A236D76-C9D9-4B1D-8DDE-F6978D110288}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {6A236D76-C9D9-4B1D-8DDE-F6978D110288}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {6A236D76-C9D9-4B1D-8DDE-F6978D110288}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {6A236D76-C9D9-4B1D-8DDE-F6978D110288}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {CF2D8DDE-5876-4EF0-B99D-3C536E887E18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {CF2D8DDE-5876-4EF0-B99D-3C536E887E18}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {CF2D8DDE-5876-4EF0-B99D-3C536E887E18}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {CF2D8DDE-5876-4EF0-B99D-3C536E887E18}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {548E65CE-0378-4812-AE00-B173F1251D3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {548E65CE-0378-4812-AE00-B173F1251D3C}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {548E65CE-0378-4812-AE00-B173F1251D3C}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {548E65CE-0378-4812-AE00-B173F1251D3C}.Release|Any CPU.Build.0 = Release|Any CPU 53 | {0DC000BA-2DF8-48E5-A7BC-D76CB9D3FC61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 54 | {0DC000BA-2DF8-48E5-A7BC-D76CB9D3FC61}.Debug|Any CPU.Build.0 = Debug|Any CPU 55 | {0DC000BA-2DF8-48E5-A7BC-D76CB9D3FC61}.Release|Any CPU.ActiveCfg = Release|Any CPU 56 | {0DC000BA-2DF8-48E5-A7BC-D76CB9D3FC61}.Release|Any CPU.Build.0 = Release|Any CPU 57 | {DC42BF57-6316-4FCA-AD33-48FFDAFB4712}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 58 | {DC42BF57-6316-4FCA-AD33-48FFDAFB4712}.Debug|Any CPU.Build.0 = Debug|Any CPU 59 | {DC42BF57-6316-4FCA-AD33-48FFDAFB4712}.Release|Any CPU.ActiveCfg = Release|Any CPU 60 | {DC42BF57-6316-4FCA-AD33-48FFDAFB4712}.Release|Any CPU.Build.0 = Release|Any CPU 61 | {07D358DF-D77A-434B-B034-95785DF7106F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 62 | {07D358DF-D77A-434B-B034-95785DF7106F}.Debug|Any CPU.Build.0 = Debug|Any CPU 63 | {07D358DF-D77A-434B-B034-95785DF7106F}.Release|Any CPU.ActiveCfg = Release|Any CPU 64 | {07D358DF-D77A-434B-B034-95785DF7106F}.Release|Any CPU.Build.0 = Release|Any CPU 65 | {7C28B706-21F3-45EE-A9C3-B39AC5BB8AB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 66 | {7C28B706-21F3-45EE-A9C3-B39AC5BB8AB5}.Debug|Any CPU.Build.0 = Debug|Any CPU 67 | {7C28B706-21F3-45EE-A9C3-B39AC5BB8AB5}.Release|Any CPU.ActiveCfg = Release|Any CPU 68 | {7C28B706-21F3-45EE-A9C3-B39AC5BB8AB5}.Release|Any CPU.Build.0 = Release|Any CPU 69 | EndGlobalSection 70 | GlobalSection(SolutionProperties) = preSolution 71 | HideSolutionNode = FALSE 72 | EndGlobalSection 73 | GlobalSection(NestedProjects) = preSolution 74 | {6A236D76-C9D9-4B1D-8DDE-F6978D110288} = {C21FD102-21B1-46DB-AD62-86692558AD01} 75 | {CF2D8DDE-5876-4EF0-B99D-3C536E887E18} = {4C63B329-66F2-473F-ABC3-B1AFE0990F47} 76 | {548E65CE-0378-4812-AE00-B173F1251D3C} = {C21FD102-21B1-46DB-AD62-86692558AD01} 77 | {0DC000BA-2DF8-48E5-A7BC-D76CB9D3FC61} = {FBD2E07B-F25B-4D2F-AEF6-6D1E10F1E523} 78 | {DC42BF57-6316-4FCA-AD33-48FFDAFB4712} = {FBD2E07B-F25B-4D2F-AEF6-6D1E10F1E523} 79 | {07D358DF-D77A-434B-B034-95785DF7106F} = {4C63B329-66F2-473F-ABC3-B1AFE0990F47} 80 | {7C28B706-21F3-45EE-A9C3-B39AC5BB8AB5} = {4C63B329-66F2-473F-ABC3-B1AFE0990F47} 81 | EndGlobalSection 82 | GlobalSection(ExtensibilityGlobals) = postSolution 83 | SolutionGuid = {46DF0C22-7B6A-4A64-BC63-7B2F6A14F334} 84 | EndGlobalSection 85 | EndGlobal 86 | -------------------------------------------------------------------------------- /NLog.Extensions.Logging.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True 5 | True -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![NLog](https://nlog-project.org/images/NLog.png) 2 | 3 | # NLog.Extensions.Logging & NLog.Extensions.Hosting 4 | 5 | 6 | [![NuGet Release](https://img.shields.io/nuget/v/NLog.Extensions.Logging.svg?label=NLog.Extensions.Logging)](https://www.nuget.org/packages/NLog.Extensions.Logging) 7 | 8 | 9 | [![NuGet Release](https://img.shields.io/nuget/v/NLog.Extensions.Hosting.svg?label=NLog.Extensions.Hosting)](https://www.nuget.org/packages/NLog.Extensions.Hosting) 10 | 11 | 12 | [![Build status](https://ci.appveyor.com/api/projects/status/0nrg8cksp4b6tab1/branch/master?svg=true)](https://ci.appveyor.com/project/nlog/nlog-framework-logging/branch/master) 13 | [![](https://sonarcloud.io/api/project_badges/measure?project=nlog.extensions.logging&branch=master&metric=ncloc)](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master) 14 | [![](https://sonarcloud.io/api/project_badges/measure?project=nlog.extensions.logging&branch=master&metric=bugs)](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master) 15 | [![](https://sonarcloud.io/api/project_badges/measure?project=nlog.extensions.logging&branch=master&metric=vulnerabilities)](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master) 16 | [![](https://sonarcloud.io/api/project_badges/measure?project=nlog.extensions.logging&branch=master&metric=code_smells)](https://sonarcloud.io/project/issues?id=nlog.extensions.logging&branch=master&resolved=false&types=CODE_SMELL) 17 | [![](https://sonarcloud.io/api/project_badges/measure?project=nlog.extensions.logging&branch=master&metric=duplicated_lines_density)](https://sonarcloud.io/component_measures/domain/Duplications?id=nlog.extensions.logging&branch=master) 18 | [![](https://sonarcloud.io/api/project_badges/measure?project=nlog.extensions.logging&branch=master&metric=sqale_debt_ratio)](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master) 19 | [![](https://sonarcloud.io/api/project_badges/measure?project=nlog.extensions.logging&branch=master&metric=coverage)](https://sonarcloud.io/component_measures?id=nlog.extensions.logging&branch=master&metric=coverage) 20 | 21 | ## NLog.Extensions.Logging 22 | 23 | [NLog.Extensions.Logging](https://www.nuget.org/packages/NLog.Extensions.Logging) enables NLog as Logging Provider for [Microsoft ILogger](https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-GetCurrentClassLogger-and-Microsoft-ILogger)-abstraction and Dependency Injection. 24 | 25 | - Introduces `AddNLog()` extension methods to register NLog as LoggingProvider for Microsoft Extension Logging. 26 | - Introduces [${configsetting}](https://github.com/NLog/NLog/wiki/ConfigSetting-Layout-Renderer) for doing lookup of appsettings.json configuration settings. 27 | - Adds support for loading [NLog Configuration from appsettings.json](https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-configuration-with-appsettings.json) 28 | 29 | Notice the standard [NLog NuGet package](https://www.nuget.org/packages/NLog) is enough for using NLog Logger with simple console application on the .NET Core platform. 30 | Just add `NLog.config` file to the project, and follow the [tutorial](https://github.com/NLog/NLog/wiki/Tutorial#configure-nlog-targets-for-output) for using `GetCurrentClassLogger()`. 31 | 32 | ## NLog.Extensions.Hosting 33 | 34 | [NLog.Extensions.Hosting](https://www.nuget.org/packages/NLog.Extensions.Hosting) only introduces `UseNLog()` as extension-method for the `IHostBuilder`. 35 | 36 | > Note if using **ASP.NET Core** then instead install [NLog.Web.AspNetCore](https://www.nuget.org/packages/NLog.web.aspnetcore). 37 | 38 | ### Getting Started Tutorials: 39 | 40 | - [Getting started for ASP.NET Core 6](https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-6) 41 | - [Getting started for ASP.NET Core 5](https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-5) 42 | - [Getting started for ASP.NET Core 3.1](https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-3) 43 | - [Getting started for .NET Core Console application](https://github.com/NLog/NLog/wiki/Getting-started-with-.NET-Core-2---Console-application) 44 | - [How to use structured logging](https://github.com/NLog/NLog/wiki/How-to-use-structured-logging) 45 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 5.0.0-{build} # Only change for mayor versions (e.g. 6.0) 2 | image: Visual Studio 2022 3 | configuration: Release 4 | skip_tags: true 5 | 6 | environment: 7 | github_auth_token: 8 | secure: WYvd/k1xGCsDS+4iOhjzxA5/e36RjkxnuVOHpBR+eDtZNNjpYydCyNfd1COME9jI 9 | sonar_token: 10 | secure: OUI/jCbBF75TwKMPT+IfewdgwCgx9nQkRg3cYOEQNJeX5J2++oWS3dmpwO51XduP 11 | JAVA_HOME: C:\Program Files\Java\jdk17 12 | 13 | build_script: 14 | - ps: .\build.ps1 15 | 16 | nuget: 17 | disable_publish_on_pr: true 18 | 19 | artifacts: 20 | - path: 'artifacts\*.nupkg' 21 | - path: 'artifacts\*.snupkg' 22 | 23 | test_script: 24 | - nuget.exe install OpenCover -ExcludeVersion -DependencyVersion Ignore 25 | - OpenCover\tools\OpenCover.Console.exe -register:user -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -f net462 -c debug NLog.Extensions.Logging.Tests" -filter:"+[NLog.Extensions.Logging]* +[NLog.Extensions.Hosting]* -[NLog.Extensions.Logging.Tests]* -[NLog.Extensions.Hosting.Tests]*" -output:"coverage.xml" -oldstyle -targetdir:"test" 26 | - OpenCover\tools\OpenCover.Console.exe -register:user -mergeoutput -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -f net6.0 -c debug NLog.Extensions.Logging.Tests" -filter:"+[NLog.Extensions.Logging]* -[NLog.Extensions.Logging.Tests]*" -output:"coverage.xml" -oldstyle -targetdir:"test" 27 | - OpenCover\tools\OpenCover.Console.exe -register:user -mergeoutput -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -f net8.0 -c debug NLog.Extensions.Logging.Tests" -filter:"+[NLog.Extensions.Logging]* -[NLog.Extensions.Logging.Tests]*" -output:"coverage.xml" -oldstyle -targetdir:"test" 28 | - OpenCover\tools\OpenCover.Console.exe -register:user -mergeoutput -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -f net462 -c debug NLog.Extensions.Hosting.Tests" -filter:"+[NLog.Extensions.Logging]* +[NLog.Extensions.Hosting]* -[NLog.Extensions.Logging.Tests]* -[NLog.Extensions.Hosting.Tests]*" -output:"coverage.xml" -oldstyle -targetdir:"test" 29 | - OpenCover\tools\OpenCover.Console.exe -register:user -mergeoutput -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -f net6.0 -c debug NLog.Extensions.Hosting.Tests" -filter:"+[NLog.Extensions.Logging]* +[NLog.Extensions.Hosting]* -[NLog.Extensions.Logging.Tests]* -[NLog.Extensions.Hosting.Tests]*" -output:"coverage.xml" -oldstyle -targetdir:"test" 30 | - OpenCover\tools\OpenCover.Console.exe -register:user -mergeoutput -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -f net8.0 -c debug NLog.Extensions.Hosting.Tests" -filter:"+[NLog.Extensions.Logging]* +[NLog.Extensions.Hosting]* -[NLog.Extensions.Logging.Tests]* -[NLog.Extensions.Hosting.Tests]*" -output:"coverage.xml" -oldstyle -targetdir:"test" 31 | - pip install codecov 32 | - codecov -f "coverage.xml" 33 | - set PATH=%JAVA_HOME%\bin;%PATH% 34 | - ps: .\run-sonar.ps1 35 | - ps: .\run-tests.ps1 36 | 37 | deploy: 38 | - provider: NuGet 39 | api_key: 40 | secure: f6oWebyOFLpuuo2PMd6xgoxwMq+JvXVUmPyBme89zS7UF0zcvLYPSKN/p6B/KaMs 41 | on: 42 | branch: master 43 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | # restore and builds all projects as release. 2 | # creates NuGet package at \artifacts 3 | dotnet --version 4 | 5 | $versionPrefix = "6.0.0" 6 | $versionSuffix = "rc3" 7 | $versionFile = $versionPrefix + "." + ${env:APPVEYOR_BUILD_NUMBER} 8 | $versionProduct = $versionPrefix; 9 | 10 | if ($env:APPVEYOR_PULL_REQUEST_NUMBER) { 11 | $versionPrefix = $versionFile 12 | $versionSuffix = "PR" + $env:APPVEYOR_PULL_REQUEST_NUMBER 13 | } 14 | 15 | if (-Not $versionSuffix.Equals("")) { 16 | $versionProduct = $versionProduct + "-" + $versionSuffix 17 | } 18 | 19 | dotnet restore .\src\NLog.Extensions.Logging\ 20 | if (-Not $LastExitCode -eq 0) { 21 | exit $LastExitCode 22 | } 23 | 24 | dotnet restore .\src\NLog.Extensions.Hosting\ 25 | if (-Not $LastExitCode -eq 0) { 26 | exit $LastExitCode 27 | } 28 | 29 | msbuild /t:Pack .\src\NLog.Extensions.Logging\ /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /p:ContinuousIntegrationBuild=true 30 | if (-Not $LastExitCode -eq 0) { 31 | exit $LastExitCode 32 | } 33 | 34 | msbuild /t:Pack .\src\NLog.Extensions.Hosting\ /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /p:ContinuousIntegrationBuild=true 35 | if (-Not $LastExitCode -eq 0) { 36 | exit $LastExitCode 37 | } 38 | 39 | exit $LastExitCode 40 | -------------------------------------------------------------------------------- /examples/NetCore2/ConsoleExample/ConsoleExample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | latest 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | PreserveNewest 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /examples/NetCore2/ConsoleExample/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Logging; 5 | using NLog; 6 | using NLog.Extensions.Logging; 7 | 8 | namespace ConsoleExample 9 | { 10 | internal static class Program 11 | { 12 | private static void Main() 13 | { 14 | var config = new ConfigurationBuilder().Build(); 15 | 16 | var logger = LogManager.Setup() 17 | .SetupExtensions(ext => ext.RegisterConfigSettings(config)) 18 | .GetCurrentClassLogger(); 19 | 20 | try 21 | { 22 | using var servicesProvider = new ServiceCollection() 23 | .AddTransient() // Runner is the custom class 24 | .AddLogging(loggingBuilder => 25 | { 26 | // configure Logging with NLog 27 | loggingBuilder.ClearProviders(); 28 | loggingBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); 29 | loggingBuilder.AddNLog(config); 30 | }).BuildServiceProvider(); 31 | 32 | var runner = servicesProvider.GetRequiredService(); 33 | runner.DoAction("Action1"); 34 | 35 | Console.WriteLine("Press ANY key to exit"); 36 | Console.ReadKey(); 37 | } 38 | catch (Exception ex) 39 | { 40 | // NLog: catch any exception and log it. 41 | logger.Error(ex, "Stopped program because of exception"); 42 | throw; 43 | } 44 | finally 45 | { 46 | // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux) 47 | LogManager.Shutdown(); 48 | } 49 | } 50 | } 51 | 52 | public class Runner 53 | { 54 | private readonly ILogger _logger; 55 | 56 | public Runner(ILogger logger) 57 | { 58 | _logger = logger; 59 | } 60 | 61 | public void DoAction(string name) 62 | { 63 | _logger.LogDebug(20, "Doing hard work! {Action}", name); 64 | _logger.LogInformation(21, "Doing hard work! {Action}", name); 65 | _logger.LogWarning(22, "Doing hard work! {Action}", name); 66 | _logger.LogError(23, "Doing hard work! {Action}", name); 67 | _logger.LogCritical(24, "Doing hard work! {Action}", name); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /examples/NetCore2/ConsoleExample/nlog.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 9 | 10 | 11 | 12 | 13 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/NetCore2/ConsoleExampleJsonConfig/ConsoleExampleJsonConfig.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | latest 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | PreserveNewest 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /examples/NetCore2/ConsoleExampleJsonConfig/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Logging; 5 | using NLog; 6 | using NLog.Extensions.Logging; 7 | 8 | namespace ConsoleExample 9 | { 10 | internal static class Program 11 | { 12 | private static void Main() 13 | { 14 | var config = new ConfigurationBuilder() 15 | .SetBasePath(System.IO.Directory.GetCurrentDirectory()) 16 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 17 | .Build(); 18 | 19 | var logger = LogManager.Setup() 20 | .SetupExtensions(s => s.RegisterConfigSettings(config)) 21 | .LoadConfigurationFromSection(config) 22 | .GetCurrentClassLogger(); 23 | 24 | try 25 | { 26 | using var servicesProvider = new ServiceCollection() 27 | .AddTransient() // Runner is the custom class 28 | .AddLogging(loggingBuilder => 29 | { 30 | // configure Logging with NLog 31 | loggingBuilder.ClearProviders(); 32 | loggingBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); 33 | loggingBuilder.AddNLog(config); 34 | }).BuildServiceProvider(); 35 | 36 | var runner = servicesProvider.GetRequiredService(); 37 | runner.DoAction("Action1"); 38 | 39 | Console.WriteLine("Press ANY key to exit"); 40 | Console.ReadKey(); 41 | } 42 | catch (Exception ex) 43 | { 44 | // NLog: catch any exception and log it. 45 | logger.Error(ex, "Stopped program because of exception"); 46 | throw; 47 | } 48 | finally 49 | { 50 | // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux) 51 | LogManager.Shutdown(); 52 | } 53 | } 54 | } 55 | 56 | public class Runner 57 | { 58 | private readonly ILogger _logger; 59 | 60 | public Runner(ILogger logger) 61 | { 62 | _logger = logger; 63 | } 64 | 65 | public void DoAction(string name) 66 | { 67 | _logger.LogDebug(20, "Doing hard work! {Action}", name); 68 | _logger.LogInformation(21, "Doing hard work! {Action}", name); 69 | _logger.LogWarning(22, "Doing hard work! {Action}", name); 70 | _logger.LogError(23, "Doing hard work! {Action}", name); 71 | _logger.LogCritical(24, "Doing hard work! {Action}", name); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /examples/NetCore2/ConsoleExampleJsonConfig/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "NLog": { 4 | "IncludeScopes": false, 5 | "ParseMessageTemplates": true, 6 | "CaptureMessageProperties": true 7 | } 8 | }, 9 | "NLog": { 10 | "autoreload": true, 11 | "internalLogLevel": "Info", 12 | "internalLogFile": "c:/temp/console-example-internal2.log", 13 | "throwConfigExceptions": true, 14 | "targets": { 15 | "console": { 16 | "type": "Console", 17 | "layout": "${date}|${level:uppercase=true}|${message} ${exception:format=tostring}|${logger}|${all-event-properties}" 18 | }, 19 | "file": { 20 | "type": "AsyncWrapper", 21 | "target": { 22 | "wrappedFile": { 23 | "type": "File", 24 | "fileName": "c:/temp/console-example2.log", 25 | "layout": { 26 | "type": "JsonLayout", 27 | "Attributes": [ 28 | { "name": "timestamp", "layout": "${date:format=o}" }, 29 | { "name": "level", "layout": "${level}" }, 30 | { "name": "logger", "layout": "${logger}" }, 31 | { "name": "message", "layout": "${message:raw=true}" }, 32 | { "name": "properties", "encode": false, "layout": { "type": "JsonLayout", "includeallproperties": "true" } } 33 | ] 34 | } 35 | } 36 | } 37 | } 38 | }, 39 | "rules": [ 40 | { 41 | "logger": "*", 42 | "minLevel": "Trace", 43 | "writeTo": "File,Console" 44 | } 45 | ] 46 | } 47 | } -------------------------------------------------------------------------------- /examples/NetCore2/HostingExample/HostingExample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | true 8 | 9 | true 10 | true 11 | true 12 | true 13 | win-x64 14 | true 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | PreserveNewest 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/NetCore2/HostingExample/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Hosting; 7 | using Microsoft.Extensions.Logging; 8 | using NLog; 9 | using NLog.Extensions.Hosting; 10 | 11 | namespace HostingExample 12 | { 13 | public class Program 14 | { 15 | private static async Task Main() 16 | { 17 | var config = new ConfigurationBuilder().Build(); 18 | 19 | var logger = LogManager.Setup() 20 | .SetupExtensions(ext => ext.RegisterHostSettings(config)) 21 | .GetCurrentClassLogger(); 22 | 23 | try 24 | { 25 | var hostBuilder = new HostBuilder() 26 | .ConfigureLogging(builder => builder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace)) 27 | .ConfigureServices((hostContext, services) => services.AddHostedService()) 28 | .UseNLog(); 29 | 30 | // Build and run the host in one go; .RCA is specialized for running it in a console. 31 | // It registers SIGTERM(Ctrl-C) to the CancellationTokenSource that's shared with all services in the container. 32 | await hostBuilder.RunConsoleAsync(); 33 | 34 | Console.WriteLine("The host container has terminated. Press ANY key to exit the console."); 35 | Console.ReadKey(); 36 | } 37 | catch (Exception ex) 38 | { 39 | // NLog: catch setup errors (exceptions thrown inside of any containers may not necessarily be caught) 40 | logger.Fatal(ex, "Stopped program because of exception"); 41 | throw; 42 | } 43 | finally 44 | { 45 | // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux) 46 | LogManager.Shutdown(); 47 | } 48 | } 49 | 50 | public class ConsoleHostedService : BackgroundService 51 | { 52 | private readonly ILogger _logger; 53 | 54 | public ConsoleHostedService(ILogger logger) 55 | { 56 | _logger = logger; 57 | _logger.LogInformation("ConsoleHostedService instance created..."); 58 | } 59 | 60 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 61 | { 62 | _logger.LogInformation("Hello from your hosted service thread!"); 63 | _logger.LogTrace("I may or may not return for a long time depending on what I do."); 64 | _logger.LogDebug("In this example, I return right away, but my host will continue to run until"); 65 | _logger.LogInformation("its CancellationToken is Cancelled (SIGTERM(Ctrl-C) or a Lifetime Event )"); 66 | await Task.CompletedTask; 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /examples/NetCore2/HostingExample/nlog.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 9 | 10 | 11 | 12 | 13 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /run-sonar.ps1: -------------------------------------------------------------------------------- 1 | $projectFile = "src\NLog.Extensions.Logging\NLog.Extensions.Logging.csproj" 2 | $sonarQubeId = "nlog.extensions.logging" 3 | $github = "nlog/NLog.Extensions.Logging" 4 | $baseBranch = "master" 5 | $framework = "netstandard2.0" 6 | $sonarOrg = "nlog" 7 | 8 | 9 | if ($env:APPVEYOR_REPO_NAME -eq $github) { 10 | 11 | if (-not $env:sonar_token) { 12 | Write-warning "Sonar: not running SonarQube, no sonar_token" 13 | return; 14 | } 15 | 16 | $prMode = $false; 17 | 18 | if ($env:APPVEYOR_PULL_REQUEST_NUMBER) { 19 | # first check PR as that is on the base branch 20 | $prMode = $true; 21 | } 22 | 23 | dotnet tool install --global dotnet-sonarscanner --version 5.13.1 24 | if (-Not $LastExitCode -eq 0) { 25 | exit $LastExitCode 26 | } 27 | 28 | $sonarUrl = "https://sonarcloud.io" 29 | $sonarToken = $env:sonar_token 30 | $buildVersion = $env:APPVEYOR_BUILD_VERSION 31 | 32 | if ($prMode) { 33 | $branch = $env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH 34 | $prBaseBranch = $env:APPVEYOR_REPO_BRANCH; 35 | $pr = $env:APPVEYOR_PULL_REQUEST_NUMBER 36 | 37 | Write-Host "Sonar: on PR $pr from $branch to $prBaseBranch" -ForegroundColor DarkGreen -BackgroundColor White 38 | dotnet-sonarscanner begin /o:"$sonarOrg" /k:"$sonarQubeId" /d:"sonar.host.url=$sonarUrl" /d:"sonar.login=$sonarToken" /v:"$buildVersion" /d:"sonar.cs.opencover.reportsPaths=coverage.xml" /d:"sonar.pullrequest.key=$pr" /d:"sonar.pullrequest.branch=$branch" /d:"sonar.pullrequest.base=$prBaseBranch" /d:"sonar.github.repository=$github" /d:"sonar.github.oauth=$env:github_auth_token" 39 | } 40 | else { 41 | $branch = $env:APPVEYOR_REPO_BRANCH; 42 | 43 | Write-Host "Sonar: on branch $branch" -ForegroundColor DarkGreen -BackgroundColor White 44 | dotnet-sonarscanner begin /o:"$sonarOrg" /k:"$sonarQubeId" /d:"sonar.host.url=$sonarUrl" /d:"sonar.login=$sonarToken" /v:"$buildVersion" /d:"sonar.cs.opencover.reportsPaths=coverage.xml" /d:"sonar.branch.name=$branch" 45 | } 46 | 47 | if (-Not $LastExitCode -eq 0) { 48 | exit $LastExitCode 49 | } 50 | 51 | msbuild /t:Rebuild $projectFile /p:targetFrameworks=$framework /verbosity:minimal 52 | if (-Not $LastExitCode -eq 0) { 53 | exit $LastExitCode 54 | } 55 | 56 | dotnet-sonarscanner end /d:"sonar.login=$env:sonar_token" 57 | if (-Not $LastExitCode -eq 0) { 58 | exit $LastExitCode 59 | } 60 | } 61 | else { 62 | Write-Host "Sonar: not running as we're on '$env:APPVEYOR_REPO_NAME'" 63 | } 64 | -------------------------------------------------------------------------------- /run-tests.ps1: -------------------------------------------------------------------------------- 1 | dotnet restore test/NLog.Extensions.Logging.Tests -v minimal 2 | dotnet restore test/NLog.Extensions.Hosting.Tests -v minimal 3 | 4 | dotnet build test/NLog.Extensions.Logging.Tests --configuration release -v minimal 5 | if (-Not $LastExitCode -eq 0) 6 | { exit $LastExitCode } 7 | 8 | dotnet build test/NLog.Extensions.Hosting.Tests --configuration release -v minimal 9 | if (-Not $LastExitCode -eq 0) 10 | { exit $LastExitCode } 11 | 12 | dotnet test test/NLog.Extensions.Logging.Tests --configuration release 13 | if (-Not $LastExitCode -eq 0) 14 | { exit $LastExitCode } 15 | 16 | dotnet test test/NLog.Extensions.Hosting.Tests --configuration release 17 | if (-Not $LastExitCode -eq 0) 18 | { exit $LastExitCode } 19 | 20 | dotnet restore 21 | dotnet list ./ package --vulnerable --include-transitive | findstr /S /c:"has the following vulnerable packages" 22 | if (-Not $LastExitCode -eq 1) 23 | { 24 | dotnet list ./ package --vulnerable --include-transitive 25 | exit 1 26 | } 27 | 28 | dotnet publish -r win-x64 -c release --self-contained .\examples\NetCore2\HostingExample 29 | if (-Not $LastExitCode -eq 0) 30 | { exit $LastExitCode } -------------------------------------------------------------------------------- /src/NLog.Extensions.Hosting/Config/SetupExtensionsBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.Hosting; 3 | using NLog.Config; 4 | using NLog.Extensions.Logging; 5 | 6 | namespace NLog.Extensions.Hosting 7 | { 8 | /// 9 | /// Extension methods to setup NLog extensions, so they are known when loading NLog LoggingConfiguration 10 | /// 11 | public static class SetupExtensionsBuilderExtensions 12 | { 13 | /// 14 | /// Setup the MEL-configuration for the ${configsetting} layoutrenderer 15 | /// 16 | public static ISetupExtensionsBuilder RegisterHostSettings(this ISetupExtensionsBuilder setupBuilder, IConfiguration? configuration) 17 | { 18 | setupBuilder.RegisterConfigSettings(configuration); 19 | return setupBuilder.RegisterLayoutRenderer("host-appname") 20 | .RegisterLayoutRenderer("host-rootdir") 21 | .RegisterLayoutRenderer("host-environment"); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Hosting/Extensions/ConfigureExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using NLog.Config; 7 | using NLog.Extensions.Logging; 8 | #if NETSTANDARD2_0 9 | using IHostEnvironment = Microsoft.Extensions.Hosting.IHostingEnvironment; 10 | #endif 11 | 12 | namespace NLog.Extensions.Hosting 13 | { 14 | /// 15 | /// Helpers for IHostbuilder 16 | /// 17 | public static class ConfigureExtensions 18 | { 19 | /// 20 | /// Enable NLog as logging provider for Microsoft Extension Logging 21 | /// 22 | /// 23 | /// IHostBuilder for chaining 24 | public static IHostBuilder UseNLog(this IHostBuilder builder) 25 | { 26 | Guard.ThrowIfNull(builder); 27 | return builder.UseNLog(null); 28 | } 29 | 30 | /// 31 | /// Enable NLog as logging provider for Microsoft Extension Logging 32 | /// 33 | /// 34 | /// NLogProviderOptions object to configure NLog behavior 35 | /// IHostBuilder for chaining 36 | public static IHostBuilder UseNLog(this IHostBuilder builder, NLogProviderOptions? options) 37 | { 38 | Guard.ThrowIfNull(builder); 39 | #if NETSTANDARD2_0 40 | builder.ConfigureServices((builderContext, services) => AddNLogLoggerProvider(services, builderContext.Configuration, null, options, CreateNLogLoggerProvider)); 41 | #else 42 | builder.ConfigureServices((builderContext, services) => AddNLogLoggerProvider(services, builderContext.Configuration, builderContext.HostingEnvironment, options, CreateNLogLoggerProvider)); 43 | #endif 44 | return builder; 45 | } 46 | 47 | /// 48 | /// Enable NLog as logging provider for Microsoft Extension Logging 49 | /// 50 | /// 51 | /// NLogProviderOptions object to configure NLog behavior 52 | /// Initialize NLog LogFactory with NLog LoggingConfiguration. 53 | /// IHostBuilder for chaining 54 | public static IHostBuilder UseNLog(this IHostBuilder builder, NLogProviderOptions options, Func factoryBuilder) 55 | { 56 | Guard.ThrowIfNull(builder); 57 | #if NETSTANDARD2_0 58 | builder.ConfigureServices((builderContext, services) => AddNLogLoggerProvider(services, builderContext.Configuration, null, options, (serviceProvider, config, hostEnv, opt) => 59 | #else 60 | builder.ConfigureServices((builderContext, services) => AddNLogLoggerProvider(services, builderContext.Configuration, builderContext.HostingEnvironment, options, (serviceProvider, config, hostEnv, opt) => 61 | #endif 62 | { 63 | RegisterHostNLogExtensions(LogManager.LogFactory, serviceProvider, hostEnv); 64 | config = serviceProvider.SetupNLogConfigSettings(config, LogManager.LogFactory); 65 | 66 | // Delay initialization of targets until we have loaded config-settings 67 | var logFactory = factoryBuilder(serviceProvider); 68 | var provider = CreateNLogLoggerProvider(serviceProvider, config, hostEnv, opt, logFactory); 69 | return provider; 70 | })); 71 | return builder; 72 | } 73 | 74 | #if NET8_0_OR_GREATER 75 | /// 76 | /// Enable NLog as logging provider for Microsoft Extension Logging 77 | /// 78 | /// 79 | /// IHostApplicationBuilder for chaining 80 | public static IHostApplicationBuilder UseNLog(this IHostApplicationBuilder builder) 81 | { 82 | Guard.ThrowIfNull(builder); 83 | return builder.UseNLog(null); 84 | } 85 | 86 | /// 87 | /// Enable NLog as logging provider for Microsoft Extension Logging 88 | /// 89 | /// 90 | /// NLogProviderOptions object to configure NLog behavior 91 | /// IHostApplicationBuilder for chaining 92 | public static IHostApplicationBuilder UseNLog(this IHostApplicationBuilder builder, NLogProviderOptions? options) 93 | { 94 | Guard.ThrowIfNull(builder); 95 | builder.Services.TryAddNLogLoggingProvider((svc, addlogging) => svc.AddLogging(addlogging), builder.Configuration, options, (serviceProvider, config, opt) => CreateNLogLoggerProvider(serviceProvider, config, builder.Environment, opt)); 96 | return builder; 97 | } 98 | 99 | /// 100 | /// Enable NLog as logging provider for Microsoft Extension Logging 101 | /// 102 | /// 103 | /// NLogProviderOptions object to configure NLog behavior 104 | /// Initialize NLog LogFactory with NLog LoggingConfiguration. 105 | /// IHostApplicationBuilder for chaining 106 | public static IHostApplicationBuilder UseNLog(this IHostApplicationBuilder builder, NLogProviderOptions options, Func factoryBuilder) 107 | { 108 | Guard.ThrowIfNull(builder); 109 | builder.Services.TryAddNLogLoggingProvider((svc, addlogging) => svc.AddLogging(addlogging), builder.Configuration, options, (serviceProvider, config, opt) => 110 | { 111 | RegisterHostNLogExtensions(LogManager.LogFactory, serviceProvider, builder.Environment); 112 | config = serviceProvider.SetupNLogConfigSettings(config, LogManager.LogFactory); 113 | 114 | // Delay initialization of targets until we have loaded config-settings 115 | var logFactory = factoryBuilder(serviceProvider); 116 | var provider = CreateNLogLoggerProvider(serviceProvider, config, builder.Environment, opt, logFactory); 117 | return provider; 118 | }); 119 | return builder; 120 | } 121 | #endif 122 | 123 | private static void AddNLogLoggerProvider(IServiceCollection services, IConfiguration? hostConfiguration, IHostEnvironment? hostEnvironment, NLogProviderOptions? options, Func factory) 124 | { 125 | services.TryAddNLogLoggingProvider((svc, addlogging) => svc.AddLogging(addlogging), hostConfiguration, options, (provider, cfg, opt) => factory(provider, cfg, hostEnvironment, opt)); 126 | } 127 | 128 | private static NLogLoggerProvider CreateNLogLoggerProvider(IServiceProvider serviceProvider, IConfiguration? hostConfiguration, IHostEnvironment? hostEnvironment, NLogProviderOptions options) 129 | { 130 | return CreateNLogLoggerProvider(serviceProvider, hostConfiguration, hostEnvironment, options, LogManager.LogFactory); 131 | } 132 | 133 | private static NLogLoggerProvider CreateNLogLoggerProvider(IServiceProvider serviceProvider, IConfiguration? hostConfiguration, IHostEnvironment? hostEnvironment, NLogProviderOptions options, LogFactory logFactory) 134 | { 135 | RegisterHostNLogExtensions(logFactory, serviceProvider, hostEnvironment); 136 | 137 | NLogLoggerProvider provider = serviceProvider.CreateNLogLoggerProvider(hostConfiguration, options, logFactory); 138 | 139 | string nlogConfigFile = string.Empty; 140 | var contentRootPath = hostEnvironment?.ContentRootPath; 141 | var environmentName = hostEnvironment?.EnvironmentName; 142 | if (!string.IsNullOrWhiteSpace(contentRootPath) || !string.IsNullOrWhiteSpace(environmentName)) 143 | { 144 | provider.LogFactory.Setup().LoadConfiguration(cfg => 145 | { 146 | if (!IsLoggingConfigurationLoaded(cfg.Configuration)) 147 | { 148 | nlogConfigFile = ResolveEnvironmentNLogConfigFile(contentRootPath, environmentName); 149 | #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. 150 | cfg.Configuration = null; 151 | #pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. 152 | } 153 | }); 154 | } 155 | 156 | if (!string.IsNullOrEmpty(nlogConfigFile)) 157 | { 158 | provider.LogFactory.Setup().LoadConfigurationFromFile(nlogConfigFile, optional: true); 159 | } 160 | 161 | provider.LogFactory.Setup().SetupLogFactory(ext => ext.AddCallSiteHiddenAssembly(typeof(ConfigureExtensions).Assembly)); 162 | return provider; 163 | } 164 | 165 | private static void RegisterHostNLogExtensions(LogFactory logFactory, IServiceProvider serviceProvider, IHostEnvironment? hostingEnvironment) 166 | { 167 | (logFactory ?? LogManager.LogFactory).Setup().SetupExtensions(ext => 168 | { 169 | if (serviceProvider != null) 170 | ext.RegisterServiceProvider(serviceProvider); 171 | if (hostingEnvironment != null) 172 | ext.RegisterSingletonService(hostingEnvironment); 173 | ext.RegisterLayoutRenderer("host-environment"); 174 | ext.RegisterLayoutRenderer("host-rootdir"); 175 | ext.RegisterLayoutRenderer("host-appname"); 176 | }); 177 | } 178 | 179 | private static string ResolveEnvironmentNLogConfigFile(string? basePath, string? environmentName) 180 | { 181 | if (!string.IsNullOrWhiteSpace(basePath)) 182 | { 183 | if (!string.IsNullOrWhiteSpace(environmentName)) 184 | { 185 | var nlogConfigEnvFilePath = Path.Combine(basePath, $"nlog.{environmentName}.config"); 186 | if (File.Exists(nlogConfigEnvFilePath)) 187 | return Path.GetFullPath(nlogConfigEnvFilePath); 188 | nlogConfigEnvFilePath = Path.Combine(basePath, $"NLog.{environmentName}.config"); 189 | if (File.Exists(nlogConfigEnvFilePath)) 190 | return Path.GetFullPath(nlogConfigEnvFilePath); 191 | } 192 | 193 | var nlogConfigFilePath = Path.Combine(basePath, "nlog.config"); 194 | if (File.Exists(nlogConfigFilePath)) 195 | return Path.GetFullPath(nlogConfigFilePath); 196 | nlogConfigFilePath = Path.Combine(basePath, "NLog.config"); 197 | if (File.Exists(nlogConfigFilePath)) 198 | return Path.GetFullPath(nlogConfigFilePath); 199 | } 200 | 201 | if (!string.IsNullOrWhiteSpace(environmentName)) 202 | return $"nlog.{environmentName}.config"; 203 | 204 | return string.Empty; 205 | } 206 | 207 | private static bool IsLoggingConfigurationLoaded(LoggingConfiguration cfg) 208 | { 209 | return cfg?.LoggingRules?.Count > 0 && cfg?.AllTargets?.Count > 0; 210 | } 211 | } 212 | } -------------------------------------------------------------------------------- /src/NLog.Extensions.Hosting/LayoutRenderers/HostAppNameLayoutRenderer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using Microsoft.Extensions.Hosting; 4 | using NLog.Config; 5 | using NLog.LayoutRenderers; 6 | 7 | #if NETSTANDARD2_0 8 | using IHostEnvironment = Microsoft.Extensions.Hosting.IHostingEnvironment; 9 | #endif 10 | 11 | namespace NLog.Extensions.Hosting 12 | { 13 | /// 14 | /// Rendering development environment. 15 | /// 16 | /// 17 | /// ${host-environment} 18 | /// 19 | /// Documentation on NLog Wiki 20 | [LayoutRenderer("host-appname")] 21 | [ThreadAgnostic] 22 | public class HostAppNameLayoutRenderer : LayoutRenderer 23 | { 24 | /// 25 | /// Provides access to the current IHostEnvironment 26 | /// 27 | /// IHostEnvironment or null 28 | internal IHostEnvironment? HostEnvironment => _hostEnvironment ?? (_hostEnvironment = ResolveHostEnvironment()); 29 | private IHostEnvironment? _hostEnvironment; 30 | private string? _hostAppName; 31 | private static string? _currentProcessName; 32 | 33 | /// 34 | protected override void Append(StringBuilder builder, LogEventInfo logEvent) 35 | { 36 | var environmentName = _hostAppName ?? (_hostAppName = ResolveHostAppName()); 37 | builder.Append(environmentName ?? ResolveProcessName()); 38 | } 39 | 40 | private IHostEnvironment? ResolveHostEnvironment() 41 | { 42 | return ResolveService(); 43 | } 44 | 45 | private string? ResolveHostAppName() 46 | { 47 | try 48 | { 49 | var appName = HostEnvironment?.ApplicationName; 50 | return string.IsNullOrWhiteSpace(appName) ? null : appName; 51 | } 52 | catch 53 | { 54 | return null; 55 | } 56 | } 57 | 58 | private static string ResolveProcessName() 59 | { 60 | if (_currentProcessName is null) 61 | _currentProcessName = System.Diagnostics.Process.GetCurrentProcess()?.ProcessName ?? "UnknownHostName"; 62 | return _currentProcessName; 63 | } 64 | 65 | /// 66 | protected override void CloseLayoutRenderer() 67 | { 68 | _hostEnvironment = null; 69 | _hostAppName = null; 70 | base.CloseLayoutRenderer(); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/NLog.Extensions.Hosting/LayoutRenderers/HostEnvironmentLayoutRenderer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using Microsoft.Extensions.Hosting; 4 | using NLog.Config; 5 | using NLog.LayoutRenderers; 6 | 7 | #if NETSTANDARD2_0 8 | using IHostEnvironment = Microsoft.Extensions.Hosting.IHostingEnvironment; 9 | #endif 10 | 11 | namespace NLog.Extensions.Hosting 12 | { 13 | /// 14 | /// Rendering development environment. 15 | /// 16 | /// 17 | /// ${host-environment} 18 | /// 19 | /// Documentation on NLog Wiki 20 | [LayoutRenderer("host-environment")] 21 | [ThreadAgnostic] 22 | public class HostEnvironmentLayoutRenderer : LayoutRenderer 23 | { 24 | /// 25 | /// Provides access to the current IHostEnvironment 26 | /// 27 | /// IHostEnvironment or null 28 | internal IHostEnvironment? HostEnvironment => _hostEnvironment ?? (_hostEnvironment = ResolveHostEnvironment()); 29 | private IHostEnvironment? _hostEnvironment; 30 | private string? _environmentName; 31 | 32 | /// 33 | protected override void Append(StringBuilder builder, LogEventInfo logEvent) 34 | { 35 | var environmentName = _environmentName ?? (_environmentName = ResolveEnvironmentName()); 36 | builder.Append(environmentName ?? "Production"); 37 | } 38 | 39 | private IHostEnvironment? ResolveHostEnvironment() 40 | { 41 | return ResolveService(); 42 | } 43 | 44 | private string? ResolveEnvironmentName() 45 | { 46 | string? environmentName = null; 47 | try 48 | { 49 | environmentName = HostEnvironment?.EnvironmentName; 50 | } 51 | catch 52 | { 53 | environmentName = null; 54 | } 55 | if (string.IsNullOrEmpty(environmentName)) 56 | { 57 | environmentName = GetDotnetHostEnvironment("ASPNETCORE_ENVIRONMENT") ?? GetDotnetHostEnvironment("DOTNET_ENVIRONMENT"); 58 | } 59 | return string.IsNullOrEmpty(environmentName) ? null : environmentName; 60 | } 61 | 62 | /// 63 | protected override void CloseLayoutRenderer() 64 | { 65 | _hostEnvironment = null; 66 | _environmentName = null; 67 | base.CloseLayoutRenderer(); 68 | } 69 | 70 | private static string? GetDotnetHostEnvironment(string variableName) 71 | { 72 | try 73 | { 74 | var environment = Environment.GetEnvironmentVariable(variableName); 75 | if (string.IsNullOrWhiteSpace(environment)) 76 | return null; 77 | 78 | return environment.Trim(); 79 | } 80 | catch (Exception ex) 81 | { 82 | NLog.Common.InternalLogger.Error(ex, "Failed to lookup environment variable {0}", variableName); 83 | return null; 84 | } 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /src/NLog.Extensions.Hosting/LayoutRenderers/HostRootDirLayoutRenderer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using NLog; 5 | using NLog.Config; 6 | using NLog.LayoutRenderers; 7 | 8 | #if NETSTANDARD2_0 9 | using IHostEnvironment = Microsoft.Extensions.Hosting.IHostingEnvironment; 10 | #endif 11 | 12 | namespace Microsoft.Extensions.Hosting 13 | { 14 | /// 15 | /// Rendering Application Host 16 | /// 17 | /// 18 | /// ${host-rootdir} 19 | /// 20 | /// Documentation on NLog Wiki 21 | [LayoutRenderer("host-rootdir")] 22 | [ThreadAgnostic] 23 | public class HostRootDirLayoutRenderer : LayoutRenderer 24 | { 25 | /// 26 | /// Provides access to the current IHostEnvironment 27 | /// 28 | /// IHostEnvironment or null 29 | internal IHostEnvironment? HostEnvironment => _hostEnvironment ?? (_hostEnvironment = ResolveHostEnvironment()); 30 | private IHostEnvironment? _hostEnvironment; 31 | private string? _contentRootPath; 32 | private static string? _currentAppPath; 33 | 34 | /// 35 | protected override void Append(StringBuilder builder, LogEventInfo logEvent) 36 | { 37 | var contentRootPath = _contentRootPath ?? (_contentRootPath = ResolveContentRootPath()); 38 | builder.Append(contentRootPath ?? ResolveCurrentAppDirectory()); 39 | } 40 | 41 | private IHostEnvironment? ResolveHostEnvironment() 42 | { 43 | return ResolveService(); 44 | } 45 | 46 | private string? ResolveContentRootPath() 47 | { 48 | string? contentRootPath = null; 49 | try 50 | { 51 | contentRootPath = HostEnvironment?.ContentRootPath; 52 | } 53 | catch 54 | { 55 | contentRootPath = null; 56 | } 57 | if (string.IsNullOrEmpty(contentRootPath)) 58 | { 59 | contentRootPath = GetDotnetHostEnvironment("ASPNETCORE_CONTENTROOT") ?? GetDotnetHostEnvironment("DOTNET_CONTENTROOT"); 60 | } 61 | return TrimEndDirectorySeparator(contentRootPath); 62 | } 63 | 64 | private static string? TrimEndDirectorySeparator(string? directoryPath) 65 | { 66 | return (directoryPath is null || string.IsNullOrEmpty(directoryPath)) ? null : directoryPath.TrimEnd(Path.DirectorySeparatorChar).TrimEnd(Path.AltDirectorySeparatorChar); 67 | } 68 | 69 | private static string? ResolveCurrentAppDirectory() 70 | { 71 | if (!string.IsNullOrEmpty(_currentAppPath)) 72 | return _currentAppPath; 73 | 74 | var currentAppPath = AppContext.BaseDirectory; 75 | 76 | try 77 | { 78 | var currentBasePath = Environment.CurrentDirectory; 79 | var normalizeCurDir = Path.GetFullPath(currentBasePath).TrimEnd(Path.DirectorySeparatorChar).TrimEnd(Path.AltDirectorySeparatorChar).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); 80 | var normalizeAppDir = Path.GetFullPath(currentAppPath).TrimEnd(Path.DirectorySeparatorChar).TrimEnd(Path.AltDirectorySeparatorChar).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); 81 | if (string.IsNullOrEmpty(normalizeCurDir) || !normalizeCurDir.StartsWith(normalizeAppDir, StringComparison.OrdinalIgnoreCase)) 82 | { 83 | currentBasePath = currentAppPath; // Avoid using Windows-System32 as current directory 84 | } 85 | return _currentAppPath = TrimEndDirectorySeparator(currentBasePath); 86 | } 87 | catch 88 | { 89 | // Not supported or access denied 90 | return _currentAppPath = TrimEndDirectorySeparator(currentAppPath); 91 | } 92 | } 93 | 94 | /// 95 | protected override void InitializeLayoutRenderer() 96 | { 97 | ResolveCurrentAppDirectory(); // Capture current directory at startup, before it changes 98 | base.InitializeLayoutRenderer(); 99 | } 100 | 101 | /// 102 | protected override void CloseLayoutRenderer() 103 | { 104 | _hostEnvironment = null; 105 | _contentRootPath = null; 106 | base.CloseLayoutRenderer(); 107 | } 108 | 109 | private static string? GetDotnetHostEnvironment(string variableName) 110 | { 111 | try 112 | { 113 | var environment = Environment.GetEnvironmentVariable(variableName); 114 | if (string.IsNullOrWhiteSpace(environment)) 115 | return null; 116 | 117 | return environment.Trim(); 118 | } 119 | catch (Exception ex) 120 | { 121 | NLog.Common.InternalLogger.Error(ex, "Failed to lookup environment variable {0}", variableName); 122 | return null; 123 | } 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /src/NLog.Extensions.Hosting/NLog.Extensions.Hosting.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net462;netstandard2.0;netstandard2.1;net6.0;net8.0 5 | Full 6 | 7 | NLog.Extensions.Hosting v$(ProductVersion) 8 | $(ProductVersion) 9 | 10 | Julian Verdurmen;Bryan Gonzalez 11 | NLog 12 | $([System.DateTime]::Now.ToString(yyyy)) 13 | Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ 14 | NLog extension for Microsoft.Extensions.Hosting for logging in .NET Standard libraries and .NET Core applications using IHostBuilder. 15 | 16 | For ASP.NET Core, check: https://www.nuget.org/packages/NLog.Web.AspNetCore 17 | 18 | NLog;Microsoft.Extensions.Hosting;log;logging;logfiles;netcore 19 | 20 | Full changelog: https://github.com/NLog/NLog.Extensions.Logging/blob/master/CHANGELOG.MD 21 | 22 | https://github.com/NLog/NLog.Extensions.Logging 23 | https://github.com/NLog/NLog.Extensions.Logging.git 24 | git 25 | BSD-2-Clause 26 | N.png 27 | README.md 28 | 29 | 30 | {548E65CE-0378-4812-AE00-B173F1251D3C} 31 | true 32 | 6.0.0.0 33 | ..\NLog.snk 34 | true 35 | latest 36 | 37 | true 38 | true 39 | enable 40 | 9 41 | true 42 | true 43 | true 44 | 45 | 46 | 47 | NLog.Extensions.Hosting for .NET Framework 4.6.2 48 | true 49 | 50 | 51 | NLog.Extensions.Hosting for .NET Standard 2.0 52 | 53 | 54 | NLog.Extensions.Hosting for .NET Standard 2.1 55 | 56 | 57 | NLog.Extensions.Hosting for .NET 6 58 | 59 | 60 | NLog.Extensions.Hosting for .NET 8 61 | 62 | 63 | $(Title) 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Hosting/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: ComVisible(false)] 5 | 6 | [assembly: InternalsVisibleTo("NLog.Extensions.Hosting.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100772391E63C104728ADCF18E2390474262559FA7F34A4215848F43288CDE875DCC92A06222E9BE0592B211FF74ADBB5D21A7AAB5522B540B1735F2F03279221056FEDBE7E534073DABEE9DB48F8ECEBCF1DC98A95576E45CBEFF5FE7C4842859451AB2DAE7A8370F1B2F7A529D2CA210E3E844D973523D73D193DF6C17F1314A6")] -------------------------------------------------------------------------------- /src/NLog.Extensions.Hosting/README.md: -------------------------------------------------------------------------------- 1 | # NLog.Extensions.Hosting 2 | 3 | [![](https://sonarcloud.io/api/project_badges/measure?project=nlog.extensions.logging&branch=master&metric=reliability_rating)](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master) 4 | [![](https://sonarcloud.io/api/project_badges/measure?project=nlog.extensions.logging&branch=master&metric=sqale_rating)](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master) 5 | [![](https://sonarcloud.io/api/project_badges/measure?project=nlog.extensions.logging&branch=master&metric=vulnerabilities)](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master) 6 | 7 | Integrates NLog as Logging provider for Microsoft.Extensions.Logging, by just calling `UseNLog()` on the application-IHostBuilder. 8 | 9 | Providing features like: 10 | 11 | - Capture [structured message properties](https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-properties-with-Microsoft-Extension-Logging) from the [Microsoft ILogger](https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-GetCurrentClassLogger-and-Microsoft-ILogger) 12 | - Capture scope context properties from the Microsoft ILogger `BeginScope` 13 | - Load NLog configuration from [appsettings.json](https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-configuration-with-appsettings.json) 14 | - Routing logging output to multiple destinations via the available [NLog Targets](https://nlog-project.org/config/?tab=targets) 15 | - Enrich logging output with additional context details via the available [NLog LayoutRenderers](https://nlog-project.org/config/?tab=layout-renderers) 16 | - Rendering logging output into standard formats like JSON, CVS, W3C ELF and XML using [NLog Layouts](https://nlog-project.org/config/?tab=layouts). 17 | 18 | If using ASP.NET Core then check [NLog.Web.AspNetCore](https://www.nuget.org/packages/NLog.Web.AspNetCore). 19 | 20 | Supported platforms: 21 | 22 | - .NET 5, 6, 7, 8 and 9 23 | - .NET Core 2 and 3 24 | - .NET Standard 2.0 and 2.1 25 | 26 | Registration of NLog as logging provider: 27 | 28 | ```csharp 29 | var hostBuilder = new HostBuilder().UseNLog(); 30 | ``` 31 | 32 | Useful Links 33 | 34 | - [Home Page](https://nlog-project.org/) 35 | - [Change Log](https://github.com/NLog/NLog.Extensions.Logging/releases) 36 | - [Tutorial](https://github.com/NLog/NLog/wiki/Tutorial) 37 | - [Logging Troubleshooting](https://github.com/NLog/NLog/wiki/Logging-troubleshooting) 38 | - [Have a question?](https://stackoverflow.com/questions/tagged/nlog) 39 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/Config/SetupBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using NLog.Config; 3 | 4 | namespace NLog.Extensions.Logging 5 | { 6 | /// 7 | /// Extension methods to setup LogFactory options 8 | /// 9 | public static class SetupBuilderExtensions 10 | { 11 | /// 12 | /// Loads NLog LoggingConfiguration from appsettings.json from NLog-section 13 | /// 14 | public static ISetupBuilder LoadConfigurationFromSection(this ISetupBuilder setupBuilder, Microsoft.Extensions.Configuration.IConfiguration configuration, string configSection = "NLog") 15 | { 16 | setupBuilder.SetupExtensions(ext => ext.RegisterConfigSettings(ConfigSettingLayoutRenderer.DefaultConfiguration ?? configuration)); 17 | if (!string.IsNullOrEmpty(configSection)) 18 | { 19 | var nlogConfig = configuration.GetSection(configSection); 20 | if (nlogConfig?.GetChildren()?.Any() == true) 21 | { 22 | setupBuilder.LogFactory.Configuration = new NLogLoggingConfiguration(nlogConfig, setupBuilder.LogFactory); 23 | } 24 | else 25 | { 26 | Common.InternalLogger.Debug("Skip loading NLogLoggingConfiguration from empty config section: {0}", configSection); 27 | } 28 | } 29 | return setupBuilder; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/Config/SetupExtensionsBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Microsoft.Extensions.Configuration; 4 | using NLog.Config; 5 | 6 | namespace NLog.Extensions.Logging 7 | { 8 | /// 9 | /// Extension methods to setup NLog extensions, so they are known when loading NLog LoggingConfiguration 10 | /// 11 | public static class SetupExtensionsBuilderExtensions 12 | { 13 | /// 14 | /// Setup the MEL-configuration for the ${configsetting} layoutrenderer 15 | /// 16 | public static ISetupExtensionsBuilder RegisterConfigSettings(this ISetupExtensionsBuilder setupBuilder, IConfiguration? configuration) 17 | { 18 | ConfigSettingLayoutRenderer.DefaultConfiguration = configuration ?? ConfigSettingLayoutRenderer.DefaultConfiguration; 19 | setupBuilder.LogFactory.Setup().SetupLogFactory(ext => 20 | { 21 | ext.AddCallSiteHiddenAssembly(typeof(NLogLoggerProvider).GetTypeInfo().Assembly); 22 | ext.AddCallSiteHiddenAssembly(typeof(Microsoft.Extensions.Logging.ILogger).GetTypeInfo().Assembly); 23 | ext.AddCallSiteHiddenAssembly(typeof(Microsoft.Extensions.Logging.LoggerFactory).GetTypeInfo().Assembly); 24 | }); 25 | return setupBuilder.RegisterLayoutRenderer("configsetting") 26 | .RegisterLayoutRenderer("MicrosoftConsoleLayout") 27 | .RegisterLayout("MicrosoftConsoleJsonLayout") 28 | .RegisterTarget("MicrosoftILogger"); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/Internal/Guard.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2004-2021 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen 3 | // 4 | // All rights reserved. 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions 8 | // are met: 9 | // 10 | // * Redistributions of source code must retain the above copyright notice, 11 | // this list of conditions and the following disclaimer. 12 | // 13 | // * Redistributions in binary form must reproduce the above copyright notice, 14 | // this list of conditions and the following disclaimer in the documentation 15 | // and/or other materials provided with the distribution. 16 | // 17 | // * Neither the name of Jaroslaw Kowalski nor the names of its 18 | // contributors may be used to endorse or promote products derived from this 19 | // software without specific prior written permission. 20 | // 21 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 | // THE POSSIBILITY OF SUCH DAMAGE. 32 | // 33 | 34 | #if !NETCOREAPP3_1_OR_GREATER 35 | namespace System.Runtime.CompilerServices 36 | { 37 | [AttributeUsage(AttributeTargets.Parameter)] 38 | sealed class CallerArgumentExpressionAttribute : Attribute 39 | { 40 | public CallerArgumentExpressionAttribute(string param) 41 | { 42 | Param = param; 43 | } 44 | 45 | public string Param { get; } 46 | } 47 | } 48 | #endif 49 | 50 | namespace NLog.Extensions.Logging 51 | { 52 | using System; 53 | using System.Runtime.CompilerServices; 54 | internal static class Guard 55 | { 56 | internal static T ThrowIfNull( 57 | T arg, 58 | [CallerArgumentExpression("arg")] string param = "") 59 | where T : class 60 | { 61 | if (arg is null) 62 | { 63 | throw new ArgumentNullException(string.IsNullOrEmpty(param) ? typeof(T).Name : param); 64 | } 65 | 66 | return arg; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/Internal/RegisterNLogLoggingProvider.cs: -------------------------------------------------------------------------------- 1 | namespace NLog.Extensions.Logging 2 | { 3 | using System; 4 | using System.Linq; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.DependencyInjection.Extensions; 8 | using Microsoft.Extensions.Logging; 9 | 10 | internal static class RegisterNLogLoggingProvider 11 | { 12 | internal static void TryAddNLogLoggingProvider(this IServiceCollection services, Action> addLogging, IConfiguration? hostConfiguration, NLogProviderOptions? options, Func factory) 13 | { 14 | var sharedFactory = factory; 15 | 16 | options = options.Configure(hostConfiguration?.GetSection("Logging:NLog")); 17 | 18 | if (options.ReplaceLoggerFactory) 19 | { 20 | NLogLoggerProvider? singleInstance = null; // Ensure that registration of ILoggerFactory and ILoggerProvider shares the same single instance 21 | sharedFactory = (provider, cfg, opt) => singleInstance ?? (singleInstance = factory(provider, cfg, opt)); 22 | 23 | addLogging?.Invoke(services, (builder) => builder?.ClearProviders()); // Cleanup the existing LoggerFactory, before replacing it with NLogLoggerFactory 24 | services.Replace(ServiceDescriptor.Singleton(serviceProvider => new NLogLoggerFactory(sharedFactory(serviceProvider, hostConfiguration, options)))); 25 | } 26 | 27 | services.TryAddEnumerable(ServiceDescriptor.Singleton(serviceProvider => sharedFactory(serviceProvider, hostConfiguration, options))); 28 | 29 | if (options.RemoveLoggerFactoryFilter) 30 | { 31 | // Will forward all messages to NLog if not specifically overridden by user 32 | addLogging?.Invoke(services, (builder) => builder?.AddFilter(null, Microsoft.Extensions.Logging.LogLevel.Trace)); 33 | } 34 | } 35 | 36 | internal static void TryLoadConfigurationFromSection(this NLogLoggerProvider loggerProvider, IConfiguration configuration) 37 | { 38 | if (string.IsNullOrEmpty(loggerProvider.Options.LoggingConfigurationSectionName)) 39 | return; 40 | 41 | var nlogConfig = configuration.GetSection(loggerProvider.Options.LoggingConfigurationSectionName); 42 | if (nlogConfig?.GetChildren()?.Any() == true) 43 | { 44 | loggerProvider.LogFactory.Setup().LoadConfiguration(configBuilder => 45 | { 46 | if (configBuilder.Configuration.LoggingRules.Count == 0 && configBuilder.Configuration.AllTargets.Count == 0) 47 | { 48 | configBuilder.Configuration = new NLogLoggingConfiguration(nlogConfig, loggerProvider.LogFactory); 49 | } 50 | }); 51 | } 52 | else 53 | { 54 | Common.InternalLogger.Debug("Skip loading NLogLoggingConfiguration from empty config section: {0}", loggerProvider.Options.LoggingConfigurationSectionName); 55 | } 56 | } 57 | 58 | internal static NLogLoggerProvider CreateNLogLoggerProvider(this IServiceProvider serviceProvider, IConfiguration? hostConfiguration, NLogProviderOptions options, LogFactory logFactory) 59 | { 60 | var provider = new NLogLoggerProvider(options, logFactory); 61 | 62 | var configuration = serviceProvider.SetupNLogConfigSettings(hostConfiguration, provider.LogFactory); 63 | 64 | if (configuration != null && (!ReferenceEquals(configuration, hostConfiguration) || options is null)) 65 | { 66 | provider.Configure(configuration.GetSection("Logging:NLog")); 67 | } 68 | 69 | if (serviceProvider != null && provider.Options.RegisterServiceProvider) 70 | { 71 | provider.LogFactory.ServiceRepository.RegisterService(typeof(IServiceProvider), serviceProvider); 72 | } 73 | 74 | if (configuration != null) 75 | { 76 | provider.TryLoadConfigurationFromSection(configuration); 77 | } 78 | 79 | if (provider.Options.ShutdownOnDispose || !provider.Options.AutoShutdown) 80 | { 81 | provider.LogFactory.AutoShutdown = false; 82 | } 83 | 84 | return provider; 85 | } 86 | 87 | internal static IConfiguration? SetupNLogConfigSettings(this IServiceProvider? serviceProvider, IConfiguration? configuration, LogFactory logFactory) 88 | { 89 | configuration = configuration ?? (serviceProvider?.GetService(typeof(IConfiguration)) as IConfiguration); 90 | logFactory.Setup().SetupExtensions(ext => ext.RegisterConfigSettings(configuration)); 91 | return configuration; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/Internal/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NLog.Extensions.Logging 4 | { 5 | internal static class StringExtensions 6 | { 7 | internal static bool EqualsOrdinalIgnoreCase(this string text, string text2) 8 | { 9 | return string.Equals(text, text2, StringComparison.OrdinalIgnoreCase); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/LayoutRenderers/ConfigSettingLayoutRenderer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using Microsoft.Extensions.Configuration; 4 | using NLog.Common; 5 | using NLog.Config; 6 | using NLog.LayoutRenderers; 7 | 8 | namespace NLog.Extensions.Logging 9 | { 10 | /// 11 | /// Layout renderer that can lookup values from Microsoft Extension Configuration Container (json, xml, ini) 12 | /// 13 | /// Not to be confused with NLog.AppConfig that includes ${appsetting} 14 | /// 15 | /// Example: appsettings.json 16 | /// { 17 | /// "Mode":"Prod", 18 | /// "Options":{ 19 | /// "StorageConnectionString":"UseDevelopmentStorage=true", 20 | /// } 21 | /// } 22 | /// 23 | /// Config Setting Lookup: 24 | /// ${configsetting:name=Mode} = "Prod" 25 | /// ${configsetting:name=Options.StorageConnectionString} = "UseDevelopmentStorage=true" 26 | /// ${configsetting:name=Options.TableName:default=MyTable} = "MyTable" 27 | /// 28 | /// Config Setting Lookup Cached: 29 | /// ${configsetting:cached=True:name=Mode} 30 | /// 31 | /// Documentation on NLog Wiki 32 | [LayoutRenderer("configsetting")] 33 | [ThreadAgnostic] 34 | public class ConfigSettingLayoutRenderer : LayoutRenderer 35 | { 36 | private IConfiguration? _serviceConfiguration; 37 | 38 | /// 39 | /// Global Configuration Container 40 | /// 41 | public static IConfiguration? DefaultConfiguration { get; set; } 42 | 43 | /// 44 | /// Item in the setting container 45 | /// 46 | [DefaultParameter] 47 | public string Item 48 | { 49 | get => _item; 50 | set 51 | { 52 | _item = value; 53 | _itemLookup = value?.Replace("\\.", "::").Replace(".", ":").Replace("::", ".") ?? string.Empty; 54 | } 55 | } 56 | private string _item = string.Empty; 57 | private string _itemLookup = string.Empty; 58 | 59 | /// 60 | /// Name of the Item 61 | /// 62 | [Obsolete("Replaced by Item-property")] 63 | public string Name { get => Item; set => Item = value; } 64 | 65 | /// 66 | /// The default value to render if the setting value is null. 67 | /// 68 | public string Default { get; set; } = string.Empty; 69 | 70 | /// 71 | protected override void InitializeLayoutRenderer() 72 | { 73 | try 74 | { 75 | // Avoid NLogDependencyResolveException when possible 76 | if (!ReferenceEquals(ResolveService(), LoggingConfiguration?.LogFactory?.ServiceRepository ?? NLog.LogManager.LogFactory.ServiceRepository)) 77 | { 78 | _serviceConfiguration = ResolveService(); 79 | } 80 | } 81 | catch (NLogDependencyResolveException ex) 82 | { 83 | _serviceConfiguration = null; 84 | InternalLogger.Debug("ConfigSetting - Fallback to DefaultConfiguration: {0}", ex.Message); 85 | } 86 | 87 | base.InitializeLayoutRenderer(); 88 | } 89 | 90 | /// 91 | protected override void Append(StringBuilder builder, LogEventInfo logEvent) 92 | { 93 | if (string.IsNullOrEmpty(_itemLookup)) 94 | return; 95 | 96 | string? value = null; 97 | var configurationRoot = _serviceConfiguration ?? DefaultConfiguration; 98 | if (configurationRoot != null) 99 | { 100 | value = configurationRoot[_itemLookup]; 101 | } 102 | else 103 | { 104 | InternalLogger.Debug("Missing DefaultConfiguration. Remember to register IConfiguration by calling UseNLog"); 105 | } 106 | 107 | builder.Append(value ?? Default); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/LayoutRenderers/MicrosoftConsoleLayoutRenderer.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text; 3 | using NLog.Config; 4 | using NLog.LayoutRenderers; 5 | 6 | namespace NLog.Extensions.Logging 7 | { 8 | /// 9 | /// Renders output that simulates simple Microsoft Console Logger. Useful for Hosting Lifetime Startup Messages. 10 | /// 11 | /// Documentation on NLog Wiki 12 | [LayoutRenderer("MicrosoftConsoleLayout")] 13 | [ThreadAgnostic] 14 | class MicrosoftConsoleLayoutRenderer : LayoutRenderer 15 | { 16 | private static readonly string[] EventIdMapper = Enumerable.Range(0, 50).Select(id => id.ToString(System.Globalization.CultureInfo.InvariantCulture)).ToArray(); 17 | 18 | /// 19 | /// Gets or sets format string used to format timestamp in logging messages. Defaults to null. 20 | /// 21 | public string TimestampFormat 22 | { 23 | get => _timestampFormat; 24 | set 25 | { 26 | _timestampFormat = value; 27 | _timestampFormatString = string.IsNullOrEmpty(value) ? string.Empty : $"{{0:{value}}}"; 28 | } 29 | } 30 | private string _timestampFormat = string.Empty; 31 | private string _timestampFormatString = string.Empty; 32 | 33 | /// 34 | /// Gets or sets indication whether or not UTC timezone should be used to format timestamps in logging messages. Defaults to false. 35 | /// 36 | public bool UseUtcTimestamp { get; set; } 37 | 38 | /// 39 | protected override void Append(StringBuilder builder, LogEventInfo logEvent) 40 | { 41 | string timestampFormatString = _timestampFormatString; 42 | if (!string.IsNullOrEmpty(timestampFormatString)) 43 | { 44 | var timestamp = UseUtcTimestamp ? logEvent.TimeStamp.ToUniversalTime() : logEvent.TimeStamp; 45 | builder.AppendFormat(UseUtcTimestamp ? System.Globalization.CultureInfo.InvariantCulture : System.Globalization.CultureInfo.CurrentCulture, timestampFormatString, timestamp); 46 | builder.Append(' '); 47 | } 48 | 49 | var microsoftLogLevel = ConvertLogLevel(logEvent.Level); 50 | builder.Append(microsoftLogLevel); 51 | builder.Append(": "); 52 | builder.Append(logEvent.LoggerName); 53 | builder.Append('['); 54 | AppendEventId(LookupEventId(logEvent), builder); 55 | builder.Append(']'); 56 | builder.Append(System.Environment.NewLine); 57 | builder.Append(" "); 58 | builder.Append(logEvent.FormattedMessage); 59 | if (logEvent.Exception != null) 60 | { 61 | builder.Append(System.Environment.NewLine); 62 | builder.Append(logEvent.Exception.ToString()); 63 | } 64 | } 65 | 66 | private static void AppendEventId(int eventId, StringBuilder builder) 67 | { 68 | if (eventId == 0) 69 | builder.Append('0'); 70 | else if (eventId > 0 && eventId < EventIdMapper.Length) 71 | builder.Append(EventIdMapper[eventId]); 72 | else 73 | builder.Append(eventId); // .NET5 (and newer) can append integer without string-allocation (using span) 74 | } 75 | 76 | private static int LookupEventId(LogEventInfo logEvent) 77 | { 78 | if (logEvent.HasProperties) 79 | { 80 | if (logEvent.Properties.TryGetValue(nameof(EventIdCaptureType.EventId), out var eventObject)) 81 | { 82 | if (eventObject is int eventId) 83 | return eventId; 84 | else if (eventObject is Microsoft.Extensions.Logging.EventId eventIdStruct) 85 | return eventIdStruct.Id; 86 | } 87 | 88 | if (logEvent.Properties.TryGetValue(nameof(EventIdCaptureType.EventId_Id), out var eventid) && eventid is int) 89 | { 90 | return (int)eventid; 91 | } 92 | } 93 | 94 | return 0; 95 | } 96 | 97 | private static string ConvertLogLevel(LogLevel logLevel) 98 | { 99 | if (logLevel == LogLevel.Trace) 100 | return "trce"; 101 | else if (logLevel == LogLevel.Debug) 102 | return "dbug"; 103 | else if (logLevel == LogLevel.Info) 104 | return "info"; 105 | else if (logLevel == LogLevel.Warn) 106 | return "warn"; 107 | else if (logLevel == LogLevel.Error) 108 | return "fail"; 109 | else 110 | return "crit"; // Fatal 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/Layouts/MicrosoftConsoleJsonLayout.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using NLog.Config; 5 | using NLog.LayoutRenderers; 6 | using NLog.Layouts; 7 | 8 | namespace NLog.Extensions.Logging 9 | { 10 | /// 11 | /// Renders output that simulates Microsoft Json Console Formatter from AddJsonConsole 12 | /// 13 | /// Documentation on NLog Wiki 14 | [Layout("MicrosoftConsoleJsonLayout")] 15 | [ThreadAgnostic] 16 | public class MicrosoftConsoleJsonLayout : JsonLayout 17 | { 18 | private static readonly string[] EventIdMapper = Enumerable.Range(0, 50).Select(id => id.ToString(System.Globalization.CultureInfo.InvariantCulture)).ToArray(); 19 | 20 | private readonly SimpleLayout _timestampLayout = new SimpleLayout("\"${date:format=o:universalTime=true}\""); 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | public MicrosoftConsoleJsonLayout() 26 | { 27 | SuppressSpaces = false; 28 | Attributes.Add(new JsonAttribute("Timestamp", _timestampLayout) { Encode = false }); 29 | Attributes.Add(new JsonAttribute("EventId", Layout.FromMethod(evt => LookupEventId(evt), LayoutRenderOptions.ThreadAgnostic)) { Encode = false }); 30 | Attributes.Add(new JsonAttribute("LogLevel", Layout.FromMethod(evt => ConvertLogLevel(evt.Level), LayoutRenderOptions.ThreadAgnostic)) { Encode = false }); 31 | Attributes.Add(new JsonAttribute("Category", "${logger}")); 32 | Attributes.Add(new JsonAttribute("Message", "${message}")); 33 | Attributes.Add(new JsonAttribute("Exception", "${replace-newlines:${exception:format=tostring,data}}")); 34 | var stateJsonLayout = new JsonLayout() { IncludeEventProperties = true }; 35 | stateJsonLayout.ExcludeProperties.Add(nameof(EventIdCaptureType.EventId)); 36 | stateJsonLayout.ExcludeProperties.Add(nameof(EventIdCaptureType.EventId_Id)); 37 | stateJsonLayout.Attributes.Add(new JsonAttribute("{OriginalFormat}", "${message:raw=true}")); 38 | Attributes.Add(new JsonAttribute("State", stateJsonLayout) { Encode = false }); 39 | } 40 | 41 | /// 42 | /// Gets the array of attributes for the "state"-section 43 | /// 44 | [ArrayParameter(typeof(JsonAttribute), "state")] 45 | public IList? StateAttributes 46 | { 47 | get 48 | { 49 | var index = LookupNamedAttributeIndex("State"); 50 | return index >= 0 ? (Attributes[index]?.Layout as JsonLayout)?.Attributes : null; 51 | } 52 | } 53 | 54 | /// 55 | /// Gets or sets whether to include "scopes"-section 56 | /// 57 | public bool IncludeScopes 58 | { 59 | get => LookupNamedAttributeIndex("Scopes") >= 0; 60 | set 61 | { 62 | var index = LookupNamedAttributeIndex("Scopes"); 63 | if (index >= 0) 64 | { 65 | if (!value) 66 | Attributes.RemoveAt(index); 67 | } 68 | else if (value) 69 | { 70 | Attributes.Add(new JsonAttribute("Scopes", "${scopenested:format=@}") { Encode = false }); 71 | } 72 | } 73 | } 74 | 75 | /// 76 | /// Gets or sets whether to include "Timestamp"-section 77 | /// 78 | public string? TimestampFormat 79 | { 80 | get 81 | { 82 | var index = LookupNamedAttributeIndex("Timestamp"); 83 | return index >= 0 ? ((Attributes[index].Layout as SimpleLayout)?.LayoutRenderers?.OfType().FirstOrDefault())?.Format : null; 84 | } 85 | set 86 | { 87 | var index = LookupNamedAttributeIndex("Timestamp"); 88 | if (index >= 0) 89 | { 90 | Attributes.RemoveAt(index); 91 | } 92 | 93 | if (value != null && !string.IsNullOrEmpty(value)) 94 | { 95 | var dateLayoutRenderer = _timestampLayout.LayoutRenderers.OfType().First(); 96 | dateLayoutRenderer.Format = value; 97 | Attributes.Insert(0, new JsonAttribute("Timestamp", _timestampLayout) { Encode = false }); 98 | } 99 | } 100 | } 101 | 102 | private int LookupNamedAttributeIndex(string attributeName) 103 | { 104 | for (int i = 0; i < Attributes.Count; ++i) 105 | { 106 | if (attributeName.Equals(Attributes[i].Name, StringComparison.OrdinalIgnoreCase)) 107 | { 108 | return i; 109 | } 110 | } 111 | return -1; 112 | } 113 | 114 | private static string LookupEventId(LogEventInfo logEvent) 115 | { 116 | if (logEvent.HasProperties) 117 | { 118 | if (logEvent.Properties.TryGetValue(nameof(EventIdCaptureType.EventId), out var eventObject)) 119 | { 120 | if (eventObject is int eventId) 121 | return ConvertEventId(eventId); 122 | else if (eventObject is Microsoft.Extensions.Logging.EventId eventIdStruct) 123 | return ConvertEventId(eventIdStruct.Id); 124 | } 125 | 126 | if (logEvent.Properties.TryGetValue(nameof(EventIdCaptureType.EventId_Id), out var eventid) && eventid is int) 127 | { 128 | return ConvertEventId((int)eventid); 129 | } 130 | } 131 | 132 | return "0"; 133 | } 134 | 135 | private static string ConvertEventId(int eventId) 136 | { 137 | if (eventId == 0) 138 | return "0"; 139 | else if (eventId > 0 && eventId < EventIdMapper.Length) 140 | return EventIdMapper[eventId]; 141 | else 142 | return eventId.ToString(System.Globalization.CultureInfo.InvariantCulture); 143 | } 144 | 145 | private static string ConvertLogLevel(LogLevel logLevel) 146 | { 147 | if (logLevel == LogLevel.Trace) 148 | return "\"" + nameof(Microsoft.Extensions.Logging.LogLevel.Trace) + "\""; 149 | else if (logLevel == LogLevel.Debug) 150 | return "\"" + nameof(Microsoft.Extensions.Logging.LogLevel.Debug) + "\""; 151 | else if (logLevel == LogLevel.Info) 152 | return "\"" + nameof(Microsoft.Extensions.Logging.LogLevel.Information) + "\""; 153 | else if (logLevel == LogLevel.Warn) 154 | return "\"" + nameof(Microsoft.Extensions.Logging.LogLevel.Warning) + "\""; 155 | else if (logLevel == LogLevel.Error) 156 | return "\"" + nameof(Microsoft.Extensions.Logging.LogLevel.Error) + "\""; 157 | else 158 | return "\"" + nameof(Microsoft.Extensions.Logging.LogLevel.Critical) + "\""; 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/Logging/ActivityExtensions.cs: -------------------------------------------------------------------------------- 1 | #if NET5_0_OR_GREATER 2 | 3 | using System.Diagnostics; 4 | 5 | namespace NLog.Extensions.Logging 6 | { 7 | /// 8 | /// Helpers for getting the right values from Activity no matter the format (w3c or hierarchical) 9 | /// 10 | internal static class ActivityExtensions 11 | { 12 | public static string GetSpanId(this Activity activity) 13 | { 14 | return activity.IdFormat switch 15 | { 16 | ActivityIdFormat.Hierarchical => activity.Id, 17 | ActivityIdFormat.W3C => activity.SpanId.ToHexString(), 18 | _ => null, 19 | } ?? string.Empty; 20 | } 21 | 22 | public static string GetTraceId(this Activity activity) 23 | { 24 | return activity.IdFormat switch 25 | { 26 | ActivityIdFormat.Hierarchical => activity.RootId, 27 | ActivityIdFormat.W3C => activity.TraceId.ToHexString(), 28 | _ => null, 29 | } ?? string.Empty; 30 | } 31 | 32 | public static string GetParentId(this Activity activity) 33 | { 34 | return activity.IdFormat switch 35 | { 36 | ActivityIdFormat.Hierarchical => activity.ParentId, 37 | ActivityIdFormat.W3C => activity.ParentSpanId.ToHexString(), 38 | _ => null, 39 | } ?? string.Empty; 40 | } 41 | } 42 | } 43 | 44 | #endif -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/Logging/EventIdCaptureType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NLog.Extensions.Logging 4 | { 5 | /// 6 | /// Defines EventId capture options 7 | /// 8 | [Flags] 9 | public enum EventIdCaptureType 10 | { 11 | /// 12 | /// Skip capture 13 | /// 14 | None = 0, 15 | /// 16 | /// Capture integer as "EventId"-property 17 | /// 18 | EventId = 1, 19 | /// 20 | /// Capture string as "EventName"-property 21 | /// 22 | EventName = 2, 23 | /// 24 | /// Capture struct as "EventId"-property (with boxing) 25 | /// 26 | EventIdStruct = 4, 27 | /// 28 | /// Capture integer as "EventId_Id"-property (Legacy) 29 | /// 30 | EventId_Id = 8, 31 | /// 32 | /// Capture string as "EventId_Name"-property (Legacy) 33 | /// 34 | EventId_Name = 16, 35 | /// 36 | /// Captures legacy properties (EventId-struct + EventId_Id + EventId_Name) 37 | /// 38 | Legacy = EventIdStruct | EventId_Id | EventId_Name, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/Logging/NLogLoggerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using Microsoft.Extensions.Logging; 4 | using NLog.Common; 5 | 6 | namespace NLog.Extensions.Logging 7 | { 8 | /// 9 | /// Creating DI loggers for Microsoft.Extensions.Logging and NLog 10 | /// 11 | public class NLogLoggerFactory : ILoggerFactory 12 | { 13 | private readonly ConcurrentDictionary _loggers = new ConcurrentDictionary(StringComparer.Ordinal); 14 | 15 | private readonly NLogLoggerProvider _provider; 16 | 17 | /// 18 | /// New factory with default options 19 | /// 20 | public NLogLoggerFactory() 21 | :this(NLogProviderOptions.Default) 22 | { 23 | } 24 | 25 | /// 26 | /// New factory with options. 27 | /// 28 | /// 29 | public NLogLoggerFactory(NLogProviderOptions options) 30 | :this(options, LogManager.LogFactory) 31 | { 32 | } 33 | 34 | /// 35 | /// New factory with options and isolated LogFactory 36 | /// 37 | /// 38 | /// 39 | public NLogLoggerFactory(NLogProviderOptions options, LogFactory logFactory) 40 | : this(new NLogLoggerProvider(options, logFactory)) 41 | { 42 | RegisterNLogLoggingProvider.SetupNLogConfigSettings(null, null, _provider.LogFactory); 43 | } 44 | 45 | /// 46 | /// New factory with provider. 47 | /// 48 | /// 49 | public NLogLoggerFactory(NLogLoggerProvider provider) 50 | { 51 | _provider = provider; 52 | } 53 | 54 | /// 55 | /// Cleanup 56 | /// 57 | public void Dispose() 58 | { 59 | Dispose(true); 60 | GC.SuppressFinalize(this); 61 | } 62 | 63 | /// 64 | /// Cleanup 65 | /// 66 | protected virtual void Dispose(bool disposing) 67 | { 68 | if (disposing) 69 | { 70 | _provider.Dispose(); 71 | } 72 | } 73 | 74 | /// 75 | /// Creates a new instance. 76 | /// 77 | /// The logger name for messages produced by the logger. 78 | /// The . 79 | public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) 80 | { 81 | if (!_loggers.TryGetValue(categoryName, out var logger)) 82 | { 83 | lock (_loggers) 84 | { 85 | if (!_loggers.TryGetValue(categoryName, out logger)) 86 | { 87 | logger = _provider.CreateLogger(categoryName); 88 | _loggers[categoryName] = logger; 89 | } 90 | } 91 | } 92 | return logger; 93 | } 94 | 95 | /// 96 | /// Do nothing 97 | /// 98 | /// The . 99 | public void AddProvider(ILoggerProvider provider) 100 | { 101 | InternalLogger.Debug("NLogLoggerFactory: AddProvider has been ignored {0}", provider?.GetType()); 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/Logging/NLogLoggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace NLog.Extensions.Logging 5 | { 6 | /// 7 | /// Provider logger for NLog + Microsoft.Extensions.Logging 8 | /// 9 | [ProviderAlias("NLog")] 10 | public class NLogLoggerProvider : ILoggerProvider 11 | { 12 | private readonly NLogBeginScopeParser _beginScopeParser; 13 | 14 | /// 15 | /// NLog options 16 | /// 17 | public NLogProviderOptions Options { get; set; } 18 | 19 | /// 20 | /// NLog Factory 21 | /// 22 | public LogFactory LogFactory { get; } 23 | 24 | /// 25 | /// New provider with default options, see 26 | /// 27 | public NLogLoggerProvider() 28 | : this(NLogProviderOptions.Default) 29 | { 30 | } 31 | 32 | /// 33 | /// New provider with options 34 | /// 35 | /// 36 | public NLogLoggerProvider(NLogProviderOptions options) 37 | : this(options, LogManager.LogFactory) 38 | { 39 | } 40 | 41 | /// 42 | /// New provider with options 43 | /// 44 | /// 45 | /// Optional isolated NLog LogFactory 46 | public NLogLoggerProvider(NLogProviderOptions options, LogFactory logFactory) 47 | { 48 | LogFactory = logFactory ?? LogManager.LogFactory; 49 | Options = options ?? NLogProviderOptions.Default; 50 | _beginScopeParser = new NLogBeginScopeParser(Options); 51 | } 52 | 53 | /// 54 | /// Create a logger with the name . 55 | /// 56 | /// Name of the logger to be created. 57 | /// New Logger 58 | public Microsoft.Extensions.Logging.ILogger CreateLogger(string name) 59 | { 60 | return new NLogLogger(LogFactory.GetLogger(name), Options, _beginScopeParser); 61 | } 62 | 63 | /// 64 | /// Cleanup 65 | /// 66 | public void Dispose() 67 | { 68 | Dispose(true); 69 | GC.SuppressFinalize(this); 70 | } 71 | 72 | /// 73 | /// Cleanup 74 | /// 75 | /// 76 | protected virtual void Dispose(bool disposing) 77 | { 78 | if (disposing) 79 | { 80 | if (Options.ShutdownOnDispose) 81 | { 82 | LogFactory.Shutdown(); 83 | } 84 | else 85 | { 86 | LogFactory.Flush(); 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/Logging/NLogMessageParameterList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using NLog.MessageTemplates; 5 | 6 | namespace NLog.Extensions.Logging 7 | { 8 | /// 9 | /// Converts Microsoft Extension Logging ParameterList into NLog MessageTemplate ParameterList 10 | /// 11 | internal class NLogMessageParameterList : IList 12 | { 13 | private static readonly NLogMessageParameterList EmptyList = new NLogMessageParameterList(Array.Empty>(), default, default, default); 14 | private static readonly NLogMessageParameterList PositionalParameterList = new NLogMessageParameterList(Array.Empty>(), default, hasComplexParameters: false, isPositional: true); 15 | private static readonly NLogMessageParameterList PositionalComplexParameterList = new NLogMessageParameterList(Array.Empty>(), default, hasComplexParameters: true, isPositional: true); 16 | private static readonly NLogMessageParameterList OriginalMessageList = new NLogMessageParameterList(new[] { new KeyValuePair(NLogLogger.OriginalFormatPropertyName, string.Empty) }, 0, default, default); 17 | 18 | private readonly IReadOnlyList> _parameterList; 19 | private readonly int _originalMessageIndex; 20 | private readonly bool _hasComplexParameters; 21 | private readonly bool _isPositional; 22 | 23 | public bool HasComplexParameters => _hasComplexParameters; 24 | public bool IsPositional => _isPositional; 25 | public int Count => _originalMessageIndex != int.MaxValue ? _parameterList.Count - 1 : _parameterList.Count; 26 | public bool IsReadOnly => true; 27 | 28 | private NLogMessageParameterList(IReadOnlyList> parameterList, int? originalMessageIndex, bool hasComplexParameters, bool isPositional) 29 | { 30 | _parameterList = parameterList; 31 | _originalMessageIndex = originalMessageIndex ?? int.MaxValue; 32 | _hasComplexParameters = hasComplexParameters; 33 | _isPositional = isPositional; 34 | } 35 | 36 | /// 37 | /// Create a if has values, otherwise null 38 | /// 39 | /// 40 | /// The LogMessageParameterList-constructor initiates all the parsing/scanning 41 | /// 42 | public static NLogMessageParameterList TryParse(IReadOnlyList> parameterList) 43 | { 44 | var parameterCount = parameterList.Count; 45 | if (parameterCount > 1 || (parameterCount == 1 && !NLogLogger.OriginalFormatPropertyName.Equals(parameterList[0].Key))) 46 | { 47 | if (IsValidParameterList(parameterList, out var originalMessageIndex, out var hasComplexParameters, out var isPositional)) 48 | { 49 | if (isPositional) 50 | { 51 | return hasComplexParameters ? PositionalComplexParameterList : PositionalParameterList; // Skip allocation, not needed to create Parameters-array 52 | } 53 | else 54 | { 55 | return new NLogMessageParameterList(parameterList, originalMessageIndex, hasComplexParameters, isPositional); 56 | } 57 | } 58 | else 59 | { 60 | return new NLogMessageParameterList(CreateValidParameterList(parameterList), originalMessageIndex, hasComplexParameters, isPositional); 61 | } 62 | } 63 | else if (parameterCount == 1) 64 | { 65 | return OriginalMessageList; // Skip allocation 66 | } 67 | else 68 | { 69 | return EmptyList; // Skip allocation 70 | } 71 | } 72 | 73 | public bool HasMessageTemplateSyntax(bool parseMessageTemplates) 74 | { 75 | return _originalMessageIndex != int.MaxValue && (HasComplexParameters || (parseMessageTemplates && _parameterList.Count > 1)); 76 | } 77 | 78 | public string? GetOriginalMessage(IReadOnlyList> messageProperties) 79 | { 80 | if (_originalMessageIndex < messageProperties?.Count) 81 | { 82 | return messageProperties[_originalMessageIndex].Value as string; 83 | } 84 | return null; 85 | } 86 | 87 | /// 88 | /// Verify that the input parameterList contains non-empty key-values and the original-format-property at the end 89 | /// 90 | private static bool IsValidParameterList(IReadOnlyList> parameterList, out int? originalMessageIndex, out bool hasComplexParameters, out bool isPositional) 91 | { 92 | hasComplexParameters = false; 93 | originalMessageIndex = null; 94 | isPositional = false; 95 | bool isMixedPositional = false; 96 | string parameterName; 97 | 98 | var parameterCount = parameterList.Count; 99 | for (int i = 0; i < parameterCount; ++i) 100 | { 101 | if (!TryGetParameterName(parameterList, i, out parameterName)) 102 | { 103 | originalMessageIndex = null; 104 | return false; 105 | } 106 | 107 | char firstChar = parameterName[0]; 108 | if (firstChar >= '0' && firstChar <= '9') 109 | { 110 | if (!isPositional) 111 | isMixedPositional = i != 0; 112 | hasComplexParameters |= i != (firstChar - '0'); 113 | isPositional = true; 114 | } 115 | else if (NLogLogger.OriginalFormatPropertyName.Equals(parameterName)) 116 | { 117 | if (originalMessageIndex.HasValue) 118 | { 119 | originalMessageIndex = null; 120 | return false; 121 | } 122 | 123 | originalMessageIndex = i; 124 | } 125 | else 126 | { 127 | isMixedPositional = isPositional; 128 | hasComplexParameters |= GetCaptureType(firstChar) != CaptureType.Normal; 129 | } 130 | } 131 | 132 | isPositional = isPositional && !isMixedPositional; 133 | return true; 134 | } 135 | 136 | private static bool TryGetParameterName(IReadOnlyList> parameterList, int i, out string parameterKey) 137 | { 138 | try 139 | { 140 | parameterKey = parameterList[i].Key; 141 | } 142 | catch (IndexOutOfRangeException ex) 143 | { 144 | // Catch an issue in MEL 145 | throw new FormatException($"Invalid format string. Expected {parameterList.Count - 1} format parameters, but failed to lookup parameter index {i}", ex); 146 | } 147 | 148 | if (string.IsNullOrEmpty(parameterKey)) 149 | { 150 | return false; 151 | } 152 | 153 | return true; 154 | } 155 | 156 | /// 157 | /// Extract all valid properties from the input parameterList, and return them in a newly allocated list 158 | /// 159 | private static IReadOnlyList> CreateValidParameterList(IReadOnlyList> parameterList) 160 | { 161 | var parameterCount = parameterList.Count; 162 | var validParameterList = new List>(parameterCount); 163 | 164 | for (int i = 0; i < parameterCount; ++i) 165 | { 166 | if (!TryGetParameterName(parameterList, i, out var parameterName)) 167 | continue; 168 | 169 | if (NLogLogger.OriginalFormatPropertyName.Equals(parameterName)) 170 | continue; 171 | 172 | validParameterList.Add(parameterList[i]); 173 | } 174 | 175 | return validParameterList; 176 | } 177 | 178 | public MessageTemplateParameter this[int index] 179 | { 180 | get 181 | { 182 | var parameter = _parameterList[index < _originalMessageIndex ? index : index + 1]; 183 | return _hasComplexParameters ? 184 | GetMessageTemplateParameter(parameter.Key, parameter.Value) : 185 | new MessageTemplateParameter(parameter.Key, parameter.Value, null, CaptureType.Normal); 186 | } 187 | set => throw new NotSupportedException(); 188 | } 189 | 190 | internal static MessageTemplateParameter GetMessageTemplateParameter(in KeyValuePair propertyValue, int index) 191 | { 192 | var propertyName = propertyValue.Key; 193 | if (propertyName is null || propertyName.Length == 0) 194 | return default; 195 | var firstChar = propertyName[0]; 196 | if (char.IsDigit(firstChar) && (propertyName.Length != 1 || (firstChar - '0') != index)) 197 | return default; 198 | 199 | if (GetCaptureType(firstChar) != CaptureType.Normal) 200 | return default; 201 | 202 | return new MessageTemplateParameter(propertyName, propertyValue.Value, null, CaptureType.Normal); 203 | } 204 | 205 | private static MessageTemplateParameter GetMessageTemplateParameter(string parameterName, object? parameterValue) 206 | { 207 | var capture = GetCaptureType(parameterName[0]); 208 | if (capture != CaptureType.Normal) 209 | parameterName = parameterName.Substring(1); 210 | return new MessageTemplateParameter(parameterName, parameterValue, null, capture); 211 | } 212 | 213 | private static CaptureType GetCaptureType(char firstChar) 214 | { 215 | if (firstChar == '@') 216 | return CaptureType.Serialize; 217 | else if (firstChar == '$') 218 | return CaptureType.Stringify; 219 | else 220 | return CaptureType.Normal; 221 | } 222 | 223 | public void Add(MessageTemplateParameter item) 224 | { 225 | throw new NotSupportedException(); 226 | } 227 | 228 | public void Clear() 229 | { 230 | throw new NotSupportedException(); 231 | } 232 | 233 | public bool Contains(MessageTemplateParameter item) 234 | { 235 | throw new NotSupportedException(); 236 | } 237 | 238 | public void CopyTo(MessageTemplateParameter[] array, int arrayIndex) 239 | { 240 | for (int i = 0; i < Count; ++i) 241 | array[i + arrayIndex] = this[i]; 242 | } 243 | 244 | public IEnumerator GetEnumerator() 245 | { 246 | for (int i = 0; i < Count; ++i) 247 | yield return this[i]; 248 | } 249 | 250 | public int IndexOf(MessageTemplateParameter item) 251 | { 252 | throw new NotSupportedException(); 253 | } 254 | 255 | public void Insert(int index, MessageTemplateParameter item) 256 | { 257 | throw new NotSupportedException(); 258 | } 259 | 260 | public bool Remove(MessageTemplateParameter item) 261 | { 262 | throw new NotSupportedException(); 263 | } 264 | 265 | public void RemoveAt(int index) 266 | { 267 | throw new NotSupportedException(); 268 | } 269 | 270 | IEnumerator IEnumerable.GetEnumerator() 271 | { 272 | return GetEnumerator(); 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/Logging/NLogProviderOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NLog.Extensions.Logging 4 | { 5 | /// 6 | /// Configuration options for the NLog Logging Provider with Microsoft Extension Logging 7 | /// 8 | public class NLogProviderOptions 9 | { 10 | /// 11 | /// Control capture of as "EventId"-property. 12 | /// 13 | public EventIdCaptureType CaptureEventId { get; set; } = EventIdCaptureType.EventId | EventIdCaptureType.EventName; 14 | 15 | /// 16 | /// Skip capture of in when default(EventId) 17 | /// 18 | public bool IgnoreEmptyEventId { get; set; } = true; 19 | 20 | /// 21 | /// Separator between for EventId.Id and EventId.Name. Default to _ 22 | /// 23 | /// 24 | /// Only relevant for , or 25 | /// 26 | public string EventIdSeparator { get; set; } = "_"; 27 | 28 | /// 29 | /// Enable structured logging by capturing message template parameters with support for "@" and "$". Enables use of ${message:raw=true} 30 | /// 31 | public bool CaptureMessageTemplates { get; set; } = true; 32 | 33 | /// 34 | /// Enable capture of properties from the ILogger-State-object, both in and 35 | /// 36 | public bool CaptureMessageProperties { get; set; } = true; 37 | 38 | /// 39 | /// Enable capture of from the ILogger-State-object. Only relevant when = true 40 | /// 41 | public bool CaptureMessageParameters { get; set; } 42 | 43 | /// 44 | /// Use the NLog engine for parsing the message template (again) and format using the NLog formatter 45 | /// 46 | public bool ParseMessageTemplates { get; set; } 47 | 48 | /// 49 | /// Enable capture of scope information and inject into 50 | /// 51 | public bool IncludeScopes { get; set; } = true; 52 | 53 | /// 54 | /// Shutdown NLog on dispose of the 55 | /// 56 | public bool ShutdownOnDispose { get; set; } 57 | 58 | /// 59 | /// Automatically Shutdown NLog on AppDomain.Unload or AppDomain.ProcessExit 60 | /// 61 | public bool AutoShutdown { get; set; } 62 | 63 | #if NET5_0_OR_GREATER 64 | /// 65 | /// Automatically include , and 66 | /// 67 | /// 68 | /// Intended for Net5.0 where these properties are no longer included by default for performance reasons 69 | /// 70 | /// Consider using ${activity} as alternative 71 | /// 72 | #else 73 | /// 74 | /// Automatically include Activity.SpanId, Activity.TraceId and Activity.ParentId. 75 | /// 76 | /// 77 | /// Intended for Net5.0 where these properties are no longer included by default for performance reasons 78 | /// 79 | /// Consider using ${activity} as alternative 80 | /// 81 | [Obsolete("Only supported with NET6 (or newer)")] 82 | #endif 83 | public bool IncludeActivityIdsWithBeginScope { get; set; } 84 | 85 | /// 86 | /// See for documentation 87 | /// 88 | [Obsolete("Fixed spelling, so use IncludeActivityIdsWithBeginScope instead. Marked obsolete with NLog 5.0")] 89 | public bool IncludeActivtyIdsWithBeginScope { get => IncludeActivityIdsWithBeginScope; set => IncludeActivityIdsWithBeginScope = value; } 90 | 91 | /// 92 | /// Resets the default Microsoft LoggerFactory Filter for the , and instead only uses NLog LoggingRules. 93 | /// 94 | public bool RemoveLoggerFactoryFilter { get; set; } = true; 95 | 96 | /// 97 | /// Replace Microsoft LoggerFactory with a pure , and disables Microsoft Filter Logic and removes other LoggingProviders. 98 | /// 99 | public bool ReplaceLoggerFactory { get; set; } 100 | 101 | /// 102 | /// Checks the Host Configuration for the specified section-name, and tries to load NLog-LoggingConfiguration after creation of NLogLoggerProvider 103 | /// 104 | /// Will only attempt to load NLog-LoggingConfiguration if valid section-name, and NLog-LoggingConfiguration has not been loaded already. 105 | public string LoggingConfigurationSectionName { get; set; } = "NLog"; 106 | 107 | /// 108 | /// Enable NLog Targets and Layouts to perform dependency lookup using the Microsoft Dependency Injection IServiceProvider 109 | /// 110 | public bool RegisterServiceProvider { get; set; } = true; 111 | 112 | /// Initializes a new instance NLogProviderOptions with default values. 113 | public NLogProviderOptions() 114 | { 115 | } 116 | 117 | /// 118 | /// Default options 119 | /// 120 | internal static readonly NLogProviderOptions Default = new NLogProviderOptions(); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/NLog.Extensions.Logging.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net461;netstandard2.0;netstandard2.1;net6.0;net8.0 4 | Full 5 | 6 | NLog.Extensions.Logging v$(ProductVersion) 7 | $(ProductVersion) 8 | 9 | Julian Verdurmen 10 | NLog 11 | $([System.DateTime]::Now.ToString(yyyy)) 12 | Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ 13 | NLog LoggerProvider for Microsoft.Extensions.Logging for logging in .NET Standard libraries and .NET Core applications. 14 | 15 | For ASP.NET Core, check: https://www.nuget.org/packages/NLog.Web.AspNetCore 16 | 17 | NLog;Microsoft.Extensions.Logging;log;logging;logfiles;netcore 18 | 19 | ChangeLog: 20 | 21 | - Updated to NLog v6.0-RC3 22 | - Removed support for NetStandard 1.3 + 1.5 23 | - Enabled nullable references 24 | - Avoid boxing when extracting LogEvent properties from struct 25 | - Reduce allocation when creating LogEvent with properties by using ReadOnlySpan 26 | - Enabled <IsAotCompatible> 27 | - Added ${host-environment} for NLog.Extensions.Hosting 28 | - Added ${host-rootdir} for NLog.Extensions.Hosting 29 | - Added ${host-appname} for NLog.Extensions.Hosting 30 | - Added RegisterHostSettings for NLog.Extensions.Hosting 31 | - Updated NLog.Extensions.Hosting to support .NET Framework 4.6.2 using NET8-nuget-dependencies 32 | 33 | Full changelog: https://github.com/NLog/NLog.Extensions.Logging/blob/master/CHANGELOG.MD 34 | 35 | List of major changes in NLog 6.0: https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html 36 | 37 | https://github.com/NLog/NLog.Extensions.Logging 38 | https://github.com/NLog/NLog.Extensions.Logging.git 39 | git 40 | BSD-2-Clause 41 | N.png 42 | README.md 43 | 44 | 45 | {6A236D76-C9D9-4B1D-8DDE-F6978D110288} 46 | true 47 | 6.0.0.0 48 | ..\NLog.snk 49 | true 50 | 51 | true 52 | true 53 | true 54 | enable 55 | 9 56 | 12 57 | true 58 | true 59 | true 60 | 61 | 62 | 63 | NLog.Extensions.Logging for .NET Framework 4.6.1 64 | true 65 | 66 | 67 | NLog.Extensions.Logging for .NET Standard 2.0 68 | 69 | 70 | NLog.Extensions.Logging for .NET Standard 2.1 71 | 72 | 73 | NLog.Extensions.Logging for .NET 6 74 | 75 | 76 | NLog.Extensions.Logging for .NET 8 77 | 78 | 79 | $(Title) 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/NLog.Extensions.Logging.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True 5 | True 6 | True 7 | True -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: ComVisible(false)] 5 | 6 | [assembly: InternalsVisibleTo("NLog.Extensions.Logging.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100772391E63C104728ADCF18E2390474262559FA7F34A4215848F43288CDE875DCC92A06222E9BE0592B211FF74ADBB5D21A7AAB5522B540B1735F2F03279221056FEDBE7E534073DABEE9DB48F8ECEBCF1DC98A95576E45CBEFF5FE7C4842859451AB2DAE7A8370F1B2F7A529D2CA210E3E844D973523D73D193DF6C17F1314A6")] -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/README.md: -------------------------------------------------------------------------------- 1 | # NLog.Extensions.Logging 2 | 3 | [![](https://sonarcloud.io/api/project_badges/measure?project=nlog.extensions.logging&branch=master&metric=reliability_rating)](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master) 4 | [![](https://sonarcloud.io/api/project_badges/measure?project=nlog.extensions.logging&branch=master&metric=sqale_rating)](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master) 5 | [![](https://sonarcloud.io/api/project_badges/measure?project=nlog.extensions.logging&branch=master&metric=vulnerabilities)](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master) 6 | 7 | Integrates NLog as Logging provider for Microsoft.Extensions.Logging, by just calling `AddNLog()` on the Logging-builder. 8 | 9 | Providing features like: 10 | 11 | - Capture [structured message properties](https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-properties-with-Microsoft-Extension-Logging) from the [Microsoft ILogger](https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-GetCurrentClassLogger-and-Microsoft-ILogger) 12 | - Capture scope context properties from the Microsoft ILogger `BeginScope` 13 | - Load NLog configuration from [appsettings.json](https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-configuration-with-appsettings.json) 14 | - Routing logging output to multiple destinations via the available [NLog Targets](https://nlog-project.org/config/?tab=targets) 15 | - Enrich logging output with additional context details via the available [NLog LayoutRenderers](https://nlog-project.org/config/?tab=layout-renderers) 16 | - Rendering logging output into standard formats like JSON, CVS, W3C ELF and XML using [NLog Layouts](https://nlog-project.org/config/?tab=layouts). 17 | 18 | If using ASP.NET Core then consider using [NLog.Web.AspNetCore](https://www.nuget.org/packages/NLog.Web.AspNetCore). 19 | 20 | Supported platforms: 21 | 22 | - .NET 5, 6, 7, 8 and 9 23 | - .NET 2 and 3 24 | - .NET Standard 2.0 and 2.1 25 | - .NET 4.6.1 - 4.8 26 | 27 | Registration of NLog as logging provider: 28 | 29 | ```csharp 30 | builder.Logging.ClearProviders(); 31 | builder.Logging.AddNLog(); 32 | ``` 33 | 34 | Useful Links: 35 | 36 | - [Home Page](https://nlog-project.org/) 37 | - [Change Log](https://github.com/NLog/NLog.Extensions.Logging/releases) 38 | - [Tutorial](https://github.com/NLog/NLog/wiki/Tutorial) 39 | - [Logging Troubleshooting](https://github.com/NLog/NLog/wiki/Logging-troubleshooting) 40 | - [Have a question?](https://stackoverflow.com/questions/tagged/nlog) 41 | -------------------------------------------------------------------------------- /src/NLog.Extensions.Logging/Targets/MicrosoftILoggerTarget.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using NLog.Common; 6 | using NLog.Layouts; 7 | using NLog.Targets; 8 | using EventId = Microsoft.Extensions.Logging.EventId; 9 | 10 | namespace NLog.Extensions.Logging 11 | { 12 | /// 13 | /// Forwards NLog LogEvents to Microsoft ILogger-interface with support for NLog Layout-features 14 | /// 15 | /// Documentation on NLog Wiki 16 | [Target("MicrosoftILogger")] 17 | public class MicrosoftILoggerTarget : TargetWithContext 18 | { 19 | private readonly Microsoft.Extensions.Logging.ILoggerFactory? _loggerFactory; 20 | private readonly Microsoft.Extensions.Logging.ILogger? _logger; 21 | private readonly System.Collections.Concurrent.ConcurrentDictionary _loggers = new System.Collections.Concurrent.ConcurrentDictionary(); 22 | 23 | /// 24 | /// EventId forwarded to ILogger 25 | /// 26 | public Layout EventId { get; set; } = Layout.Empty; 27 | 28 | /// 29 | /// EventId-Name forwarded to ILogger 30 | /// 31 | public Layout EventName { get; set; } = Layout.Empty; 32 | 33 | /// 34 | /// Override name of ILogger, when target has been initialized with 35 | /// 36 | public Layout LoggerName { get; set; } = Layout.Empty; 37 | 38 | /// 39 | /// Initializes a new instance of the class. 40 | /// 41 | public MicrosoftILoggerTarget() 42 | { 43 | _logger = Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance; 44 | } 45 | 46 | /// 47 | /// Initializes a new instance of the class. 48 | /// 49 | /// Microsoft ILogger singleton instance 50 | public MicrosoftILoggerTarget(Microsoft.Extensions.Logging.ILogger logger) 51 | { 52 | _logger = logger; 53 | Layout = "${message}"; 54 | } 55 | 56 | /// 57 | /// Initializes a new instance of the class. 58 | /// 59 | /// Microsoft ILoggerFactory instance 60 | public MicrosoftILoggerTarget(Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) 61 | { 62 | _loggerFactory = loggerFactory; 63 | Layout = "${message}"; 64 | } 65 | 66 | /// 67 | protected override void CloseTarget() 68 | { 69 | base.CloseTarget(); 70 | _loggers.Clear(); 71 | } 72 | 73 | /// 74 | protected override void WriteAsyncThreadSafe(AsyncLogEventInfo logEvent) 75 | { 76 | var ilogger = _logger ?? CreateFromLoggerFactory(logEvent.LogEvent); // Create Logger without any protection from lock-object 77 | var logLevel = ConvertToLogLevel(logEvent.LogEvent.Level); 78 | if (ilogger.IsEnabled(logLevel)) 79 | { 80 | base.WriteAsyncThreadSafe(logEvent); 81 | } 82 | else 83 | { 84 | logEvent.Continuation(null); 85 | } 86 | } 87 | 88 | /// 89 | /// Converts NLog-LogEvent into Microsoft Extension Logging LogState 90 | /// 91 | protected override void Write(LogEventInfo logEvent) 92 | { 93 | var ilogger = _logger ?? CreateFromLoggerFactory(logEvent); 94 | var logLevel = ConvertToLogLevel(logEvent.Level); 95 | if (!ilogger.IsEnabled(logLevel)) 96 | return; 97 | 98 | var layoutMessage = RenderLogEvent(Layout, logEvent); 99 | 100 | IDictionary? contextProperties = null; 101 | if (ContextProperties.Count > 0 || IncludeScopeProperties || IncludeGdc) 102 | { 103 | contextProperties = GetContextProperties(logEvent); 104 | if (contextProperties?.Count == 0) 105 | contextProperties = null; 106 | } 107 | 108 | var eventId = RenderEventId(logEvent); 109 | ilogger.Log(logLevel, eventId, new LogState(logEvent, layoutMessage, contextProperties), logEvent.Exception, (s, ex) => LogStateFormatter(s)); 110 | } 111 | 112 | private EventId RenderEventId(LogEventInfo logEvent) 113 | { 114 | var eventId = default(EventId); 115 | 116 | if (EventId != null) 117 | { 118 | var eventIdValue = RenderLogEvent(EventId, logEvent); 119 | if (!string.IsNullOrEmpty(eventIdValue) && int.TryParse(eventIdValue, out int eventIdParsed) && eventIdParsed != 0) 120 | eventId = new EventId(eventIdParsed); 121 | } 122 | 123 | if (EventName != null) 124 | { 125 | var eventNameValue = RenderLogEvent(EventName, logEvent); 126 | if (!string.IsNullOrEmpty(eventNameValue)) 127 | eventId = new EventId(eventId.Id, eventNameValue); 128 | } 129 | 130 | return eventId; 131 | } 132 | 133 | private Microsoft.Extensions.Logging.ILogger CreateFromLoggerFactory(LogEventInfo logEvent) 134 | { 135 | var loggerName = logEvent.LoggerName; 136 | if (!ReferenceEquals(LoggerName, null) && !ReferenceEquals(LoggerName, Layout.Empty)) 137 | loggerName = RenderLogEvent(LoggerName, logEvent); 138 | if (string.IsNullOrEmpty(loggerName)) 139 | loggerName = "NLog"; 140 | 141 | if (!_loggers.TryGetValue(loggerName, out var logger)) 142 | { 143 | logger = _loggerFactory?.CreateLogger(loggerName); 144 | if (logger is null) 145 | throw new NLogRuntimeException($"Failed to create Microsoft Logger with category: {loggerName}"); 146 | 147 | _loggers.TryAdd(loggerName, logger); // Local caching of Loggers to reduce chance of congestions and deadlock 148 | } 149 | return logger; 150 | } 151 | 152 | private struct LogState : IReadOnlyList>, IEquatable 153 | { 154 | private readonly LogEventInfo _logEvent; 155 | public readonly string LayoutMessage; 156 | private readonly IDictionary? _contextProperties; 157 | 158 | public int Count => (_logEvent.HasProperties ? _logEvent.Properties.Count : 0) + (_contextProperties?.Count ?? 0) + 1; 159 | 160 | public KeyValuePair this[int index] 161 | { 162 | get 163 | { 164 | if (_logEvent.HasProperties && TryGetLogEventProperty(_logEvent.Properties, ref index, out var property)) 165 | { 166 | return property; 167 | } 168 | if (_contextProperties != null && TryGetContextProperty(_contextProperties, ref index, out var contextProperty)) 169 | { 170 | return contextProperty; 171 | } 172 | if (index != 0) 173 | throw new ArgumentOutOfRangeException(nameof(index)); 174 | return CreateOriginalFormatProperty(); 175 | } 176 | } 177 | 178 | public LogState(LogEventInfo logEvent, string layoutMessage, IDictionary? contextProperties) 179 | { 180 | _logEvent = logEvent; 181 | LayoutMessage = layoutMessage; 182 | _contextProperties = contextProperties; 183 | } 184 | 185 | public IEnumerator> GetEnumerator() 186 | { 187 | IList> originalMessage = new[] { CreateOriginalFormatProperty() }; 188 | IEnumerable> allProperties = _contextProperties?.Concat(originalMessage) ?? originalMessage; 189 | if (_logEvent.HasProperties) 190 | { 191 | allProperties = _logEvent.Properties.Select(p => CreateLogEventProperty(p)).Concat(allProperties); 192 | } 193 | return allProperties.GetEnumerator(); 194 | } 195 | 196 | IEnumerator IEnumerable.GetEnumerator() 197 | { 198 | return GetEnumerator(); 199 | } 200 | 201 | private KeyValuePair CreateOriginalFormatProperty() 202 | { 203 | return new KeyValuePair(NLogLogger.OriginalFormatPropertyName, _logEvent.Message); 204 | } 205 | 206 | public bool Equals(LogState other) 207 | { 208 | return ReferenceEquals(_logEvent, other._logEvent); 209 | } 210 | 211 | public override bool Equals(object? obj) 212 | { 213 | return obj is LogState other && Equals(other); 214 | } 215 | 216 | public override int GetHashCode() 217 | { 218 | return _logEvent.GetHashCode(); 219 | } 220 | } 221 | 222 | private static bool TryGetContextProperty(IDictionary contextProperties, ref int index, out KeyValuePair contextProperty) 223 | { 224 | return TryGetPropertyFromIndex(contextProperties, p => p, ref index, out contextProperty); 225 | } 226 | 227 | private static bool TryGetLogEventProperty(IDictionary logEventProperties, ref int index, out KeyValuePair logEventProperty) 228 | { 229 | return TryGetPropertyFromIndex(logEventProperties, p => CreateLogEventProperty(p), ref index, out logEventProperty); 230 | } 231 | 232 | private static bool TryGetPropertyFromIndex(ICollection> properties, Func, KeyValuePair> converter, ref int index, out KeyValuePair property) where TKey : class 233 | { 234 | if (index < properties.Count) 235 | { 236 | foreach (var prop in properties) 237 | { 238 | if (index-- == 0) 239 | { 240 | property = converter(prop); 241 | return true; 242 | } 243 | } 244 | } 245 | else 246 | { 247 | index -= properties.Count; 248 | } 249 | 250 | property = default; 251 | return false; 252 | } 253 | 254 | private static KeyValuePair CreateLogEventProperty(KeyValuePair prop) 255 | { 256 | return new KeyValuePair(prop.Key?.ToString() ?? string.Empty, prop.Value); 257 | } 258 | 259 | private static string LogStateFormatter(LogState logState) 260 | { 261 | return logState.LayoutMessage; 262 | } 263 | 264 | private static Microsoft.Extensions.Logging.LogLevel ConvertToLogLevel(LogLevel logLevel) 265 | { 266 | if (logLevel == LogLevel.Trace) 267 | return Microsoft.Extensions.Logging.LogLevel.Trace; 268 | if (logLevel == LogLevel.Debug) 269 | return Microsoft.Extensions.Logging.LogLevel.Debug; 270 | if (logLevel == LogLevel.Info) 271 | return Microsoft.Extensions.Logging.LogLevel.Information; 272 | if (logLevel == LogLevel.Warn) 273 | return Microsoft.Extensions.Logging.LogLevel.Warning; 274 | if (logLevel == LogLevel.Error) 275 | return Microsoft.Extensions.Logging.LogLevel.Error; 276 | return Microsoft.Extensions.Logging.LogLevel.Critical; 277 | } 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /src/NLog.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLog/NLog.Extensions.Logging/86f79be621c3e8938f7171eb508abf41ba1f179a/src/NLog.snk -------------------------------------------------------------------------------- /test/NLog.Extensions.Hosting.Tests/NLog.Extensions.Hosting.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net462;net8.0 4 | Library 5 | false 6 | full 7 | true 8 | 9 | true 10 | false 11 | ..\..\src\NLog.snk 12 | true 13 | latest 14 | true 15 | 16 | 17 | 18 | 19 | 20 | 21 | all 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | Always 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Hosting.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: ComVisible(false)] 5 | 6 | [assembly: Guid("a47d764d-c167-4bf2-bc60-d0d01eba9742")] 7 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Logging.Tests/ConfigSettingLayoutRendererTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.Extensions.Configuration; 3 | using Xunit; 4 | 5 | namespace NLog.Extensions.Logging.Tests 6 | { 7 | public class ConfigSettingLayoutRendererTests 8 | { 9 | [Fact] 10 | public void ConfigSettingFallbackDefaultLookup() 11 | { 12 | ConfigSettingLayoutRenderer.DefaultConfiguration = null; 13 | var layoutRenderer = new ConfigSettingLayoutRenderer { Item = "Options.TableName", Default = "MyTableName" }; 14 | var result = layoutRenderer.Render(LogEventInfo.CreateNullEvent()); 15 | Assert.Equal("MyTableName", result); 16 | } 17 | 18 | [Fact] 19 | public void ConfigSettingSimpleLookup() 20 | { 21 | var memoryConfig = new Dictionary(); 22 | memoryConfig["Mode"] = "Test"; 23 | ConfigSettingLayoutRenderer.DefaultConfiguration = new ConfigurationBuilder().AddInMemoryCollection(memoryConfig).Build(); 24 | var layoutRenderer = new ConfigSettingLayoutRenderer { Item = "Mode" }; 25 | var result = layoutRenderer.Render(LogEventInfo.CreateNullEvent()); 26 | Assert.Equal("Test", result); 27 | } 28 | 29 | [Fact] 30 | public void ConfigSettingNestedLookup() 31 | { 32 | var memoryConfig = new Dictionary(); 33 | memoryConfig["Options:TableName"] = "Test"; 34 | ConfigSettingLayoutRenderer.DefaultConfiguration = new ConfigurationBuilder().AddInMemoryCollection(memoryConfig).Build(); 35 | var layoutRenderer = new ConfigSettingLayoutRenderer { Item = "Options.TableName" }; 36 | var result = layoutRenderer.Render(LogEventInfo.CreateNullEvent()); 37 | Assert.Equal("Test", result); 38 | } 39 | 40 | [Fact] 41 | public void ConfigSettingConfigEscapeLookup() 42 | { 43 | var memoryConfig = new Dictionary(); 44 | memoryConfig["Logging:Microsoft.Logging"] = "Test"; 45 | ConfigSettingLayoutRenderer.DefaultConfiguration = new ConfigurationBuilder().AddInMemoryCollection(memoryConfig).Build(); 46 | var layoutRenderer = new ConfigSettingLayoutRenderer { Item = @"Logging.Microsoft\.Logging" }; 47 | var result = layoutRenderer.Render(LogEventInfo.CreateNullEvent()); 48 | Assert.Equal("Test", result); 49 | 50 | var layoutRenderer2 = new ConfigSettingLayoutRenderer { Item = @"Logging.Microsoft..Logging" }; 51 | var result2 = layoutRenderer2.Render(LogEventInfo.CreateNullEvent()); 52 | Assert.Equal("Test", result2); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Logging.Tests/CustomBeginScopeTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Microsoft.Extensions.Logging; 6 | using Xunit; 7 | 8 | namespace NLog.Extensions.Logging.Tests 9 | { 10 | public class CustomBeginScopeTest : NLogTestBase 11 | { 12 | [Fact] 13 | public void TestNonSerializableSayHello() 14 | { 15 | var target = new Targets.MemoryTarget { Layout = "${message} ${scopeproperty:World}. Welcome ${scopenested}" }; 16 | var runner = GetRunner(target: target); 17 | Assert.True(runner.SayHello().Wait(5000)); 18 | Assert.Single(target.Logs); 19 | Assert.Equal("Hello Earth. Welcome Earth People", target.Logs[0]); 20 | } 21 | 22 | [Fact] 23 | public void TestNonSerializableSayHelloWithScope() 24 | { 25 | var target = new Targets.MemoryTarget { Layout = "${message} ${scopeproperty:World}. Welcome ${scopenested}" }; 26 | var runner = GetRunner(new NLogProviderOptions { IncludeScopes = false }, target: target); 27 | Assert.True(runner.SayHello().Wait(5000)); 28 | Assert.Single(target.Logs); 29 | Assert.Equal("Hello . Welcome ", target.Logs[0]); 30 | } 31 | 32 | [Fact] 33 | public void TestNonSerializableSayHi() 34 | { 35 | var target = new Targets.MemoryTarget { Layout = "${message} ${scopeproperty:World}. Welcome ${scopenested}" }; 36 | var runner = GetRunner(target: target); 37 | var scopeString = runner.SayHi().Result; 38 | Assert.Single(target.Logs); 39 | Assert.Equal("Hi Earth. Welcome Earth People", target.Logs[0]); 40 | // Assert.Equal("Earth People", scopeString); <-- Bug https://github.com/aspnet/Logging/issues/893 41 | } 42 | 43 | [Fact] 44 | public void TestNonSerializableSayHiToEarth() 45 | { 46 | var target = new Targets.MemoryTarget { Layout = "${message} ${scopeproperty:Planet}. Welcome to the ${scopeproperty:Galaxy}" }; 47 | var runner = GetRunner(target: target); 48 | var scopeString = runner.SayHiToEarth().Result; 49 | Assert.Single(target.Logs); 50 | Assert.Equal("Hi Earth. Welcome to the Milky Way", target.Logs[0]); 51 | } 52 | 53 | [Fact] 54 | public void TestNonSerializableSayNothing() 55 | { 56 | var target = new Targets.MemoryTarget { Layout = "${message}" }; 57 | var runner = GetRunner(target: target); 58 | Assert.True(runner.SayNothing().Wait(5000)); 59 | Assert.Single(target.Logs); 60 | Assert.Equal("Nothing", target.Logs[0]); 61 | } 62 | 63 | [Fact] 64 | public void TestNonSerializableSaySomething() 65 | { 66 | var target = new Targets.MemoryTarget { Layout = "${message}${scopeproperty:Say}" }; 67 | var runner = GetRunner(target: target); 68 | Assert.True(runner.SaySomething().Wait(5000)); 69 | Assert.Single(target.Logs); 70 | Assert.Equal("SaySomething", target.Logs[0]); 71 | } 72 | 73 | public sealed class CustomBeginScopeTestRunner 74 | { 75 | private readonly ILogger _logger; 76 | 77 | public CustomBeginScopeTestRunner(ILogger logger) 78 | { 79 | _logger = logger; 80 | } 81 | 82 | public async Task SayHello() 83 | { 84 | using (_logger.BeginScope(new ActionLogScope("Earth"))) 85 | { 86 | await Task.Yield(); 87 | _logger.LogInformation("Hello"); 88 | } 89 | } 90 | 91 | public async Task SayHi() 92 | { 93 | using (var scopeState = _logger.BeginScope("{World} People", "Earth")) 94 | { 95 | await Task.Yield(); 96 | _logger.LogInformation("Hi"); 97 | return scopeState.ToString(); 98 | } 99 | } 100 | 101 | public async Task SayHiToEarth() 102 | { 103 | using (var scopeState = _logger.BeginScope("{Planet} in {Galaxy}", "Earth", "Milky Way")) 104 | { 105 | await Task.Yield(); 106 | _logger.LogInformation("Hi"); 107 | return scopeState.ToString(); 108 | } 109 | } 110 | 111 | public async Task SayNothing() 112 | { 113 | using (var scopeState = _logger.BeginScope(new Dictionary())) 114 | { 115 | await Task.Yield(); 116 | _logger.LogInformation("Nothing"); 117 | } 118 | } 119 | 120 | public async Task SaySomething() 121 | { 122 | using (var scopeState = _logger.BeginScope(new Dictionary() 123 | { 124 | { "Say", "Something" }, 125 | })) 126 | { 127 | await Task.Yield(); 128 | _logger.LogInformation("Say"); 129 | } 130 | } 131 | } 132 | 133 | private sealed class ActionLogScope : IReadOnlyList> 134 | { 135 | private readonly string _world; 136 | 137 | public ActionLogScope(string world) 138 | { 139 | if (world is null) 140 | { 141 | throw new ArgumentNullException(nameof(world)); 142 | } 143 | 144 | _world = world; 145 | } 146 | 147 | public KeyValuePair this[int index] 148 | { 149 | get 150 | { 151 | if (index == 0) 152 | { 153 | return new KeyValuePair("World", _world); 154 | } 155 | throw new IndexOutOfRangeException(nameof(index)); 156 | } 157 | } 158 | 159 | public int Count => 1; 160 | 161 | public IEnumerator> GetEnumerator() 162 | { 163 | for (var i = 0; i < Count; ++i) 164 | { 165 | yield return this[i]; 166 | } 167 | } 168 | 169 | public override string ToString() 170 | { 171 | // We don't include the _action.Id here because it's just an opaque guid, and if 172 | // you have text logging, you can already use the requestId for correlation. 173 | return string.Concat(_world, " People"); 174 | } 175 | 176 | IEnumerator IEnumerable.GetEnumerator() 177 | { 178 | return GetEnumerator(); 179 | } 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Logging.Tests/CustomLoggerCallSiteTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Logging; 4 | using NLog.Targets; 5 | using Xunit; 6 | 7 | namespace NLog.Extensions.Logging.Tests 8 | { 9 | public class CustomLoggerCallSiteTest : NLogTestBase 10 | { 11 | [Fact] 12 | public void TestCallSiteSayHello() 13 | { 14 | var target = new Targets.MemoryTarget { Layout = "${callsite}|${message}" }; 15 | var runner = GetRunner(target: target); 16 | 17 | runner.SayHello(); 18 | 19 | Assert.Single(target.Logs); 20 | Assert.Contains("SayHello", target.Logs[0]); 21 | Assert.Contains("stuff", target.Logs[0]); 22 | } 23 | 24 | private CustomLoggerCallSiteTestRunner GetRunner(NLogProviderOptions options = null, Target target = null) 25 | { 26 | return SetupServiceProvider(options, target, configureServices: (s) => s.AddTransient().AddSingleton(typeof(ILogger<>), typeof(SameAssemblyLogger<>))).GetRequiredService(); 27 | } 28 | 29 | public sealed class SameAssemblyLogger : ILogger 30 | { 31 | private readonly Microsoft.Extensions.Logging.ILogger _logger; 32 | 33 | public SameAssemblyLogger(ILoggerFactory loggerFactory) 34 | { 35 | _logger = loggerFactory.CreateLogger(); 36 | } 37 | 38 | public void Log(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception exception, 39 | Func formatter) 40 | { 41 | string Formatter(TState innerState, Exception innerException) 42 | { 43 | // additional logic for all providers goes here 44 | var message = formatter(innerState, innerException) ?? string.Empty; 45 | return message + " additional stuff in here"; 46 | } 47 | 48 | _logger.Log(logLevel, eventId, state, exception, Formatter); 49 | } 50 | 51 | public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) 52 | { 53 | return _logger.IsEnabled(logLevel); 54 | } 55 | 56 | public IDisposable BeginScope(TState state) 57 | { 58 | return _logger.BeginScope(state); 59 | } 60 | } 61 | 62 | public sealed class CustomLoggerCallSiteTestRunner 63 | { 64 | private readonly ILogger _logger; 65 | 66 | public CustomLoggerCallSiteTestRunner(ILogger logger) 67 | { 68 | _logger = logger; 69 | } 70 | 71 | public void SayHello() 72 | { 73 | _logger.LogInformation("Hello"); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Logging.Tests/CustomLoggerPropertyTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Logging; 7 | using NLog.Targets; 8 | using Xunit; 9 | 10 | namespace NLog.Extensions.Logging.Tests 11 | { 12 | public class CustomLoggerPropertyTest : NLogTestBase 13 | { 14 | [Fact] 15 | public void TestExtraMessageTemplatePropertySayHello() 16 | { 17 | var runner = GetRunner(); 18 | 19 | runner.SayHello(); 20 | 21 | Assert.Single(runner.GetTarget().Logs); 22 | Assert.Equal(@"Hello ""World""|userid=World, ActivityId=42", runner.GetTarget().Logs[0]); 23 | } 24 | 25 | [Fact] 26 | public void TestExtraMessageTemplatePropertySayHigh5() 27 | { 28 | var runner = GetRunner(); 29 | 30 | runner.SayHigh5(); 31 | 32 | Assert.Single(runner.GetTarget().Logs); 33 | Assert.Equal(@"Hi 5|ActivityId=42", runner.GetTarget().Logs[0]); 34 | } 35 | 36 | [Fact] 37 | public void TestExtraMessagePropertySayHi() 38 | { 39 | var options = new NLogProviderOptions { CaptureMessageTemplates = false }; 40 | var runner = GetRunner(options); 41 | 42 | runner.SayHigh5(); 43 | 44 | Assert.Single(runner.GetTarget().Logs); 45 | Assert.Equal(@"Hi 5|ActivityId=42, 0=5", runner.GetTarget().Logs[0]); 46 | } 47 | 48 | private CustomLoggerPropertyTestRunner GetRunner(NLogProviderOptions options = null) 49 | { 50 | var target = new Targets.MemoryTarget { Layout = "${message}|${all-event-properties}" }; 51 | return SetupServiceProvider(options, target, configureServices: (s) => s.AddTransient().AddSingleton(typeof(ILogger<>), typeof(SameAssemblyLogger<>))).GetRequiredService(); 52 | } 53 | 54 | public sealed class SameAssemblyLogger : ILogger 55 | { 56 | private readonly Microsoft.Extensions.Logging.ILogger _logger; 57 | 58 | public SameAssemblyLogger(ILoggerFactory loggerFactory) 59 | { 60 | _logger = loggerFactory.CreateLogger(); 61 | } 62 | 63 | public void Log(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception exception, 64 | Func formatter) 65 | { 66 | _logger.Log(logLevel, eventId, new MyLogEvent(state, formatter).AddProp("ActivityId", 42), exception, MyLogEvent.Formatter); 67 | } 68 | 69 | public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) 70 | { 71 | return _logger.IsEnabled(logLevel); 72 | } 73 | 74 | public IDisposable BeginScope(TState state) 75 | { 76 | return _logger.BeginScope(state); 77 | } 78 | } 79 | 80 | public sealed class CustomLoggerPropertyTestRunner 81 | { 82 | private readonly ILogger _logger; 83 | private readonly IServiceProvider _serviceProvider; 84 | 85 | public LogFactory LogFactory => (_serviceProvider.GetRequiredService() as NLogLoggerProvider)?.LogFactory; 86 | 87 | public MemoryTarget GetTarget() => LogFactory?.Configuration?.AllTargets.OfType().FirstOrDefault(); 88 | 89 | public CustomLoggerPropertyTestRunner(ILogger logger, IServiceProvider serviceProvider) 90 | { 91 | _logger = logger; 92 | _serviceProvider = serviceProvider; 93 | } 94 | 95 | public void SayHello() 96 | { 97 | _logger.LogInformation("Hello {$userid}", "World"); 98 | } 99 | 100 | public void SayHigh5() 101 | { 102 | _logger.LogInformation("Hi {0}", 5); 103 | } 104 | } 105 | 106 | sealed class MyLogEvent : IReadOnlyList> 107 | { 108 | private readonly List> _properties = new List>(); 109 | private readonly Func _originalFormattter; 110 | private readonly TState _originalState; 111 | 112 | public MyLogEvent(TState state, Func formatter) 113 | { 114 | _originalState = state; 115 | _originalFormattter = formatter; 116 | if (_originalState is IReadOnlyList> customProperties) 117 | { 118 | _properties.AddRange(customProperties); 119 | } 120 | } 121 | 122 | public MyLogEvent AddProp(string name, T value) 123 | { 124 | _properties.Insert(0, new KeyValuePair(name, value)); 125 | return this; 126 | } 127 | 128 | public IEnumerator> GetEnumerator() 129 | { 130 | return ((IReadOnlyList>)_properties).GetEnumerator(); 131 | } 132 | 133 | IEnumerator IEnumerable.GetEnumerator() 134 | { 135 | return ((IReadOnlyList>)_properties).GetEnumerator(); 136 | } 137 | 138 | public static Func, Exception, string> Formatter { get; } = (l, e) => l._originalFormattter(l._originalState, e); 139 | 140 | public int Count => ((IReadOnlyList>)_properties).Count; 141 | 142 | public KeyValuePair this[int index] => ((IReadOnlyList>)_properties)[index]; 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Logging.Tests/Extensions/ConfigureExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using NLog.Config; 3 | using NLog.Extensions.Logging; 4 | using System; 5 | using System.Linq; 6 | using NLog.Targets; 7 | using Xunit; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using System.Collections.Generic; 10 | using Microsoft.Extensions.Configuration; 11 | 12 | namespace NLog.Extensions.Logging.Tests.Extensions 13 | { 14 | public class ConfigureExtensionsTests 15 | { 16 | #if NET5_0_OR_GREATER 17 | [Fact] 18 | public void AddNLog_LoggerFactory_IncludeActivityIdsWithBeginScope() 19 | { 20 | // Arrange 21 | var loggerFactory = LoggerFactory.Create(builder => builder.AddNLog(new NLogProviderOptions { IncludeActivityIdsWithBeginScope = true })); 22 | var config = CreateConfigWithMemoryTarget(out var memoryTarget, $"${{scopeproperty:ParentId}} - ${{message}}"); 23 | 24 | // Act 25 | LogManager.Configuration = config; 26 | var logger = loggerFactory.CreateLogger(nameof(AddNLog_LoggerFactory_IncludeActivityIdsWithBeginScope)); 27 | var activity = new System.Diagnostics.Activity("TestActivity").SetParentId("42").Start(); 28 | var scopeProperties = new Dictionary { { "RequestId", "123" }, { "RequestPath", "Unknown" } }; 29 | using (logger.BeginScope(new ArraySegment>(scopeProperties.ToArray()))) 30 | { 31 | logger.LogInformation(default(EventId), "test message with {0} arg", 1); 32 | } 33 | 34 | // Assert 35 | AssertSingleMessage(memoryTarget, "42 - test message with 1 arg"); 36 | } 37 | #endif 38 | 39 | [Fact] 40 | public void AddNLog_LoggingBuilder_LogInfo_ShouldLogToNLog() 41 | { 42 | // Arrange 43 | ILoggingBuilder builder = new LoggingBuilderStub(); 44 | var config = CreateConfigWithMemoryTarget(out var memoryTarget); 45 | 46 | // Act 47 | builder.AddNLog(config); 48 | var provider = GetLoggerProvider(builder); 49 | var logger = provider.CreateLogger("logger1"); 50 | 51 | logger.LogInformation("test message with {0} arg", 1); 52 | 53 | // Assert 54 | AssertSingleMessage(memoryTarget, "Info|test message with 1 arg"); 55 | } 56 | 57 | [Theory] 58 | [InlineData("EventId", "eventId2", true)] 59 | [InlineData("EventName", "", true)] 60 | [InlineData("EventId_Name", "eventId2", true)] 61 | [InlineData("EventId_Id", "2", true)] 62 | [InlineData("EventId", "2", false)] 63 | [InlineData("EventName", "eventId2", false)] 64 | [InlineData("EventId_Id", "", false)] 65 | [InlineData("EventId_Name", "", false)] 66 | public void AddNLog_LoggingBuilder_LogInfoWithEventId_ShouldLogToNLogWithEventId(string eventPropery, string expectedEventInLog, bool captureEntireEventId) 67 | { 68 | // Arrange 69 | ILoggingBuilder builder = new LoggingBuilderStub(); 70 | var config = CreateConfigWithMemoryTarget(out var memoryTarget, $"${{event-properties:{eventPropery}}} - ${{message}}"); 71 | var options = new NLogProviderOptions { EventIdSeparator = "_", CaptureEventId = captureEntireEventId ? EventIdCaptureType.Legacy : (EventIdCaptureType.EventId | EventIdCaptureType.EventName) }; 72 | 73 | // Act 74 | builder.AddNLog(config, options); 75 | var provider = GetLoggerProvider(builder); 76 | var logger = provider.CreateLogger("logger1"); 77 | logger.LogInformation(new EventId(2, "eventId2"), "test message with {0} arg", 1); 78 | 79 | // Assert 80 | AssertSingleMessage(memoryTarget, $"{expectedEventInLog} - test message with 1 arg"); 81 | } 82 | 83 | [Fact] 84 | public void AddNLog_LogFactoryBuilder_LogInfo_ShouldLogToNLog() 85 | { 86 | // Arrange 87 | ILoggingBuilder builder = new LoggingBuilderStub(); 88 | 89 | // Act 90 | MemoryTarget memoryTarget = null; 91 | builder.AddNLog(ServiceProvider => CreateConfigWithMemoryTarget(out memoryTarget, logFactory: new NLog.LogFactory()).LogFactory); 92 | var provider = GetLoggerProvider(builder); 93 | var logger = provider.CreateLogger("logger1"); 94 | 95 | logger.LogInformation("test message with {0} arg", 1); 96 | 97 | // Assert 98 | AssertSingleMessage(memoryTarget, "Info|test message with 1 arg"); 99 | } 100 | 101 | [Fact] 102 | public void AddNLog_ReplaceLoggerFactory() 103 | { 104 | // Arrange 105 | ILoggingBuilder builder = new LoggingBuilderStub(); 106 | 107 | // Act 108 | builder.AddNLog(new NLogProviderOptions() { ReplaceLoggerFactory = true, RemoveLoggerFactoryFilter = true }); 109 | var loggerFactory = builder.Services.BuildServiceProvider().GetService(); 110 | var loggerProvider = GetLoggerProvider(builder); 111 | 112 | // Assert 113 | Assert.Equal(typeof(NLogLoggerFactory), loggerFactory.GetType()); 114 | Assert.Equal(typeof(NLogLoggerProvider), loggerProvider.GetType()); 115 | } 116 | 117 | [Fact] 118 | public void AddNLog_ServiceCollection_ReplaceLoggerFactory() 119 | { 120 | // Arrange 121 | var collection = new ServiceCollection(); 122 | 123 | // Act 124 | collection.AddNLog(new NLogProviderOptions() { ReplaceLoggerFactory = true }); 125 | 126 | // Assert 127 | using var provider = collection.BuildServiceProvider(); 128 | var loggerFactory = provider.GetService(); 129 | Assert.NotNull(loggerFactory); 130 | Assert.Equal(typeof(NLogLoggerFactory), loggerFactory.GetType()); 131 | } 132 | 133 | [Fact] 134 | public void AddNLog_ServiceCollection_ShouldLog() 135 | { 136 | // Arrange 137 | var collection = new ServiceCollection(); 138 | 139 | // Act 140 | var nlogTarget = new Targets.MemoryTarget() { Name = "Output" }; 141 | collection.AddNLog(new NLogProviderOptions(), (ServiceProvider) => 142 | { 143 | var nLogFactory = new LogFactory().Setup().LoadConfiguration(c => c.ForLogger().WriteTo(nlogTarget)).LogFactory; 144 | return nLogFactory; 145 | }); 146 | 147 | // Assert 148 | using var provider = collection.BuildServiceProvider(); 149 | var loggerFactory = provider.GetService(); 150 | Assert.NotNull(loggerFactory); 151 | 152 | var logger = loggerFactory.CreateLogger("Hello"); 153 | logger.LogCritical("World"); 154 | Assert.Single(nlogTarget.Logs); 155 | } 156 | 157 | [Fact] 158 | public void AddNLog_ArgumentNullException() 159 | { 160 | ILoggingBuilder loggingBuilder = null; 161 | var argNulLException = Assert.Throws(() => loggingBuilder.AddNLog()); 162 | Assert.Equal("builder", argNulLException.ParamName); 163 | } 164 | 165 | [Fact] 166 | public void AddNLog_WithConfig_ReplaceLoggerFactory() 167 | { 168 | // Arrange 169 | ILoggingBuilder builder = new LoggingBuilderStub(); 170 | var memoryConfig = new Dictionary(); 171 | memoryConfig["Logging:NLog:ReplaceLoggerFactory"] = "True"; 172 | memoryConfig["Logging:NLog:RemoveLoggerFactoryFilter"] = "True"; 173 | var configuration = new ConfigurationBuilder().AddInMemoryCollection(memoryConfig).Build(); 174 | 175 | // Act 176 | builder.AddNLog(configuration); 177 | var loggerFactory = builder.Services.BuildServiceProvider().GetService(); 178 | var loggerProvider = GetLoggerProvider(builder); 179 | 180 | // Assert 181 | Assert.Equal(typeof(NLogLoggerFactory), loggerFactory.GetType()); 182 | Assert.Equal(typeof(NLogLoggerProvider), loggerProvider.GetType()); 183 | } 184 | 185 | private static ILoggerProvider GetLoggerProvider(ILoggingBuilder builder) 186 | { 187 | var services = builder.Services; 188 | var serviceProvider = services.BuildServiceProvider(); 189 | var provider = serviceProvider.GetRequiredService(); 190 | return provider; 191 | } 192 | 193 | internal sealed class LoggingBuilderStub : ILoggingBuilder 194 | { 195 | public IServiceCollection Services { get; set; } = new ServiceCollection(); 196 | } 197 | 198 | private static void AssertSingleMessage(MemoryTarget memoryTarget, string expectedMessage) 199 | { 200 | Assert.Single(memoryTarget.Logs); 201 | var log = memoryTarget.Logs.Single(); 202 | Assert.Equal(expectedMessage, log); 203 | } 204 | 205 | private static LoggingConfiguration CreateConfigWithMemoryTarget(out MemoryTarget memoryTarget, string levelMessage = "${level}|${message}", LogFactory logFactory = null) 206 | { 207 | var config = new LoggingConfiguration(logFactory); 208 | memoryTarget = new MemoryTarget { Layout = levelMessage }; 209 | config.AddRuleForAllLevels(memoryTarget); 210 | if (logFactory != null) 211 | logFactory.Configuration = config; 212 | return config; 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Logging.Tests/Logging/NLogLoggerFactoryTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.Extensions.Logging; 3 | using Xunit; 4 | 5 | namespace NLog.Extensions.Logging.Tests.Logging 6 | { 7 | public class NLogLoggerFactoryTests 8 | { 9 | [Fact] 10 | public void Dispose_HappyPath_FlushLogFactory() 11 | { 12 | // Arrange 13 | var logFactory = new LogFactory(); 14 | var logConfig = new Config.LoggingConfiguration(logFactory); 15 | var target = new Targets.Wrappers.BufferingTargetWrapper("buffer", new Targets.MemoryTarget("output")); 16 | logConfig.AddRuleForAllLevels(target); 17 | logFactory.Configuration = logConfig; 18 | var provider = new NLogLoggerProvider(new NLogProviderOptions(), logFactory); 19 | var loggerFactory = new NLogLoggerFactory(provider); 20 | 21 | // Act 22 | loggerFactory.CreateLogger("test").LogInformation("Hello"); 23 | loggerFactory.Dispose(); 24 | 25 | // Assert 26 | Assert.Single(logFactory.Configuration.AllTargets.OfType().FirstOrDefault().Logs); 27 | } 28 | 29 | [Fact] 30 | public void CreateLogger_HappyPath_LoggerWithCorrectName() 31 | { 32 | // Arrange 33 | var loggerFactory = new NLogLoggerFactory(); 34 | string loggerName = "namespace.class1"; 35 | 36 | // Act 37 | var result = loggerFactory.CreateLogger(loggerName); 38 | 39 | // Assert 40 | Assert.NotNull(result); 41 | var logger = Assert.IsType(result); 42 | Assert.Equal(loggerName, logger.LoggerName); 43 | } 44 | 45 | [Fact] 46 | public void AddProvider_StateUnderTest_ExpectedBehavior() 47 | { 48 | // Arrange 49 | var logFactory = new LogFactory(); 50 | var logConfig = new Config.LoggingConfiguration(logFactory); 51 | logConfig.AddRuleForAllLevels(new Targets.MemoryTarget("output")); 52 | logFactory.Configuration = logConfig; 53 | var provider = new NLogLoggerProvider(null, logFactory); 54 | var loggerFactory = new NLogLoggerFactory(provider); 55 | 56 | // Act 57 | ILoggerProvider newProvider = new NLogLoggerProvider(); 58 | loggerFactory.AddProvider(newProvider); 59 | loggerFactory.CreateLogger("test").LogInformation("Hello"); 60 | 61 | // Assert 62 | Assert.Single(logFactory.Configuration.FindTargetByName("output").Logs); 63 | } 64 | 65 | [Fact] 66 | public void CreateLogger_SameName_ReturnsSameInstanceTest() 67 | { 68 | // Arrange 69 | var loggerFactory = new NLogLoggerFactory(); 70 | string loggerName = "namespace.class1"; 71 | 72 | // Act 73 | var result1 = loggerFactory.CreateLogger(loggerName); 74 | var result2 = loggerFactory.CreateLogger(loggerName); 75 | 76 | // Assert 77 | Assert.NotNull(result1); 78 | Assert.Equal(loggerName, result1.ToString()); 79 | Assert.Same(result1, result2); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Logging.Tests/Logging/NLogLoggerProviderTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Xunit; 3 | 4 | namespace NLog.Extensions.Logging.Tests.Logging 5 | { 6 | public class NLogLoggerProviderTests : NLogTestBase 7 | { 8 | [Fact] 9 | public void CreateLogger_HappyPath_LoggerWithCorrectName() 10 | { 11 | // Arrange 12 | var unitUnderTest = new NLogLoggerProvider(); 13 | string name = "namespace.class1"; 14 | 15 | // Act 16 | var result = unitUnderTest.CreateLogger(name); 17 | 18 | // Assert 19 | Assert.NotNull(result); 20 | var logger = Assert.IsType(result); 21 | Assert.Equal(name, logger.LoggerName); 22 | } 23 | 24 | [Fact] 25 | public void Dispose_HappyPath_FlushLogFactory() 26 | { 27 | // Arrange 28 | var logFactory = new LogFactory(); 29 | var logConfig = new Config.LoggingConfiguration(logFactory); 30 | logConfig.AddTarget(new Targets.MemoryTarget("output")); 31 | logConfig.AddRuleForAllLevels(new Targets.Wrappers.BufferingTargetWrapper("buffer", logConfig.FindTargetByName("output"))); 32 | logFactory.Configuration = logConfig; 33 | var provider = new NLogLoggerProvider(null, logFactory); 34 | 35 | // Act 36 | provider.CreateLogger("test").LogInformation("Hello"); 37 | provider.Dispose(); 38 | 39 | // Assert 40 | Assert.Single(logFactory.Configuration.FindTargetByName("output").Logs); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Logging.Tests/Logging/NLogMessageParameterListTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using NLog.MessageTemplates; 5 | using Xunit; 6 | 7 | namespace NLog.Extensions.Logging.Tests.Logging 8 | { 9 | public class NLogMessageParameterListTests 10 | { 11 | private readonly NLogMessageParameterList _messageParameterList = NLogMessageParameterList.TryParse(new List> 12 | { 13 | new KeyValuePair("nr1", "a"), 14 | new KeyValuePair("@nr2", "b"), 15 | }); 16 | 17 | [Fact] 18 | public void CopyTo_FullCopy_AllCopied() 19 | { 20 | // Arrange 21 | MessageTemplateParameter[] array = new MessageTemplateParameter[2]; 22 | int arrayIndex = 0; 23 | 24 | // Act 25 | _messageParameterList.CopyTo(array, arrayIndex); 26 | 27 | // Assert 28 | AssertParameters(array); 29 | } 30 | 31 | [Fact] 32 | public void CopyTo_WithOffset_AllCopied() 33 | { 34 | // Arrange 35 | MessageTemplateParameter[] array = new MessageTemplateParameter[3]; 36 | int arrayIndex = 1; 37 | 38 | // Act 39 | _messageParameterList.CopyTo(array, arrayIndex); 40 | 41 | // Assert 42 | AssertParameters(array, 1); 43 | } 44 | 45 | [Fact] 46 | public void GetEnumerator_StateUnderTest_ExpectedBehavior() 47 | { 48 | // Arrange 49 | 50 | // Act 51 | using (var enumerator = _messageParameterList.GetEnumerator()) 52 | { 53 | var list = new List(); 54 | while (enumerator.MoveNext()) 55 | list.Add(enumerator.Current); 56 | var array = list.ToArray(); 57 | 58 | // Assert 59 | AssertParameters(array); 60 | } 61 | } 62 | 63 | [Fact] 64 | public void Add_ThrowsNotSupported() 65 | { 66 | // Arrange 67 | MessageTemplateParameter item = new MessageTemplateParameter(); 68 | 69 | // Act & Assert 70 | Assert.Throws(() => _messageParameterList.Add(item)); 71 | } 72 | 73 | [Fact] 74 | public void Clear_ThrowsNotSupported() 75 | { 76 | // Arrange 77 | 78 | // Act & Assert 79 | Assert.Throws(() => _messageParameterList.Clear()); 80 | } 81 | 82 | [Fact] 83 | public void Contains_ThrowsNotSupported() 84 | { 85 | // Arrange 86 | MessageTemplateParameter item = new MessageTemplateParameter(); 87 | 88 | // Act & Assert 89 | Assert.Throws(() => _messageParameterList.Contains(item)); 90 | } 91 | 92 | 93 | [Fact] 94 | public void IndexOf_ThrowsNotSupported() 95 | { 96 | // Arrange 97 | MessageTemplateParameter item = new MessageTemplateParameter(); 98 | 99 | // Act & Assert 100 | Assert.Throws(() => _messageParameterList.IndexOf(item)); 101 | } 102 | 103 | [Fact] 104 | public void Insert_ThrowsNotSupported() 105 | { 106 | // Arrange 107 | int index = 0; 108 | MessageTemplateParameter item = new MessageTemplateParameter(); 109 | 110 | // Act & Assert 111 | Assert.Throws(() => _messageParameterList.Insert(index, item)); 112 | } 113 | 114 | [Fact] 115 | public void Remove_ThrowsNotSupported() 116 | { 117 | // Arrange 118 | MessageTemplateParameter item = new MessageTemplateParameter(); 119 | 120 | // Act & Assert 121 | Assert.Throws(() => _messageParameterList.Remove(item)); 122 | } 123 | 124 | [Fact] 125 | public void RemoveAt_ThrowsNotSupported() 126 | { 127 | // Arrange 128 | 129 | // Act & Assert 130 | Assert.Throws(() => _messageParameterList.RemoveAt(0)); 131 | } 132 | 133 | private static void AssertParameters(MessageTemplateParameter[] array, int startIndex = 0) 134 | { 135 | AssertParameter(array[startIndex], "nr1", "a", CaptureType.Normal); 136 | AssertParameter(array[startIndex + 1], "nr2", "b", CaptureType.Serialize); 137 | } 138 | 139 | private static void AssertParameter(MessageTemplateParameter item, string expectedName, string expectedValue, CaptureType captureType) 140 | { 141 | Assert.Equal(expectedName, item.Name); 142 | Assert.Null(item.Format); 143 | Assert.Null(item.PositionalIndex); 144 | Assert.Equal(expectedValue, item.Value); 145 | Assert.Equal(captureType, item.CaptureType); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Logging.Tests/MicrosoftConsoleJsonLayoutTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.Extensions.Logging; 6 | using Xunit; 7 | 8 | namespace NLog.Extensions.Logging.Tests 9 | { 10 | public class MicrosoftConsoleJsonLayoutTests 11 | { 12 | [Fact] 13 | public void MicrosoftConsoleJsonLayout_NullEvent() 14 | { 15 | var layout = new MicrosoftConsoleJsonLayout() { TimestampFormat = null }; 16 | var result = layout.Render(LogEventInfo.CreateNullEvent()); 17 | Assert.Contains("{ \"EventId\": 0, \"LogLevel\": \"Critical\" }", result); 18 | } 19 | 20 | [Fact] 21 | public void MicrosoftConsoleJsonLayout_TimestampFormat() 22 | { 23 | var layout = new MicrosoftConsoleJsonLayout() { TimestampFormat = "R" }; 24 | var logEvent = new LogEventInfo(LogLevel.Error, "MyLogger", "Hello World"); 25 | var result = layout.Render(logEvent); 26 | Assert.Equal($"{{ \"Timestamp\": \"{logEvent.TimeStamp.ToUniversalTime().ToString("R")}\", \"EventId\": {0}, \"LogLevel\": \"Error\", \"Category\": \"MyLogger\", \"Message\": \"Hello World\", \"State\": {{ \"{{OriginalFormat}}\": \"Hello World\" }} }}", result); 27 | } 28 | 29 | [Fact] 30 | public void MicrosoftConsoleJsonLayout_ExceptionEvent() 31 | { 32 | var layout = new MicrosoftConsoleJsonLayout(); 33 | var exception = new ArgumentException("Test"); 34 | var eventId = 42; 35 | var logEvent1 = new LogEventInfo(LogLevel.Error, "MyLogger", null, "Alert {EventId}", new object[] { eventId }, exception); 36 | var result1 = layout.Render(logEvent1); 37 | Assert.Equal($"{{ \"Timestamp\": \"{logEvent1.TimeStamp.ToUniversalTime().ToString("O")}\", \"EventId\": {eventId}, \"LogLevel\": \"Error\", \"Category\": \"MyLogger\", \"Message\": \"Alert {eventId}\", \"Exception\": \"{exception.ToString()}\", \"State\": {{ \"{{OriginalFormat}}\": \"Alert {{EventId}}\" }} }}", result1); 38 | 39 | var eventId2 = 420; 40 | var logEvent2 = new LogEventInfo(LogLevel.Error, "MyLogger", null, "Alert {EventId_Id}", new object[] { eventId2 }, exception); 41 | var result2 = layout.Render(logEvent2); 42 | Assert.Equal($"{{ \"Timestamp\": \"{logEvent2.TimeStamp.ToUniversalTime().ToString("O")}\", \"EventId\": {eventId2}, \"LogLevel\": \"Error\", \"Category\": \"MyLogger\", \"Message\": \"Alert {eventId2}\", \"Exception\": \"{exception.ToString()}\", \"State\": {{ \"{{OriginalFormat}}\": \"Alert {{EventId_Id}}\" }} }}", result2); 43 | } 44 | 45 | [Fact] 46 | public void MicrosoftConsoleJsonLayout_IncludeScopesEvent() 47 | { 48 | var logFactory = new LogFactory().Setup().LoadConfiguration(builder => 49 | { 50 | var layout = new MicrosoftConsoleJsonLayout() { IncludeScopes = true }; 51 | builder.ForLogger().WriteTo(new NLog.Targets.MemoryTarget("test") { Layout = layout }); 52 | }).LogFactory; 53 | var logger = logFactory.GetCurrentClassLogger(); 54 | 55 | var exception = new ArgumentException("Test"); 56 | var eventId = 42; 57 | using var requestScope = logger.PushScopeNested("Request Started"); 58 | using var activityScope = logger.PushScopeNested("Activity Started"); 59 | var logEvent = new LogEventInfo(LogLevel.Error, null, null, "Alert {EventId}", new object[] { eventId }, exception); 60 | logger.Log(logEvent); 61 | var result = logFactory.Configuration.FindTargetByName("test")?.Logs?.FirstOrDefault(); 62 | Assert.Equal($"{{ \"Timestamp\": \"{logEvent.TimeStamp.ToUniversalTime().ToString("O")}\", \"EventId\": {eventId}, \"LogLevel\": \"Error\", \"Category\": \"{typeof(MicrosoftConsoleJsonLayoutTests).FullName}\", \"Message\": \"Alert {eventId}\", \"Exception\": \"{exception.ToString()}\", \"State\": {{ \"{{OriginalFormat}}\": \"Alert {{EventId}}\" }}, \"Scopes\": [ \"Request Started\", \"Activity Started\" ] }}", result); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Logging.Tests/MicrosoftConsoleLayoutRendererTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xunit; 5 | 6 | namespace NLog.Extensions.Logging.Tests 7 | { 8 | public class MicrosoftConsoleLayoutRendererTest 9 | { 10 | [Fact] 11 | public void MicrosoftConsoleLayoutRenderer_NullEvent() 12 | { 13 | var layoutRenderer = new MicrosoftConsoleLayoutRenderer(); 14 | var result = layoutRenderer.Render(LogEventInfo.CreateNullEvent()); 15 | Assert.Contains("crit: [0]", result); 16 | } 17 | 18 | [Fact] 19 | public void MicrosoftConsoleLayoutRenderer_ExceptionEvent() 20 | { 21 | var layoutRenderer = new MicrosoftConsoleLayoutRenderer(); 22 | var exception = new ArgumentException("Test"); 23 | var eventId = 42; 24 | var result = layoutRenderer.Render(new LogEventInfo(LogLevel.Error, "MyLogger", null, "Alert {EventId_Id}", new object[] { eventId }, exception)); 25 | Assert.Equal($"fail: MyLogger[{eventId}]{Environment.NewLine} Alert 42{Environment.NewLine}{exception}", result); 26 | } 27 | 28 | [Fact] 29 | public void MicrosoftConsoleLayoutRenderer_OutOfMapperBoundsEventId() 30 | { 31 | var layoutRenderer = new MicrosoftConsoleLayoutRenderer(); 32 | var exception = new ArgumentException("Test"); 33 | var eventId = 500; 34 | var result = layoutRenderer.Render(new LogEventInfo(LogLevel.Error, "MyLogger", null, "Alert {EventId_Id}", new object[] { eventId }, exception)); 35 | Assert.Equal($"fail: MyLogger[{eventId}]{Environment.NewLine} Alert 500{Environment.NewLine}{exception}", result); 36 | } 37 | 38 | 39 | [Fact] 40 | public void MicrosoftConsoleLayoutRenderer_TimestampFormat() 41 | { 42 | var timestampFormat = "hh:mm:ss"; 43 | var layoutRenderer = new MicrosoftConsoleLayoutRenderer() { TimestampFormat = timestampFormat }; 44 | var exception = new ArgumentException("Test"); 45 | var eventId = 42; 46 | var logEvent = new LogEventInfo(LogLevel.Error, "MyLogger", null, "Alert {EventId}", new object[] { eventId }, exception); 47 | var result1 = layoutRenderer.Render(logEvent); 48 | Assert.Equal($"{logEvent.TimeStamp.ToString(timestampFormat)} fail: MyLogger[{eventId}]{Environment.NewLine} Alert 42{Environment.NewLine}{exception}", result1); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Logging.Tests/NLog.Extensions.Logging.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net462;net8.0 5 | Library 6 | false 7 | Full 8 | 9 | true 10 | false 11 | ..\..\src\NLog.snk 12 | true 13 | latest 14 | true 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | all 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Logging.Tests/NLogMessageParameterListTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NLog.MessageTemplates; 4 | using Xunit; 5 | 6 | namespace NLog.Extensions.Logging.Tests 7 | { 8 | public class NLogMessageParameterListTests 9 | { 10 | [Fact] 11 | public void CreateNLogMessageParameterListWithEmptyKey() 12 | { 13 | var items = new List> 14 | { 15 | new KeyValuePair("", 1), 16 | new KeyValuePair("a", 2), 17 | new KeyValuePair("b", 3), 18 | new KeyValuePair("{OriginalFormat}", "{0}{1}{2}"), 19 | }; 20 | var list = NLogMessageParameterList.TryParse(items); 21 | 22 | Assert.Equal(2, list.Count); 23 | Assert.Equal(new MessageTemplateParameter("a", 2, null, CaptureType.Normal), list[0]); 24 | Assert.Equal(new MessageTemplateParameter("b", 3, null, CaptureType.Normal), list[1]); 25 | } 26 | 27 | [Theory] 28 | [InlineData(null)] 29 | [InlineData(1)] 30 | public void CreateNLogMessageParameterListWithOriginalFormatKey(object originalFormat) 31 | { 32 | var items = new List> 33 | { 34 | new KeyValuePair("a", 2), 35 | new KeyValuePair("{OriginalFormat}", originalFormat), 36 | new KeyValuePair("b", 3) 37 | }; 38 | var list = NLogMessageParameterList.TryParse(items); 39 | 40 | Assert.Equal(2, list.Count); 41 | Assert.Equal(new MessageTemplateParameter("a", 2, null, CaptureType.Normal), list[0]); 42 | Assert.Equal(new MessageTemplateParameter("b", 3, null, CaptureType.Normal), list[1]); 43 | } 44 | 45 | [Fact] 46 | public void CreateNLogMessageParameterDifferentCaptureTypes() 47 | { 48 | var items = new List> 49 | { 50 | new KeyValuePair("a", 1), 51 | new KeyValuePair("$b", 2), 52 | new KeyValuePair("@c", 3) 53 | }; 54 | var list = NLogMessageParameterList.TryParse(items); 55 | 56 | Assert.Equal(3, list.Count); 57 | Assert.Equal(new MessageTemplateParameter("a", 1, null, CaptureType.Normal), list[0]); 58 | Assert.Equal(new MessageTemplateParameter("b", 2, null, CaptureType.Stringify), list[1]); 59 | Assert.Equal(new MessageTemplateParameter("c", 3, null, CaptureType.Serialize), list[2]); 60 | Assert.True(list.HasComplexParameters); 61 | Assert.False(list.IsPositional); 62 | } 63 | 64 | [Fact] 65 | public void CreateNLogMessageParameterIsPositional() 66 | { 67 | var items = new List> 68 | { 69 | new KeyValuePair("0", 1), 70 | new KeyValuePair("1", 2), 71 | new KeyValuePair("2", 3) 72 | }; 73 | var list = NLogMessageParameterList.TryParse(items); 74 | 75 | Assert.Empty(list); 76 | Assert.False(list.HasComplexParameters); 77 | Assert.True(list.IsPositional); 78 | } 79 | 80 | [Fact] 81 | public void CreateNLogMessageParameterMixedPositional() 82 | { 83 | var items = new List> 84 | { 85 | new KeyValuePair("2", 1), 86 | new KeyValuePair("1", 2), 87 | new KeyValuePair("0", 3) 88 | }; 89 | var list = NLogMessageParameterList.TryParse(items); 90 | 91 | Assert.Empty(list); 92 | Assert.True(list.HasComplexParameters); 93 | Assert.True(list.IsPositional); 94 | } 95 | 96 | [Fact] 97 | public void TryParseShouldReturnEmptyListWhenInputIsEmpty() 98 | { 99 | var items = new List>{}; 100 | NLogMessageParameterList parsedList = NLogMessageParameterList.TryParse(items); 101 | 102 | var expectedCount = 0; 103 | Assert.NotNull(parsedList); 104 | Assert.Equal(expectedCount, parsedList.Count); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Logging.Tests/NLogTestBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace NLog.Extensions.Logging.Tests 6 | { 7 | public abstract class NLogTestBase 8 | { 9 | private IServiceProvider _serviceProvider; 10 | 11 | protected NLogTestBase() 12 | { 13 | LogManager.ThrowExceptions = true; 14 | } 15 | 16 | protected IServiceProvider SetupServiceProvider(NLogProviderOptions options = null, Targets.Target target = null, Action configureServices = null) 17 | { 18 | IServiceProvider serviceProvider = _serviceProvider; 19 | 20 | if (serviceProvider is null || options != null || target != null) 21 | { 22 | var configTarget = target ?? new Targets.MemoryTarget("output") { Layout = "${logger}|${level:uppercase=true}|${message}|${all-event-properties:includeScopeProperties=true}${onexception:|}${exception:format=tostring}" }; 23 | 24 | var services = new ServiceCollection(); 25 | services.AddLogging(builder => builder.AddNLog(options ?? new NLogProviderOptions(), provider => new LogFactory())); 26 | configureServices?.Invoke(services); 27 | serviceProvider = services.BuildServiceProvider(); 28 | if (options is null && target is null) 29 | { 30 | _serviceProvider = serviceProvider; 31 | var loggerProvider = serviceProvider.GetRequiredService() as NLogLoggerProvider; 32 | var nlogConfig = new Config.LoggingConfiguration(loggerProvider.LogFactory); 33 | nlogConfig.AddRuleForAllLevels(configTarget); 34 | loggerProvider.LogFactory.Configuration = nlogConfig; 35 | } 36 | else 37 | { 38 | var loggerProvider = serviceProvider.GetRequiredService() as NLogLoggerProvider; 39 | var nlogConfig = new Config.LoggingConfiguration(loggerProvider.LogFactory); 40 | nlogConfig.AddRuleForAllLevels(configTarget); 41 | loggerProvider.LogFactory.Configuration = nlogConfig; 42 | } 43 | } 44 | 45 | return serviceProvider; 46 | } 47 | 48 | protected T GetRunner(NLogProviderOptions options = null, Targets.Target target = null) where T : class 49 | { 50 | // Start program 51 | return SetupServiceProvider(options, target, configureServices: (s) => s.AddTransient()).GetRequiredService(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/NLog.Extensions.Logging.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | // General Information about an assembly is controlled through the following 4 | // set of attributes. Change these attribute values to modify the information 5 | // associated with an assembly. 6 | 7 | // Setting ComVisible to false makes the types in this assembly not visible 8 | // to COM components. If you need to access a type in this assembly from 9 | // COM, set the ComVisible attribute to true on that type. 10 | [assembly: ComVisible(false)] 11 | 12 | [assembly: Xunit.CollectionBehavior(DisableTestParallelization = true)] --------------------------------------------------------------------------------