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 2f374fc2..fe252604 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; using NLog.Extensions.Logging; using NLog.Web; diff --git a/src/Shared/LayoutRenderers/AspNetLayoutRendererBase.cs b/src/Shared/LayoutRenderers/AspNetLayoutRendererBase.cs index 61ffff03..4eff1ceb 100644 --- a/src/Shared/LayoutRenderers/AspNetLayoutRendererBase.cs +++ b/src/Shared/LayoutRenderers/AspNetLayoutRendererBase.cs @@ -44,7 +44,7 @@ public IHttpContextAccessor HttpContextAccessor private static IHttpContextAccessor RetrieveHttpContextAccessor(Type _) => DefaultHttpContextAccessor; #else - private static IHttpContextAccessor RetrieveHttpContextAccessor(Type classType) + internal static IHttpContextAccessor RetrieveHttpContextAccessor(Type classType) { var serviceProvider = ServiceLocator.ServiceProvider; if (serviceProvider == null) @@ -113,16 +113,14 @@ protected override void CloseLayoutRenderer() #endif /// - /// Register a custom layout renderer with a callback function . The callback recieves the logEvent and the current configuration. + /// Register a custom layout renderer with a callback function . The callback receives the logEvent and the current configuration. /// /// Name of the layout renderer - without ${}. /// Callback that returns the value for the layout renderer. public static void Register(string name, Func func) { - // TODO Missing caching (and cache-reset) of HttpContextAccessor - Constant lookup in ServiceProvider can lead to deadlock situation - object NewFunc(LogEventInfo logEventInfo, LoggingConfiguration configuration) => func(logEventInfo, RetrieveHttpContextAccessor(null)?.HttpContext, configuration); - - Register(name, NewFunc); + var renderer = new NLogWebLayoutRenderer(name, func); + Register(renderer); } } } diff --git a/src/Shared/LayoutRenderers/NLogWebLayoutRenderer.cs b/src/Shared/LayoutRenderers/NLogWebLayoutRenderer.cs new file mode 100644 index 00000000..8a047f5a --- /dev/null +++ b/src/Shared/LayoutRenderers/NLogWebLayoutRenderer.cs @@ -0,0 +1,45 @@ +using System; +#if ASP_NET_CORE +using Microsoft.AspNetCore.Http; +using HttpContextBase = Microsoft.AspNetCore.Http.HttpContext; +#else +using System.Web; +#endif +using NLog.Config; +using NLog.LayoutRenderers; + +namespace NLog.Web.LayoutRenderers +{ + /// + /// Specialized layout render which has a cached + /// + internal class NLogWebLayoutRenderer : FuncLayoutRenderer + { + readonly Func _func; + +#if !ASP_NET_CORE + + private static IHttpContextAccessor HttpContextAccessor { get; } = AspNetLayoutRendererBase.DefaultHttpContextAccessor; +#else + + private IHttpContextAccessor _httpContextAccessor; + public IHttpContextAccessor HttpContextAccessor + { + get => _httpContextAccessor ?? (_httpContextAccessor = AspNetLayoutRendererBase.RetrieveHttpContextAccessor(GetType())); + set => _httpContextAccessor = value; + } +#endif + + public NLogWebLayoutRenderer(string name, Func func) : base(name) + { + _func = func; + } + + /// + protected override object RenderValue(LogEventInfo logEvent) + { + var httpContext = HttpContextAccessor?.HttpContext; + return _func(logEvent, httpContext, LoggingConfiguration); + } + } +} \ No newline at end of file diff --git a/tests/Shared/RegisterCustomLayoutRenderer.cs b/tests/Shared/RegisterCustomLayoutRenderer.cs index 9337c0d2..ebc3f608 100644 --- a/tests/Shared/RegisterCustomLayoutRenderer.cs +++ b/tests/Shared/RegisterCustomLayoutRenderer.cs @@ -1,10 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; - - using NLog.Layouts; #if ASP_NET_CORE using Microsoft.AspNetCore.Http; @@ -30,11 +25,11 @@ public class RegisterCustomLayoutRenderer : TestBase [Fact] public void RegisterLayoutRendererTest() { - var httpcontextMock = SetupHttpAccessorWithHttpContext(); + var httpContextMock = SetupHttpAccessorWithHttpContext(); #if ASP_NET_CORE - httpcontextMock.Connection.LocalPort.Returns(123); + httpContextMock.Connection.LocalPort.Returns(123); #else - httpcontextMock.Request.RawUrl.Returns("123"); + httpContextMock.Request.RawUrl.Returns("123"); #endif @@ -47,10 +42,10 @@ public void RegisterLayoutRendererTest() httpContext.Request.RawUrl); #endif Layout l = "${test-web}"; - var restult = l.Render(LogEventInfo.CreateNullEvent()); + var result = l.Render(LogEventInfo.CreateNullEvent()); // Assert - Assert.Equal("123", restult); + Assert.Equal("123", result); } private static @@ -68,17 +63,17 @@ private static #if ASP_NET_CORE var serviceProviderMock = Substitute.For(); serviceProviderMock.GetService(typeof(IHttpContextAccessor)).Returns(httpContextAccessorMock); - var httpcontext = Substitute.For(); + var httpContext = Substitute.For(); ServiceLocator.ServiceProvider = serviceProviderMock; #else - var httpcontext = Substitute.For(); - httpContextAccessorMock.HttpContext.Returns(httpcontext); + var httpContext = Substitute.For(); + httpContextAccessorMock.HttpContext.Returns(httpContext); AspNetLayoutRendererBase.DefaultHttpContextAccessor = httpContextAccessorMock; #endif - httpContextAccessorMock.HttpContext.Returns(httpcontext); - return httpcontext; + httpContextAccessorMock.HttpContext.Returns(httpContext); + return httpContext; } } }