From e0b1503faae95a6f318224e970324884bee3352f Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 21 Jun 2020 23:30:17 +0200 Subject: [PATCH] LogManager.Setup() - SetupBuilderExtension with LoadConfigurationFromAppSettings (#540) --- .../ASP.NET Core 2 - VS2017/Program.cs | 9 +- .../ASP.NET Core 3 - VS2019/Program.cs | 6 +- .../Config/SetupBuilderExtensions.cs | 103 +++++++++++++++++ .../SetupExtensionsBuilderExtensions.cs | 40 +++++++ .../AspNetCoreTests.cs | 104 ++++++++++++++++-- 5 files changed, 251 insertions(+), 11 deletions(-) create mode 100644 src/NLog.Web.AspNetCore/Config/SetupBuilderExtensions.cs create mode 100644 src/NLog.Web.AspNetCore/Config/SetupExtensionsBuilderExtensions.cs diff --git a/examples/ASP.NET Core 2/Visual Studio 2017/ASP.NET Core 2 - VS2017/Program.cs b/examples/ASP.NET Core 2/Visual Studio 2017/ASP.NET Core 2 - VS2017/Program.cs index b311a20f..1f1b4ec6 100644 --- a/examples/ASP.NET Core 2/Visual Studio 2017/ASP.NET Core 2 - VS2017/Program.cs +++ b/examples/ASP.NET Core 2/Visual Studio 2017/ASP.NET Core 2 - VS2017/Program.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using NLog.Extensions.Logging; using NLog.Web; namespace NLog.Web.AspNetCore2.Example @@ -11,7 +12,13 @@ public static class Program { public static void Main(string[] args) { - var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger(); + var config = new ConfigurationBuilder().Build(); + + var logger = LogManager.Setup() + .RegisterNLogWeb(config) + .LoadConfigurationFromFile("nlog.config") + .GetCurrentClassLogger(); + try { logger.Debug("init main"); diff --git a/examples/ASP.NET Core 3/ASP.NET Core 3 - VS2019/Program.cs b/examples/ASP.NET Core 3/ASP.NET Core 3 - VS2019/Program.cs index 01e13fb3..5059b799 100644 --- a/examples/ASP.NET Core 3/ASP.NET Core 3 - VS2019/Program.cs +++ b/examples/ASP.NET Core 3/ASP.NET Core 3 - VS2019/Program.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using NLog; using NLog.Web; namespace ASP.NET_Core_3___VS2019 @@ -14,7 +15,10 @@ public class Program { public static void Main(string[] args) { - var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger(); + var logger = LogManager.Setup() + .LoadConfigurationFromAppSettings() + .GetCurrentClassLogger(); + try { logger.Debug("init main"); diff --git a/src/NLog.Web.AspNetCore/Config/SetupBuilderExtensions.cs b/src/NLog.Web.AspNetCore/Config/SetupBuilderExtensions.cs new file mode 100644 index 00000000..09c6f6bd --- /dev/null +++ b/src/NLog.Web.AspNetCore/Config/SetupBuilderExtensions.cs @@ -0,0 +1,103 @@ +using System; +#if !ASP_NET_CORE1 && !ASP_NET_CORE2 +using System.IO; +using System.Linq; +#endif +using Microsoft.Extensions.Configuration; +using NLog.Config; +using NLog.Extensions.Logging; + +namespace NLog.Web +{ + /// + /// Extension methods to setup LogFactory options + /// + public static class SetupBuilderExtensions + { +#if !ASP_NET_CORE1 && !ASP_NET_CORE2 + /// + /// Loads NLog LoggingConfiguration from appsettings.json from the NLog-section + /// + public static ISetupBuilder LoadConfigurationFromAppSettings(this ISetupBuilder setupBuilder, string basePath = null, string environment = null, string nlogConfigSection = "NLog", bool optional = true, bool reloadOnChange = false) + { + environment = environment ?? GetAspNetCoreEnvironment("DOTNET_ENVIRONMENT") ?? GetAspNetCoreEnvironment("ASPNETCORE_ENVIRONMENT") ?? "Production"; + + var builder = new ConfigurationBuilder() + // Host Configuration + .SetBasePath(basePath ?? Directory.GetCurrentDirectory()) + .AddEnvironmentVariables(prefix: "ASPNETCORE_") + .AddEnvironmentVariables(prefix: "DOTNET_") + // App Configuration + .AddJsonFile("appsettings.json", optional, reloadOnChange) + .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: reloadOnChange) + .AddEnvironmentVariables(); + + var config = builder.Build(); + if (!string.IsNullOrEmpty(nlogConfigSection) && config.GetSection(nlogConfigSection)?.GetChildren().Any() == true) + { + return setupBuilder.SetupExtensions(e => e.RegisterNLogWeb()).LoadConfigurationFromSection(config, nlogConfigSection); + } + else + { + setupBuilder.SetupExtensions(e => e.RegisterNLogWeb().RegisterConfigSettings(config)); + + if (!string.IsNullOrEmpty(basePath)) + { + if (!string.IsNullOrEmpty(environment)) + { + setupBuilder.LoadConfigurationFromFile(Path.Combine(basePath, $"nlog.{environment}.config"), optional: true); + } + + setupBuilder.LoadConfigurationFromFile(Path.Combine(basePath, "nlog.config"), optional: true); + } + else if (!string.IsNullOrEmpty(environment)) + { + setupBuilder.LoadConfigurationFromFile($"nlog.{environment}.config", optional: true); + } + + return setupBuilder.LoadConfigurationFromFile(); // No effect, if config already loaded + } + } + + private static string GetAspNetCoreEnvironment(string variableName) + { + try + { + var environment = Environment.GetEnvironmentVariable(variableName); + if (string.IsNullOrWhiteSpace(environment)) + return null; + + return environment.Trim(); + } + catch (Exception ex) + { + NLog.Common.InternalLogger.Error(ex, "Failed to lookup environment variable {0}", variableName); + return null; + } + } +#endif + + /// + /// Convience method to register aspnet-layoutrenders in NLog.Web as one-liner before loading NLog.config + /// + /// + /// If not providing , then output from aspnet-layoutrenderers will remain empty + /// + public static ISetupBuilder RegisterNLogWeb(this ISetupBuilder setupBuilder, IConfiguration configuration = null, IServiceProvider serviceProvider = null) + { + setupBuilder.SetupExtensions(e => e.RegisterNLogWeb(serviceProvider)); + + if (configuration == null && serviceProvider != null) + { + configuration = serviceProvider.GetService(typeof(IConfiguration)) as IConfiguration; + } + + if (configuration != null) + { + setupBuilder.SetupExtensions(e => e.RegisterConfigSettings(configuration)); + } + + return setupBuilder; + } + } +} diff --git a/src/NLog.Web.AspNetCore/Config/SetupExtensionsBuilderExtensions.cs b/src/NLog.Web.AspNetCore/Config/SetupExtensionsBuilderExtensions.cs new file mode 100644 index 00000000..722f1ec3 --- /dev/null +++ b/src/NLog.Web.AspNetCore/Config/SetupExtensionsBuilderExtensions.cs @@ -0,0 +1,40 @@ +using System; +using System.Reflection; +using Microsoft.Extensions.Configuration; +using NLog.Config; +using NLog.Extensions.Logging; +using NLog.Web.DependencyInjection; + +namespace NLog.Web +{ + /// + /// Extension methods to setup NLog extensions, so they are known when loading NLog LoggingConfiguration + /// + public static class SetupExtensionsBuilderExtensions + { + /// + /// Replace with version from NLog.Extension.Logging when it has been released with NLog 4.7 + /// + internal static ISetupExtensionsBuilder RegisterConfigSettings(this ISetupExtensionsBuilder setupBuilder, IConfiguration configuration) + { + ConfigSettingLayoutRenderer.DefaultConfiguration = configuration; + return setupBuilder.RegisterLayoutRenderer("configsetting"); + } + + /// + /// Register the NLog.Web.AspNetCore LayoutRenderers + /// + /// + /// If not providing , then output from aspnet-layoutrenderers will remain empty + /// + public static ISetupExtensionsBuilder RegisterNLogWeb(this ISetupExtensionsBuilder setupBuilder, IServiceProvider serviceProvider = null) + { + if (serviceProvider != null) + { + ServiceLocator.ServiceProvider = serviceProvider; + } + + return setupBuilder.RegisterAssembly(typeof(NLogAspNetCoreOptions).GetTypeInfo().Assembly); + } + } +} diff --git a/tests/NLog.Web.AspNetCore.Tests/AspNetCoreTests.cs b/tests/NLog.Web.AspNetCore.Tests/AspNetCoreTests.cs index e0b85c19..935fc0d3 100644 --- a/tests/NLog.Web.AspNetCore.Tests/AspNetCoreTests.cs +++ b/tests/NLog.Web.AspNetCore.Tests/AspNetCoreTests.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using Castle.Core.Logging; using Xunit; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; @@ -16,7 +15,6 @@ using NLog.Config; using NLog.Layouts; using NLog.Targets; -using NLog.Web.Tests.LayoutRenderers; using ILoggerFactory = Microsoft.Extensions.Logging.ILoggerFactory; namespace NLog.Web.Tests @@ -44,7 +42,7 @@ public void UseNLogShouldLogTest() var configuration = CreateConfigWithMemoryTarget(out var target, "${logger}|${message}"); - LogManager.Configuration = configuration; + LogManager.Setup().RegisterNLogWeb(serviceProvider: webhost.Services).LoadConfiguration(configuration); var logger = loggerFact.CreateLogger("logger1"); @@ -54,9 +52,100 @@ public void UseNLogShouldLogTest() Assert.Single(logged); Assert.Equal("logger1|error1", logged.First()); + } + +#if !ASP_NET_CORE1 && !ASP_NET_CORE2 + [Fact] + public void LoadConfigurationFromAppSettingsShouldLogTest() + { + var tempPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), nameof(AspNetCoreTests), Guid.NewGuid().ToString()).Replace("\\", "/"); + var appSettings = System.IO.Path.Combine(tempPath, "appsettings.json"); + try + { + // Arrange + System.IO.Directory.CreateDirectory(tempPath); + System.IO.File.AppendAllText(appSettings, @"{ + ""basepath"": """ + tempPath + @""", + ""NLog"": { + ""throwConfigExceptions"": true, + ""targets"": { + ""logfile"": { + ""type"": ""File"", + ""fileName"": ""${configsetting:basepath}/hello.txt"", + ""layout"": ""${message}"" + } + }, + ""rules"": [ + { + ""logger"": ""*"", + ""minLevel"": ""Debug"", + ""writeTo"": ""logfile"" + } + ] + } + }"); + + // Act + var logFactory = new LogFactory(); + var logger = logFactory.Setup().LoadConfigurationFromAppSettings(basePath: tempPath).GetCurrentClassLogger(); + logger.Info("Hello World"); + + // Assert + var fileOutput = System.IO.File.ReadAllText(System.IO.Path.Combine(tempPath, "hello.txt")); + Assert.Contains("Hello World", fileOutput); + } + finally + { + if (System.IO.Directory.Exists(tempPath)) + { + System.IO.Directory.Delete(tempPath, true); + } + } } + [Fact] + public void LoadConfigurationFromAppSettingsShouldLogTest2() + { + var tempPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), nameof(AspNetCoreTests), Guid.NewGuid().ToString()).Replace("\\", "/"); + var appSettings = System.IO.Path.Combine(tempPath, "appsettings.json"); + + try + { + // Arrange + System.IO.Directory.CreateDirectory(tempPath); + System.IO.File.AppendAllText(appSettings, @"{ + ""basepath"": """ + tempPath + @""" + }"); + + System.IO.File.AppendAllText(System.IO.Path.Combine(tempPath, "nlog.config"), @" + + + + + + + "); + + // Act + var logFactory = new LogFactory(); + var logger = logFactory.Setup().LoadConfigurationFromAppSettings(basePath: tempPath).GetCurrentClassLogger(); + logger.Info("Hello World"); + + // Assert + var fileOutput = System.IO.File.ReadAllText(System.IO.Path.Combine(tempPath, "hello.txt")); + Assert.Contains("Hello World", fileOutput); + } + finally + { + if (System.IO.Directory.Exists(tempPath)) + { + System.IO.Directory.Delete(tempPath, true); + } + } + } +#endif + private static LoggingConfiguration CreateConfigWithMemoryTarget(out MemoryTarget target, Layout layout) { var configuration = new LoggingConfiguration(); @@ -66,7 +155,6 @@ private static LoggingConfiguration CreateConfigWithMemoryTarget(out MemoryTarge return configuration; } - [Fact] public void UseAspNetWithoutRegister() { @@ -79,7 +167,7 @@ public void UseAspNetWithoutRegister() var configuration = CreateConfigWithMemoryTarget(out var target, "${logger}|${message}|${aspnet-item:key1}"); - LogManager.Configuration = configuration; + LogManager.Setup().RegisterNLogWeb(serviceProvider: webhost.Services).LoadConfiguration(configuration); var httpContext = webhost.Services.GetService().HttpContext = new DefaultHttpContext(); httpContext.Items["key1"] = "value1"; @@ -100,10 +188,8 @@ public void UseAspNetWithoutRegister() //clear so next time it's rebuild ConfigurationItemFactory.Default = null; } - } - [Fact] public void RegisterHttpContext() { @@ -111,7 +197,7 @@ public void RegisterHttpContext() Assert.NotNull(webhost.Services.GetService()); } -#if ASP_NET_CORE2 +#if !ASP_NET_CORE1 [Fact] public void SkipRegisterHttpContext() { @@ -126,7 +212,7 @@ public void SkipRegisterHttpContext() /// private static IWebHost CreateWebHost(NLogAspNetCoreOptions options = null) { -#if ASP_NET_CORE2 || ASP_NET_CORE3 +#if !ASP_NET_CORE1 var webhost = Microsoft.AspNetCore.WebHost.CreateDefaultBuilder() .Configure(c => c.New()) //.New needed, otherwise: