diff --git a/src/Shared/Internal/HttpContextExtensions.cs b/src/Shared/Internal/HttpContextExtensions.cs index 1d2b95ff..5fa13270 100644 --- a/src/Shared/Internal/HttpContextExtensions.cs +++ b/src/Shared/Internal/HttpContextExtensions.cs @@ -28,6 +28,22 @@ internal static HttpRequestBase TryGetRequest(this HttpContextBase context) return null; } } + + internal static HttpResponseBase TryGetResponse(this HttpContextBase context) + { + try + { + var response = context?.Response; + if (response == null) + InternalLogger.Debug("HttpContext Response Lookup returned null"); + return response; + } + catch (HttpException ex) + { + InternalLogger.Debug(ex, "HttpContext Response Lookup failed."); + return null; + } + } #else internal static HttpRequest TryGetRequest(this HttpContext context) { @@ -36,6 +52,14 @@ internal static HttpRequest TryGetRequest(this HttpContext context) InternalLogger.Debug("HttpContext Request Lookup returned null"); return request; } + + internal static HttpResponse TryGetResponse(this HttpContext context) + { + var response = context?.Response; + if (response == null) + InternalLogger.Debug("HttpContext Response Lookup returned null"); + return response; + } #endif #if ASP_NET_CORE diff --git a/src/Shared/LayoutRenderers/AspNetResponseStatusCodeRenderer.cs b/src/Shared/LayoutRenderers/AspNetResponseStatusCodeRenderer.cs new file mode 100644 index 00000000..8a12f25a --- /dev/null +++ b/src/Shared/LayoutRenderers/AspNetResponseStatusCodeRenderer.cs @@ -0,0 +1,42 @@ +using System.Text; +using NLog.Config; +using NLog.LayoutRenderers; +using NLog.Web.Internal; + +namespace NLog.Web.LayoutRenderers +{ + /// + /// ASP.NET Response Status Code. + /// + /// Example usage of ${aspnet-response-statuscode}: + /// + /// + /// ${aspnet-response-statuscode} - Produces - 200. + /// + /// + [LayoutRenderer("aspnet-response-statuscode")] + [ThreadSafe] + public class AspNetResponseStatusCodeRenderer : AspNetLayoutRendererBase + { + /// + /// ASP.NET Http Response Status Code + /// + /// The to append the rendered data to. + /// Logging event. + protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent) + { + var httpResponse = HttpContextAccessor.HttpContext.TryGetResponse(); + if (httpResponse == null) + { + return; + } + + int statusCode = httpResponse.StatusCode; + + if (statusCode >= 100 && statusCode <= 599) + { + builder.Append(statusCode); + } + } + } +} diff --git a/tests/Shared/LayoutRenderers/AspNetResponseStatusCodeRendererTests.cs b/tests/Shared/LayoutRenderers/AspNetResponseStatusCodeRendererTests.cs new file mode 100644 index 00000000..9d6e0360 --- /dev/null +++ b/tests/Shared/LayoutRenderers/AspNetResponseStatusCodeRendererTests.cs @@ -0,0 +1,49 @@ +using NLog.Web.LayoutRenderers; +using NSubstitute; +using Xunit; + +namespace NLog.Web.Tests.LayoutRenderers +{ + public class AspNetResponseStatusCodeRendererTests : LayoutRenderersTestBase + { + [Fact] + public void StatusCode_Set_Renderer() + { + // Arrange + var (renderer, httpContext) = CreateWithHttpContext(); + httpContext.Response.StatusCode.Returns(200); + + // Act + string result = renderer.Render(new LogEventInfo()); + + // Assert + Assert.Equal("200", result); + } + + [Theory] + [InlineData(0, false)] + [InlineData(99, false)] + [InlineData(100, true)] + [InlineData(599, true)] + [InlineData(600, false)] + public void Only_Render_Valid_StatusCodes(int statusCode, bool shouldBeRendered) + { + // Arrange + var (renderer, httpContext) = CreateWithHttpContext(); + httpContext.Response.StatusCode.Returns(statusCode); + + // Act + string result = renderer.Render(new LogEventInfo()); + + // Assert + if (shouldBeRendered) + { + Assert.Equal($"{statusCode}", result); + } + else + { + Assert.Empty(result); + } + } + } +}