diff --git a/.travis.yml b/.travis.yml
index 9d00bbe4..02fac129 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,10 +18,13 @@ install:
# Restore dependencies
- dotnet restore src/NLog.Extensions.Logging
- dotnet restore src/NLog.Extensions.Hosting
+ - dotnet restore src/NLog.Extensions.Configuration
- dotnet restore test/NLog.Extensions.Hosting.Tests
- dotnet restore test/NLog.Extensions.Logging.Tests
+ - dotnet restore test/NLog.Extensions.Configuration.Tests
script:
# Run tests
- dotnet test test/NLog.Extensions.Hosting.Tests --configuration Release --framework netcoreapp2.0
- dotnet test test/NLog.Extensions.Logging.Tests --configuration Release --framework netcoreapp2.0
+ - dotnet test test/NLog.Extensions.Configuration.Tests --configuration Release --framework netcoreapp2.0
diff --git a/NLog.Extensions.Logging.sln b/NLog.Extensions.Logging.sln
index 62e12c90..21a650a4 100644
--- a/NLog.Extensions.Logging.sln
+++ b/NLog.Extensions.Logging.sln
@@ -20,6 +20,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Extensions.Logging.Tes
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostingExample", "examples\NetCore2\HostingExample\HostingExample.csproj", "{07D358DF-D77A-434B-B034-95785DF7106F}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Extensions.Configuration", "src\NLog.Extensions.Configuration\NLog.Extensions.Configuration.csproj", "{AE82D026-CE85-48CC-BFFE-2D5C1556CC2B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Extensions.Configuration.Tests", "test\NLog.Extensions.Configuration.Tests\NLog.Extensions.Configuration.Tests.csproj", "{78A9081B-066B-4B34-BBD7-764D53CE4AA3}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -50,6 +54,14 @@ Global
{07D358DF-D77A-434B-B034-95785DF7106F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07D358DF-D77A-434B-B034-95785DF7106F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07D358DF-D77A-434B-B034-95785DF7106F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AE82D026-CE85-48CC-BFFE-2D5C1556CC2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AE82D026-CE85-48CC-BFFE-2D5C1556CC2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AE82D026-CE85-48CC-BFFE-2D5C1556CC2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AE82D026-CE85-48CC-BFFE-2D5C1556CC2B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {78A9081B-066B-4B34-BBD7-764D53CE4AA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {78A9081B-066B-4B34-BBD7-764D53CE4AA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {78A9081B-066B-4B34-BBD7-764D53CE4AA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {78A9081B-066B-4B34-BBD7-764D53CE4AA3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -61,6 +73,8 @@ Global
{0DC000BA-2DF8-48E5-A7BC-D76CB9D3FC61} = {FBD2E07B-F25B-4D2F-AEF6-6D1E10F1E523}
{DC42BF57-6316-4FCA-AD33-48FFDAFB4712} = {FBD2E07B-F25B-4D2F-AEF6-6D1E10F1E523}
{07D358DF-D77A-434B-B034-95785DF7106F} = {BD106966-02BE-4137-B9DC-4ECE56B4C204}
+ {AE82D026-CE85-48CC-BFFE-2D5C1556CC2B} = {C21FD102-21B1-46DB-AD62-86692558AD01}
+ {78A9081B-066B-4B34-BBD7-764D53CE4AA3} = {FBD2E07B-F25B-4D2F-AEF6-6D1E10F1E523}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {46DF0C22-7B6A-4A64-BC63-7B2F6A14F334}
diff --git a/appveyor.yml b/appveyor.yml
index ca90b067..0f48a4ab 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -23,8 +23,9 @@ artifacts:
test_script:
- nuget.exe install OpenCover -ExcludeVersion -DependencyVersion Ignore
- OpenCover\tools\OpenCover.Console.exe -register:user -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -f netcoreapp1.1 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"
- - OpenCover\tools\OpenCover.Console.exe -register:user -mergeoutput -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -f netcoreapp2.0 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"
+ - OpenCover\tools\OpenCover.Console.exe -register:user -mergeoutput -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -f netcoreapp2.0 NLog.Extensions.Logging.Tests" -filter:"+[NLog.Extensions.Logging]* -[NLog.Extensions.Logging.Tests]*" -output:"coverage.xml" -oldstyle -targetdir:"test"
- OpenCover\tools\OpenCover.Console.exe -register:user -mergeoutput -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -f netcoreapp2.0 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"
+ - OpenCover\tools\OpenCover.Console.exe -register:user -mergeoutput -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -f netcoreapp2.0 NLog.Extensions.Configuration.Tests" -filter:"+[NLog.Extensions.Logging]* +[NLog.Extensions.Hosting]* +[NLog.Extensions.Configuration]* -[NLog.Extensions.Logging.Tests]* -[NLog.Extensions.Hosting.Tests]* -[NLog.Extensions.Configuration.Tests]*" -output:"coverage.xml" -oldstyle -targetdir:"test"
- pip install codecov
- codecov -f "coverage.xml"
- ps: .\run-sonar.ps1
diff --git a/build.ps1 b/build.ps1
index 16b0a697..cbc00a74 100644
--- a/build.ps1
+++ b/build.ps1
@@ -17,6 +17,10 @@ dotnet restore .\src\NLog.Extensions.Hosting\
if (-Not $LastExitCode -eq 0)
{ exit $LastExitCode }
+dotnet restore .\src\NLog.Extensions.Configuration\
+if (-Not $LastExitCode -eq 0)
+ { exit $LastExitCode }
+
msbuild /t:Pack .\src\NLog.Extensions.Logging\ /p:targetFrameworks='"net451;net461;netstandard1.3;netstandard1.5;netstandard2.0"' /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal
if (-Not $LastExitCode -eq 0)
{ exit $LastExitCode }
@@ -25,4 +29,9 @@ msbuild /t:Pack .\src\NLog.Extensions.Hosting\ /p:targetFrameworks='"netstandard
if (-Not $LastExitCode -eq 0)
{ exit $LastExitCode }
+msbuild /t:Pack .\src\NLog.Extensions.Configuration\ /p:targetFrameworks='"net451;net461;netstandard1.3;netstandard1.5;netstandard2.0"' /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal
+if (-Not $LastExitCode -eq 0)
+ { exit $LastExitCode }
+
+
exit $LastExitCode
diff --git a/src/NLog.Extensions.Configuration/ConfigSettingLayoutRenderer.cs b/src/NLog.Extensions.Configuration/ConfigSettingLayoutRenderer.cs
new file mode 100644
index 00000000..5cf22a37
--- /dev/null
+++ b/src/NLog.Extensions.Configuration/ConfigSettingLayoutRenderer.cs
@@ -0,0 +1,184 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Extensions.Configuration;
+using NLog.Config;
+using NLog.Layouts;
+using NLog.LayoutRenderers;
+using System.Text;
+
+namespace NLog.Extensions.Configuration
+{
+ ///
+ /// Layout renderer that can lookup values from Microsoft Extension Configuration Container (json, xml, ini)
+ ///
+ /// Not to be confused with NLog.AppConfig that includes ${appsetting}
+ ///
+ /// Example: appsettings.json
+ /// {
+ /// "Mode":"Prod",
+ /// "Options":{
+ /// "StorageConnectionString":"UseDevelopmentStorage=true",
+ /// }
+ /// }
+ ///
+ /// Config Setting Lookup:
+ /// ${configsetting:name=Mode} = "Prod"
+ /// ${configsetting:name=Options.StorageConnectionString} = "UseDevelopmentStorage=true"
+ /// ${configsetting:name=Options.TableName:default=MyTable} = "MyTable"
+ ///
+ /// Config Setting Lookup Cached:
+ /// ${configsetting:cached=True:name=Mode}
+ ///
+ [LayoutRenderer("configsetting")]
+ public class ConfigSettingLayoutRenderer : LayoutRenderer
+ {
+ internal IConfigurationRoot _configurationRoot;
+
+ private static readonly Dictionary> _cachedConfigFiles = new Dictionary>();
+
+ ///
+ /// Global Configuration Container. Used if has default value
+ ///
+ public static IConfiguration DefaultConfiguration { get; set; }
+
+ ///
+ /// Name of the setting
+ ///
+ [RequiredParameter]
+ [DefaultParameter]
+ public string Name { get => _name; set => _name = value?.Replace(".", ":"); }
+ private string _name;
+
+ ///
+ /// The default value to render if the setting value is null.
+ ///
+ public string Default { get; set; }
+
+ ///
+ /// Configuration FileName (Multiple filenames can be split using '|' pipe-character)
+ ///
+ /// Relative paths are automatically prefixed with ${basedir}
+ public Layout FileName { get; set; } = DefaultFileName;
+ private const string DefaultFileName = "appsettings.json|appsettings.${environment:variable=ASPNETCORE_ENVIRONMENT}.json";
+
+ ///
+ protected override void InitializeLayoutRenderer()
+ {
+ _configurationRoot = null;
+ base.InitializeLayoutRenderer();
+ }
+
+ ///
+ protected override void CloseLayoutRenderer()
+ {
+ _configurationRoot = null;
+ base.CloseLayoutRenderer();
+ }
+
+ ///
+ protected override void Append(StringBuilder builder, LogEventInfo logEvent)
+ {
+ if (string.IsNullOrEmpty(_name))
+ return;
+
+ string value = null;
+ var configurationRoot = TryGetConfigurationRoot();
+ if (configurationRoot != null)
+ {
+ value = configurationRoot[_name];
+ }
+
+ builder.Append(value ?? Default);
+ }
+
+ private IConfiguration TryGetConfigurationRoot()
+ {
+ if (DefaultConfiguration != null)
+ {
+ var simpleLayout = FileName as SimpleLayout;
+ if (simpleLayout == null || string.IsNullOrEmpty(simpleLayout.Text) || ReferenceEquals(simpleLayout.Text, DefaultFileName))
+ {
+ if (_configurationRoot != null)
+ _configurationRoot = null;
+ return DefaultConfiguration;
+ }
+ }
+
+ if (_configurationRoot != null)
+ return _configurationRoot;
+
+ var fileNames = FileName?.Render(LogEventInfo.CreateNullEvent());
+ if (!string.IsNullOrEmpty(fileNames))
+ {
+ return _configurationRoot = LoadFileConfiguration(fileNames);
+ }
+
+ return null;
+ }
+
+ private IConfigurationRoot LoadFileConfiguration(string fileNames)
+ {
+ lock (_cachedConfigFiles)
+ {
+ if (_cachedConfigFiles.TryGetValue(fileNames, out var wearkConfigRoot) && wearkConfigRoot.TryGetTarget(out var configRoot))
+ {
+ return configRoot;
+ }
+ else
+ {
+ configRoot = BuildConfigurationRoot(fileNames);
+ _cachedConfigFiles[fileNames] = new WeakReference(configRoot);
+ return configRoot;
+ }
+ }
+ }
+
+ private static IConfigurationRoot BuildConfigurationRoot(string fileNames)
+ {
+ var configBuilder = new ConfigurationBuilder();
+ string baseDir = null;
+ foreach (var fileName in fileNames.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
+ {
+ var fullPath = fileName;
+ if (!System.IO.Path.IsPathRooted(fullPath))
+ {
+ if (baseDir == null)
+ baseDir = new BaseDirLayoutRenderer().Render(LogEventInfo.CreateNullEvent()) ?? string.Empty;
+ fullPath = System.IO.Path.Combine(baseDir, fileName);
+ }
+
+ AddFileConfiguration(configBuilder, fullPath);
+ }
+
+ return configBuilder.Build();
+ }
+
+ private static void AddFileConfiguration(ConfigurationBuilder configBuilder, string fullPath)
+ {
+ if (System.IO.File.Exists(fullPath))
+ {
+ // NOTE! Decided not to monitor for changes, as it would require access to dipose this monitoring again
+ if (string.Equals(System.IO.Path.GetExtension(fullPath), ".json", StringComparison.OrdinalIgnoreCase))
+ {
+ configBuilder.AddJsonFile(fullPath);
+ }
+ else if (string.Equals(System.IO.Path.GetExtension(fullPath), ".xml", StringComparison.OrdinalIgnoreCase))
+ {
+ configBuilder.AddXmlFile(fullPath);
+ }
+ else if (string.Equals(System.IO.Path.GetExtension(fullPath), ".ini", StringComparison.OrdinalIgnoreCase))
+ {
+ configBuilder.AddIniFile(fullPath);
+ }
+ else
+ {
+ Common.InternalLogger.Info("configSetting - Skipping FileName with unknown file-extension: {0}", fullPath);
+ }
+ }
+ else
+ {
+ Common.InternalLogger.Info("configSetting - Skipping FileName as file doesnt't exists: {0}", fullPath);
+ }
+ }
+ }
+}
diff --git a/src/NLog.Extensions.Configuration/NLog.Extensions.Configuration.csproj b/src/NLog.Extensions.Configuration/NLog.Extensions.Configuration.csproj
new file mode 100644
index 00000000..8828889d
--- /dev/null
+++ b/src/NLog.Extensions.Configuration/NLog.Extensions.Configuration.csproj
@@ -0,0 +1,89 @@
+
+
+
+ net451;net461;netstandard1.3;netstandard1.5;netstandard2.0
+ full
+ true
+ true
+ true
+
+ NLog.Extensions.Configuration v$(ProductVersion)
+ $(ProductVersion)
+
+ NLog
+ Julian Verdurmen;CoCo Lin
+ NLog extensions for Microsoft.Extensions.Configuration
+ https://github.com/NLog/NLog.Extensions.Logging
+ https://github.com/NLog/NLog.Extensions.Logging/blob/master/LICENSE
+ https://nlog-project.org/NConfig.png
+ https://github.com/NLog/NLog.Extensions.Logging.git
+ git
+ NLog;Microsoft.Extensions.Configuration;log;logfiles;netcore
+ 1.0: Initial release
+
+
+ {0A5EC30A-2DC6-4EB3-BF1E-2E82BA83220F}
+ true
+ 1.0.0.0
+ ..\NLog.snk
+ true
+
+
+
+ NLog.Extensions.Configuration for .NET Framework 4.6.1
+ true
+
+
+ NLog.Extensions.Configuration for .NET Framework 4.5.1
+ true
+
+
+ NLog.Extensions.Configuration for NetStandard 1.3
+
+
+ NLog.Extensions.Configuration for NetStandard 1.5
+
+
+ NLog.Extensions.v for NetStandard 2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/NLog.Extensions.Configuration/Properties/AssemblyInfo.cs b/src/NLog.Extensions.Configuration/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..3958956b
--- /dev/null
+++ b/src/NLog.Extensions.Configuration/Properties/AssemblyInfo.cs
@@ -0,0 +1,6 @@
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: ComVisible(false)]
+
+[assembly: InternalsVisibleTo("NLog.Extensions.Configuration.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100772391E63C104728ADCF18E2390474262559FA7F34A4215848F43288CDE875DCC92A06222E9BE0592B211FF74ADBB5D21A7AAB5522B540B1735F2F03279221056FEDBE7E534073DABEE9DB48F8ECEBCF1DC98A95576E45CBEFF5FE7C4842859451AB2DAE7A8370F1B2F7A529D2CA210E3E844D973523D73D193DF6C17F1314A6")]
\ No newline at end of file
diff --git a/test/NLog.Extensions.Configuration.Tests/ConfigSettingLayoutRendererTests.cs b/test/NLog.Extensions.Configuration.Tests/ConfigSettingLayoutRendererTests.cs
new file mode 100644
index 00000000..5639a139
--- /dev/null
+++ b/test/NLog.Extensions.Configuration.Tests/ConfigSettingLayoutRendererTests.cs
@@ -0,0 +1,62 @@
+using System.Collections.Generic;
+using Microsoft.Extensions.Configuration;
+using Xunit;
+
+namespace NLog.Extensions.Configuration.Tests
+{
+ public class ConfigSettingLayoutRendererTests
+ {
+ [Fact]
+ public void ConfigSettingSimpleLookup()
+ {
+ ConfigSettingLayoutRenderer.DefaultConfiguration = null;
+ var layoutRenderer = new ConfigSettingLayoutRenderer() { Name = "Mode" };
+ var result = layoutRenderer.Render(LogEventInfo.CreateNullEvent());
+ Assert.Equal("Prod", result);
+ }
+
+ [Fact]
+ public void ConfigSettingOptimizedLookup()
+ {
+ ConfigSettingLayoutRenderer.DefaultConfiguration = null;
+ var layoutRenderer1 = new ConfigSettingLayoutRenderer() { Name = "Mode" };
+ var result1 = layoutRenderer1.Render(LogEventInfo.CreateNullEvent());
+ Assert.Equal("Prod", result1);
+
+ var layoutRenderer2 = new ConfigSettingLayoutRenderer() { Name = "Options.SqlConnectionString" };
+ var result2 = layoutRenderer2.Render(LogEventInfo.CreateNullEvent());
+ Assert.Equal("YourProdStorageConnectionString", result2);
+
+ Assert.Same(layoutRenderer1._configurationRoot, layoutRenderer2._configurationRoot);
+ }
+
+ [Fact]
+ public void ConfigSettingFallbackDefaultLookup()
+ {
+ ConfigSettingLayoutRenderer.DefaultConfiguration = null;
+ var layoutRenderer = new ConfigSettingLayoutRenderer() { Name = "Options.TableName", Default = "MyTableName" };
+ var result = layoutRenderer.Render(LogEventInfo.CreateNullEvent());
+ Assert.Equal("MyTableName", result);
+ }
+
+ [Fact]
+ public void ConfigSettingCustomeFileNameLookup()
+ {
+ ConfigSettingLayoutRenderer.DefaultConfiguration = null;
+ var layoutRenderer = new ConfigSettingLayoutRenderer() { Name = "Mode", FileName = "appsettings.json" };
+ var result = layoutRenderer.Render(LogEventInfo.CreateNullEvent());
+ Assert.Equal("Prod", result);
+ }
+
+ [Fact]
+ public void ConfigSettingGlobalConfigLookup()
+ {
+ var memoryConfig = new Dictionary();
+ memoryConfig["Mode"] = "Test";
+ ConfigSettingLayoutRenderer.DefaultConfiguration = new ConfigurationBuilder().AddInMemoryCollection(memoryConfig).Build();
+ var layoutRenderer = new ConfigSettingLayoutRenderer() { Name = "Mode", FileName = "" };
+ var result = layoutRenderer.Render(LogEventInfo.CreateNullEvent());
+ Assert.Equal("Test", result);
+ }
+ }
+}
diff --git a/test/NLog.Extensions.Configuration.Tests/NLog.Extensions.Configuration.Tests.csproj b/test/NLog.Extensions.Configuration.Tests/NLog.Extensions.Configuration.Tests.csproj
new file mode 100644
index 00000000..74638da3
--- /dev/null
+++ b/test/NLog.Extensions.Configuration.Tests/NLog.Extensions.Configuration.Tests.csproj
@@ -0,0 +1,33 @@
+
+
+ PackageReference
+
+ netcoreapp2.0
+ Library
+ false
+ full
+ true
+
+ true
+ false
+ ..\..\src\NLog.snk
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ Always
+
+
+
\ No newline at end of file
diff --git a/test/NLog.Extensions.Configuration.Tests/Properties/AssemblyInfo.cs b/test/NLog.Extensions.Configuration.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..97f073b7
--- /dev/null
+++ b/test/NLog.Extensions.Configuration.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,14 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+[assembly: Xunit.CollectionBehavior(DisableTestParallelization = true)]
\ No newline at end of file
diff --git a/test/NLog.Extensions.Configuration.Tests/appsettings.json b/test/NLog.Extensions.Configuration.Tests/appsettings.json
new file mode 100644
index 00000000..944de05a
--- /dev/null
+++ b/test/NLog.Extensions.Configuration.Tests/appsettings.json
@@ -0,0 +1,7 @@
+{
+ "Mode": "Prod",
+ "Options": {
+ "SqlConnectionString": "YourProdStorageConnectionString",
+ "Language": "English"
+ }
+}
\ No newline at end of file