├── .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 | 
2 |
3 | # NLog.Extensions.Logging & NLog.Extensions.Hosting
4 |
5 |
6 | [](https://www.nuget.org/packages/NLog.Extensions.Logging)
7 |
8 |
9 | [](https://www.nuget.org/packages/NLog.Extensions.Hosting)
10 |
11 |
12 | [](https://ci.appveyor.com/project/nlog/nlog-framework-logging/branch/master)
13 | [](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master)
14 | [](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master)
15 | [](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master)
16 | [](https://sonarcloud.io/project/issues?id=nlog.extensions.logging&branch=master&resolved=false&types=CODE_SMELL)
17 | [](https://sonarcloud.io/component_measures/domain/Duplications?id=nlog.extensions.logging&branch=master)
18 | [](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master)
19 | [](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/dashboard/?id=nlog.extensions.logging&branch=master)
4 | [](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master)
5 | [](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/dashboard/?id=nlog.extensions.logging&branch=master)
4 | [](https://sonarcloud.io/dashboard/?id=nlog.extensions.logging&branch=master)
5 | [](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