diff --git a/src/Shared/LayoutRenderers/AspNetResponseStatusCodeRenderer.cs b/src/Shared/LayoutRenderers/AspNetResponseStatusCodeRenderer.cs index d5efa23e..e2e75b4d 100644 --- a/src/Shared/LayoutRenderers/AspNetResponseStatusCodeRenderer.cs +++ b/src/Shared/LayoutRenderers/AspNetResponseStatusCodeRenderer.cs @@ -1,4 +1,6 @@ -using System.Text; +using System; +using System.Net; +using System.Text; using NLog.Config; using NLog.LayoutRenderers; using NLog.Web.Internal; @@ -9,12 +11,31 @@ namespace NLog.Web.LayoutRenderers /// ASP.NET Response Status Code. /// /// - /// ${aspnet-response-statuscode} + /// ${aspnet-response-statuscode} emits the http status code as integer + /// ${aspnet-response-statuscode:Format=D} emits the http status code as integer + /// ${aspnet-response-statuscode:Format=F} emits the http status code as enum-string-value + /// ${aspnet-response-statuscode:Format=G} emits the http status code as enum-string-value + /// ${aspnet-response-statuscode:Format=X} emits the http status code as hexadecimal /// /// Documentation on NLog Wiki [LayoutRenderer("aspnet-response-statuscode")] public class AspNetResponseStatusCodeRenderer : AspNetLayoutRendererBase { + /// + /// A valid enumeration format string, defaults to integer format + /// + /// + /// Supported Values, Case Insensitive + /// D: outputs the HttpStatusCode enum as a integer + /// F: outputs the HttpStatusCode enum as a string if possible, otherwise an integer + /// G: outputs the HttpStatusCode enum as a string if possible, otherwise an integer + /// X: outputs the HttpStatusCode enum as a hexadecimal + /// + /// Documentation on Enum Format Strings + + [DefaultParameter] + public string Format { get; set; } = "d"; + /// protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent) { @@ -24,11 +45,22 @@ protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent) return; } - int statusCode = httpResponse.StatusCode; + var statusCode = httpResponse.StatusCode; + if (statusCode < 100 || statusCode > 599) + { + // Only output valid HTTP status codes + return; + } - if (statusCode >= 100 && statusCode <= 599) + try + { + builder.Append(((HttpStatusCode)statusCode).ToString(Format)); + } + catch (Exception ex) { - builder.Append(statusCode); + NLog.Common.InternalLogger.Error(ex, + "Error occurred outputting HttpStatusCode enum ToString() with format specifier of {0} and value of {1}", + Format, statusCode); } } } diff --git a/tests/Shared/LayoutRenderers/AspNetResponseStatusCodeRendererTests.cs b/tests/Shared/LayoutRenderers/AspNetResponseStatusCodeRendererTests.cs index 9d6e0360..3a7463a9 100644 --- a/tests/Shared/LayoutRenderers/AspNetResponseStatusCodeRendererTests.cs +++ b/tests/Shared/LayoutRenderers/AspNetResponseStatusCodeRendererTests.cs @@ -1,4 +1,5 @@ -using NLog.Web.LayoutRenderers; +using System.Net; +using NLog.Web.LayoutRenderers; using NSubstitute; using Xunit; @@ -7,7 +8,7 @@ namespace NLog.Web.Tests.LayoutRenderers public class AspNetResponseStatusCodeRendererTests : LayoutRenderersTestBase { [Fact] - public void StatusCode_Set_Renderer() + public void StatusCode_Set_Renderer_DefaultFormat() { // Arrange var (renderer, httpContext) = CreateWithHttpContext(); @@ -20,13 +21,109 @@ public void StatusCode_Set_Renderer() Assert.Equal("200", result); } + [Fact] + public void StatusCode_Set_Renderer_EnumFormatF() + { + // Arrange + var (renderer, httpContext) = CreateWithHttpContext(); + renderer.Format = "f"; + + httpContext.Response.StatusCode.Returns(200); + + // Act + string result = renderer.Render(new LogEventInfo()); + + // Assert + Assert.Equal(((HttpStatusCode)200).ToString(), result); + } + + [Fact] + public void StatusCode_Set_Renderer_EnumFormatG() + { + // Arrange + var (renderer, httpContext) = CreateWithHttpContext(); + renderer.Format = "g"; + + httpContext.Response.StatusCode.Returns(200); + + // Act + string result = renderer.Render(new LogEventInfo()); + + // Assert + Assert.Equal(((HttpStatusCode)200).ToString(), result); + } + + [Fact] + public void StatusCode_Set_Renderer_IntegerFormat() + { + // Arrange + var (renderer, httpContext) = CreateWithHttpContext(); + renderer.Format = "d"; + + httpContext.Response.StatusCode.Returns(200); + + // Act + string result = renderer.Render(new LogEventInfo()); + + // Assert + Assert.Equal("200", result); + } + + [Fact] + public void StatusCode_Set_Renderer_HexFormat() + { + // Arrange + var (renderer, httpContext) = CreateWithHttpContext(); + renderer.Format = "x"; + + httpContext.Response.StatusCode.Returns(200); + + // Act + string result = renderer.Render(new LogEventInfo()); + + // Assert + Assert.Equal(((HttpStatusCode)200).ToString("x"), result); + } + + [Fact] + public void StatusCode_Set_Renderer_InvalidFormat() + { + // Arrange + var (renderer, httpContext) = CreateWithHttpContext(); + renderer.Format = "invalid"; + + httpContext.Response.StatusCode.Returns(200); + + // Act + string result = renderer.Render(new LogEventInfo()); + + // Assert + Assert.Equal("", result); + } + + [Fact] + public void StatusCode_Set_Renderer_UpperCaseIntegerFormat() + { + // Arrange + var (renderer, httpContext) = CreateWithHttpContext(); + renderer.Format = "D"; + + 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) + public void Only_Render_Valid_StatusCodes_DefaultFormat(int statusCode, bool shouldBeRendered) { // Arrange var (renderer, httpContext) = CreateWithHttpContext(); @@ -45,5 +142,141 @@ public void Only_Render_Valid_StatusCodes(int statusCode, bool shouldBeRendered) Assert.Empty(result); } } + + [Theory] + [InlineData(0, false)] + [InlineData(99, false)] + [InlineData(100, true)] + [InlineData(599, true)] + [InlineData(600, false)] + public void Only_Render_Valid_StatusCodes_EnumFormatF(int statusCode, bool shouldBeRendered) + { + // Arrange + var (renderer, httpContext) = CreateWithHttpContext(); + renderer.Format = "f"; + httpContext.Response.StatusCode.Returns(statusCode); + + // Act + string result = renderer.Render(new LogEventInfo()); + + // Assert + if (shouldBeRendered) + { + Assert.Equal(((HttpStatusCode)statusCode).ToString(), result); + } + else + { + Assert.Empty(result); + } + } + + + [Theory] + [InlineData(0, false)] + [InlineData(99, false)] + [InlineData(100, true)] + [InlineData(599, true)] + [InlineData(600, false)] + public void Only_Render_Valid_StatusCodes_EnumFormatG(int statusCode, bool shouldBeRendered) + { + // Arrange + var (renderer, httpContext) = CreateWithHttpContext(); + renderer.Format = "g"; + httpContext.Response.StatusCode.Returns(statusCode); + + // Act + string result = renderer.Render(new LogEventInfo()); + + // Assert + if (shouldBeRendered) + { + Assert.Equal(((HttpStatusCode)statusCode).ToString(), result); + } + else + { + Assert.Empty(result); + } + } + + [Theory] + [InlineData(0, false)] + [InlineData(99, false)] + [InlineData(100, true)] + [InlineData(599, true)] + [InlineData(600, false)] + public void Only_Render_Valid_StatusCodes_IntegerFormat(int statusCode, bool shouldBeRendered) + { + // Arrange + var (renderer, httpContext) = CreateWithHttpContext(); + renderer.Format = "d"; + httpContext.Response.StatusCode.Returns(statusCode); + + // Act + string result = renderer.Render(new LogEventInfo()); + + // Assert + if (shouldBeRendered) + { + Assert.Equal($"{statusCode}", result); + } + else + { + Assert.Empty(result); + } + } + + [Theory] + [InlineData(0, false)] + [InlineData(99, false)] + [InlineData(100, true)] + [InlineData(599, true)] + [InlineData(600, false)] + public void Only_Render_Valid_StatusCodes_UpperCaseIntegerFormat(int statusCode, bool shouldBeRendered) + { + // Arrange + var (renderer, httpContext) = CreateWithHttpContext(); + renderer.Format = "D"; + httpContext.Response.StatusCode.Returns(statusCode); + + // Act + string result = renderer.Render(new LogEventInfo()); + + // Assert + if (shouldBeRendered) + { + Assert.Equal($"{statusCode}", result); + } + else + { + Assert.Empty(result); + } + } + + [Theory] + [InlineData(0, false)] + [InlineData(99, false)] + [InlineData(100, true)] + [InlineData(599, true)] + [InlineData(600, false)] + public void Only_Render_Valid_StatusCodes_HexFormat(int statusCode, bool shouldBeRendered) + { + // Arrange + var (renderer, httpContext) = CreateWithHttpContext(); + renderer.Format = "x"; + httpContext.Response.StatusCode.Returns(statusCode); + + // Act + string result = renderer.Render(new LogEventInfo()); + + // Assert + if (shouldBeRendered) + { + Assert.Equal(((HttpStatusCode)statusCode).ToString("x"), result); + } + else + { + Assert.Empty(result); + } + } } }