From a7abf3cd5109169f5203f3e3c2ebc71476d5b2cf Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 13 May 2019 20:14:35 +0200 Subject: [PATCH 1/3] WhenEmptyLayoutRendererWrapper with IRawValue and IStringValueRenderer --- .../WhenEmptyLayoutRendererWrapper.cs | 66 ++++++++++++++++++- .../Wrappers/WhenEmptyTests.cs | 22 +++++++ 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs index 13a304550a..da004bee68 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs @@ -36,6 +36,7 @@ namespace NLog.LayoutRenderers.Wrappers using System; using System.Text; using NLog.Config; + using NLog.Internal; using NLog.Layouts; /// @@ -43,10 +44,9 @@ namespace NLog.LayoutRenderers.Wrappers /// [LayoutRenderer("whenEmpty")] [AmbientProperty("WhenEmpty")] - [AppDomainFixedOutput] [ThreadAgnostic] [ThreadSafe] - public sealed class WhenEmptyLayoutRendererWrapper : WrapperLayoutRendererBuilderBase + public sealed class WhenEmptyLayoutRendererWrapper : WrapperLayoutRendererBuilderBase, IRawValue, IStringValueRenderer { /// /// Gets or sets the layout to be rendered when original layout produced empty result. @@ -55,11 +55,14 @@ public sealed class WhenEmptyLayoutRendererWrapper : WrapperLayoutRendererBuilde [RequiredParameter] public Layout WhenEmpty { get; set; } + private bool _skipStringValueRenderer; + /// protected override void InitializeLayoutRenderer() { base.InitializeLayoutRenderer(); WhenEmpty?.Initialize(LoggingConfiguration); + _skipStringValueRenderer = !TryGetStringValue(out var _, out var _); } /// @@ -73,6 +76,65 @@ protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBui WhenEmpty.RenderAppendBuilder(logEvent, builder); } + string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) + { + if (!_skipStringValueRenderer) + { + if (TryGetStringValue(out var innerLayout, out var whenEmptyLayout)) + { + var innerValue = innerLayout.Render(logEvent); + if (!string.IsNullOrEmpty(innerValue)) + { + return innerValue; + } + + // render WhenEmpty when the inner layout was empty + return whenEmptyLayout.Render(logEvent); + } + + _skipStringValueRenderer = true; + } + return null; + } + + bool TryGetStringValue(out SimpleLayout innerLayout, out SimpleLayout whenEmptyLayout) + { + innerLayout = Inner as SimpleLayout; + whenEmptyLayout = WhenEmpty as SimpleLayout; + + if (innerLayout == null || (!innerLayout.IsFixedText && !innerLayout.IsSimpleStringText)) + return false; + + if (whenEmptyLayout == null || (!whenEmptyLayout.IsFixedText && !whenEmptyLayout.IsSimpleStringText)) + return false; + + return true; + } + + bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) + { + if (Inner.TryGetRawValue(logEvent, out var innerValue)) + { + if (innerValue != null && !innerValue.Equals(string.Empty)) + { + value = innerValue; + return true; + } + } + else + { + var innerResult = Inner.Render(logEvent); // Beware this can be very expensive call! + if (!string.IsNullOrEmpty(innerResult)) + { + value = null; + return false; + } + } + + // render WhenEmpty when the inner layout was empty + return WhenEmpty.TryGetRawValue(logEvent, out value); + } + /// [Obsolete("Inherit from WrapperLayoutRendererBase and override RenderInnerAndTransform() instead. Marked obsolete in NLog 4.6")] protected override void TransformFormattedMesssage(StringBuilder target) diff --git a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs index 6521b42f8b..361a402156 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs @@ -33,6 +33,7 @@ namespace NLog.UnitTests.LayoutRenderers.Wrappers { + using System; using NLog; using NLog.Layouts; using Xunit; @@ -73,5 +74,26 @@ public void WhenEmpty_MissingInner_ShouldNotThrow() var le = LogEventInfo.Create(LogLevel.Info, "logger", "message"); Assert.Equal("api.log", l.Render(le)); } + + [Fact] + public void WhenDbNullRawValueShouldWork() + { + SimpleLayout l = @"${event-properties:prop1:whenEmpty=${db-null}}"; + { + var le = LogEventInfo.Create(LogLevel.Info, "logger", "message"); + le.Properties["prop1"] = 1; + var success = l.TryGetRawValue(le, out var rawValue); + Assert.True(success); + Assert.Equal(1, rawValue); + } + // empty log message + { + var le = LogEventInfo.Create(LogLevel.Info, "logger", "message"); + var success = l.TryGetRawValue(le, out var rawValue); + Assert.True(success); + Assert.Equal(DBNull.Value, rawValue); + } + + } } } \ No newline at end of file From 2686f4db7facb8588b6e60fd008c9e3ee49fd175 Mon Sep 17 00:00:00 2001 From: Julian Verdurmen <304NotModified@users.noreply.github.com> Date: Tue, 14 May 2019 20:51:20 +0200 Subject: [PATCH 2/3] refactor --- .../WhenEmptyLayoutRendererWrapper.cs | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs index da004bee68..505bc0f140 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs @@ -48,6 +48,8 @@ namespace NLog.LayoutRenderers.Wrappers [ThreadSafe] public sealed class WhenEmptyLayoutRendererWrapper : WrapperLayoutRendererBuilderBase, IRawValue, IStringValueRenderer { + private bool _skipStringValueRenderer; + /// /// Gets or sets the layout to be rendered when original layout produced empty result. /// @@ -55,14 +57,12 @@ public sealed class WhenEmptyLayoutRendererWrapper : WrapperLayoutRendererBuilde [RequiredParameter] public Layout WhenEmpty { get; set; } - private bool _skipStringValueRenderer; - /// protected override void InitializeLayoutRenderer() { base.InitializeLayoutRenderer(); WhenEmpty?.Initialize(LoggingConfiguration); - _skipStringValueRenderer = !TryGetStringValue(out var _, out var _); + _skipStringValueRenderer = !TryGetStringValue(out _, out _); } /// @@ -78,37 +78,38 @@ protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBui string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) { - if (!_skipStringValueRenderer) + if (_skipStringValueRenderer) + { + return null; + } + + if (TryGetStringValue(out var innerLayout, out var whenEmptyLayout)) { - if (TryGetStringValue(out var innerLayout, out var whenEmptyLayout)) + var innerValue = innerLayout.Render(logEvent); + if (!string.IsNullOrEmpty(innerValue)) { - var innerValue = innerLayout.Render(logEvent); - if (!string.IsNullOrEmpty(innerValue)) - { - return innerValue; - } - - // render WhenEmpty when the inner layout was empty - return whenEmptyLayout.Render(logEvent); + return innerValue; } - _skipStringValueRenderer = true; + // render WhenEmpty when the inner layout was empty + return whenEmptyLayout.Render(logEvent); } + + _skipStringValueRenderer = true; return null; } - bool TryGetStringValue(out SimpleLayout innerLayout, out SimpleLayout whenEmptyLayout) + private bool TryGetStringValue(out SimpleLayout innerLayout, out SimpleLayout whenEmptyLayout) { - innerLayout = Inner as SimpleLayout; whenEmptyLayout = WhenEmpty as SimpleLayout; + innerLayout = Inner as SimpleLayout; - if (innerLayout == null || (!innerLayout.IsFixedText && !innerLayout.IsSimpleStringText)) - return false; - - if (whenEmptyLayout == null || (!whenEmptyLayout.IsFixedText && !whenEmptyLayout.IsSimpleStringText)) - return false; + return IsStringLayout(innerLayout) && IsStringLayout(whenEmptyLayout); + } - return true; + private static bool IsStringLayout(SimpleLayout innerLayout) + { + return innerLayout != null && (innerLayout.IsFixedText || innerLayout.IsSimpleStringText); } bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) @@ -123,7 +124,7 @@ bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) } else { - var innerResult = Inner.Render(logEvent); // Beware this can be very expensive call! + var innerResult = Inner.Render(logEvent); // Beware this can be very expensive call if (!string.IsNullOrEmpty(innerResult)) { value = null; From 003fa2e691db4593cafd807441745f10a8ef8b2d Mon Sep 17 00:00:00 2001 From: Julian Verdurmen <304NotModified@users.noreply.github.com> Date: Tue, 14 May 2019 21:03:53 +0200 Subject: [PATCH 3/3] added test --- .../Wrappers/WhenEmptyTests.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs index 361a402156..f29319c86f 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +using NLog.Internal; + namespace NLog.UnitTests.LayoutRenderers.Wrappers { using System; @@ -95,5 +97,24 @@ public void WhenDbNullRawValueShouldWork() } } + + [Theory] + [InlineData("message", "message")] + [InlineData("", "default")] + public void GetStringValueShouldWork(string message, string expected) + { + // Arrange + SimpleLayout layout = @"${message:whenEmpty=default}"; + var stringValueRenderer = (IStringValueRenderer)layout.Renderers[0]; + var logEvent = LogEventInfo.Create(LogLevel.Info, "logger", message); + + // Act + var result = stringValueRenderer.GetFormattedString(logEvent); + + // Assert + Assert.Equal(expected, result); + + + } } } \ No newline at end of file