Skip to content

Commit

Permalink
NLog.Extensions.Configuration - ConfigSetting LayoutRenderer
Browse files Browse the repository at this point in the history
  • Loading branch information
snakefoot committed Sep 16, 2018
1 parent 6971370 commit 5427bbb
Show file tree
Hide file tree
Showing 8 changed files with 398 additions and 0 deletions.
14 changes: 14 additions & 0 deletions NLog.Extensions.Logging.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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}
Expand Down
173 changes: 173 additions & 0 deletions src/NLog.Extensions.Configuration/ConfigSettingLayoutRenderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
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
{
/// <summary>
/// Layout renderer that can lookup values from Microsoft Extension Configuration Container (json, xml, ini)
/// </summary>
/// <example>
/// 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}
/// </example>
[LayoutRenderer("configsetting")]
public class ConfigSettingLayoutRenderer : LayoutRenderer
{
internal IConfigurationRoot _configurationRoot;

private static readonly Dictionary<string, WeakReference<IConfigurationRoot>> _cachedConfigFiles = new Dictionary<string, WeakReference<IConfigurationRoot>>();

/// <summary>
/// Global Configuration Container. Used if <see cref="FileName" /> has default value
/// </summary>
public static IConfigurationRoot DefaultConfigurationRoot { get; set; }

///<summary>
/// Name of the setting
///</summary>
[RequiredParameter]
[DefaultParameter]
public string Name { get => _name; set => _name = value?.Replace(".", ":"); }
private string _name;

///<summary>
/// The default value to render if the setting value is null.
///</summary>
public string Default { get; set; }

/// <summary>
/// Configuration FileName (Multiple filenames can be split using '|' pipe-character)
/// </summary>
/// <remarks>Relative paths are automatically prefixed with ${basedir}</remarks>
public Layout FileName { get; set; } = DefaultFileName;
private const string DefaultFileName = "appsettings.json|appsettings.${environment:lowercase=true:variable=ASPNETCORE_ENVIRONMENT}.json";

/// <inheritdoc/>
protected override void InitializeLayoutRenderer()
{
_configurationRoot = null;
base.InitializeLayoutRenderer();
}

/// <inheritdoc/>
protected override void CloseLayoutRenderer()
{
_configurationRoot = null;
base.CloseLayoutRenderer();
}

/// <inheritdoc/>
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 (DefaultConfigurationRoot != null)
{
var simpleLayout = FileName as SimpleLayout;
if (simpleLayout == null || string.IsNullOrEmpty(simpleLayout.Text) || ReferenceEquals(simpleLayout.Text, DefaultFileName))
return DefaultConfigurationRoot;
}

if (_configurationRoot != null)
return _configurationRoot;

var fileNames = FileName?.Render(LogEventInfo.CreateNullEvent());
if (!string.IsNullOrEmpty(fileNames))
{
return LoadFileConfiguration(fileNames);
}

return null;
}

private IConfiguration LoadFileConfiguration(string fileNames)
{
lock (_cachedConfigFiles)
{
if (_cachedConfigFiles.TryGetValue(fileNames, out var wearkConfigRoot) && wearkConfigRoot.TryGetTarget(out var configRoot))
{
return _configurationRoot = configRoot;
}
else
{
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);
}

configRoot = configBuilder.Build();
_cachedConfigFiles[fileNames] = new WeakReference<IConfigurationRoot>(configRoot);
return _configurationRoot = configRoot;
}
}
}

private static void AddFileConfiguration(ConfigurationBuilder configBuilder, string fullPath)
{
if (System.IO.File.Exists(fullPath))
{
if (string.Equals(System.IO.Path.GetExtension(fullPath), ".json", StringComparison.OrdinalIgnoreCase))
{
configBuilder.AddJsonFile(fullPath, false, true);
}
else if (string.Equals(System.IO.Path.GetExtension(fullPath), ".xml", StringComparison.OrdinalIgnoreCase))
{
configBuilder.AddXmlFile(fullPath, false, true);
}
else if (string.Equals(System.IO.Path.GetExtension(fullPath), ".ini", StringComparison.OrdinalIgnoreCase))
{
configBuilder.AddIniFile(fullPath, false, true);
}
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);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net451;net461;netstandard1.3;netstandard1.5;netstandard2.0</TargetFrameworks>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<GenerateDocumentationFile>true</GenerateDocumentationFile>

<Product>NLog.Extensions.Configuration v$(ProductVersion)</Product>
<InformationalVersion>$(ProductVersion)</InformationalVersion>

<Company>NLog</Company>
<Authors>Julian Verdurmen;CoCo Lin</Authors>
<Description>NLog extensions for Microsoft.Extensions.Configuration</Description>
<PackageProjectUrl>https://github.com/NLog/NLog.Extensions.Logging</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/NLog/NLog.Extensions.Logging/blob/master/LICENSE</PackageLicenseUrl>
<PackageIconUrl>https://nlog-project.org/NConfig.png</PackageIconUrl>
<RepositoryUrl>https://github.com/NLog/NLog.Extensions.Logging.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>NLog;Microsoft.Extensions.Configuration;log;logfiles;netcore</PackageTags>
<PackageReleaseNotes>1.0: Initial release</PackageReleaseNotes>

<!-- SonarQube needs this -->
<ProjectGuid>{0A5EC30A-2DC6-4EB3-BF1E-2E82BA83220F}</ProjectGuid>
<SignAssembly>true</SignAssembly>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<AssemblyOriginatorKeyFile>..\NLog.snk</AssemblyOriginatorKeyFile>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup>

<PropertyGroup Condition=" '$(TargetFramework)' == 'net461' ">
<Title>NLog.Extensions.Configuration for .NET Framework 4.6.1</Title>
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net451' ">
<Title>NLog.Extensions.Configuration for .NET Framework 4.5.1</Title>
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<Title>NLog.Extensions.Configuration for NetStandard 1.3</Title>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.5' ">
<Title>NLog.Extensions.Configuration for NetStandard 1.5</Title>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<Title>NLog.Extensions.v for NetStandard 2.0</Title>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NLog" Version="4.5.9" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Xml" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.0" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Xml" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Xml" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.5' ">
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Xml" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Xml" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
</ItemGroup>
</Project>
6 changes: 6 additions & 0 deletions src/NLog.Extensions.Configuration/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

[assembly: ComVisible(false)]

[assembly: InternalsVisibleTo("NLog.Extensions.Configuration.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100772391E63C104728ADCF18E2390474262559FA7F34A4215848F43288CDE875DCC92A06222E9BE0592B211FF74ADBB5D21A7AAB5522B540B1735F2F03279221056FEDBE7E534073DABEE9DB48F8ECEBCF1DC98A95576E45CBEFF5FE7C4842859451AB2DAE7A8370F1B2F7A529D2CA210E3E844D973523D73D193DF6C17F1314A6")]
Original file line number Diff line number Diff line change
@@ -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.DefaultConfigurationRoot = null;
var layoutRenderer = new ConfigSettingLayoutRenderer() { Name = "Mode" };
var result = layoutRenderer.Render(LogEventInfo.CreateNullEvent());
Assert.Equal("Prod", result);
}

[Fact]
public void ConfigSettingOptimizedLookup()
{
ConfigSettingLayoutRenderer.DefaultConfigurationRoot = 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.DefaultConfigurationRoot = 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.DefaultConfigurationRoot = 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<string, string>();
memoryConfig["Mode"] = "Test";
ConfigSettingLayoutRenderer.DefaultConfigurationRoot = new ConfigurationBuilder().AddInMemoryCollection(memoryConfig).Build();
var layoutRenderer = new ConfigSettingLayoutRenderer() { Name = "Mode", FileName = "" };
var result = layoutRenderer.Render(LogEventInfo.CreateNullEvent());
Assert.Equal("Test", result);
}
}
}
Loading

0 comments on commit 5427bbb

Please sign in to comment.