diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs
index 13a304550a..505bc0f140 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,11 +44,12 @@ namespace NLog.LayoutRenderers.Wrappers
///
[LayoutRenderer("whenEmpty")]
[AmbientProperty("WhenEmpty")]
- [AppDomainFixedOutput]
[ThreadAgnostic]
[ThreadSafe]
- public sealed class WhenEmptyLayoutRendererWrapper : WrapperLayoutRendererBuilderBase
+ public sealed class WhenEmptyLayoutRendererWrapper : WrapperLayoutRendererBuilderBase, IRawValue, IStringValueRenderer
{
+ private bool _skipStringValueRenderer;
+
///
/// Gets or sets the layout to be rendered when original layout produced empty result.
///
@@ -60,6 +62,7 @@ protected override void InitializeLayoutRenderer()
{
base.InitializeLayoutRenderer();
WhenEmpty?.Initialize(LoggingConfiguration);
+ _skipStringValueRenderer = !TryGetStringValue(out _, out _);
}
///
@@ -73,6 +76,66 @@ protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBui
WhenEmpty.RenderAppendBuilder(logEvent, builder);
}
+ string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent)
+ {
+ if (_skipStringValueRenderer)
+ {
+ return null;
+ }
+
+ 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;
+ }
+
+ private bool TryGetStringValue(out SimpleLayout innerLayout, out SimpleLayout whenEmptyLayout)
+ {
+ whenEmptyLayout = WhenEmpty as SimpleLayout;
+ innerLayout = Inner as SimpleLayout;
+
+ return IsStringLayout(innerLayout) && IsStringLayout(whenEmptyLayout);
+ }
+
+ private static bool IsStringLayout(SimpleLayout innerLayout)
+ {
+ return innerLayout != null && (innerLayout.IsFixedText || innerLayout.IsSimpleStringText);
+ }
+
+ 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..f29319c86f 100644
--- a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs
+++ b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs
@@ -31,8 +31,11 @@
// THE POSSIBILITY OF SUCH DAMAGE.
//
+using NLog.Internal;
+
namespace NLog.UnitTests.LayoutRenderers.Wrappers
{
+ using System;
using NLog;
using NLog.Layouts;
using Xunit;
@@ -73,5 +76,45 @@ 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);
+ }
+
+ }
+
+ [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