Skip to content

Commit

Permalink
${whenEmpty} implemented IRawValue and IStringValueRenderer (#3398)
Browse files Browse the repository at this point in the history
* WhenEmptyLayoutRendererWrapper with IRawValue and IStringValueRenderer

* refactor

* added test
  • Loading branch information
snakefoot authored and 304NotModified committed May 28, 2019
1 parent 2b17e0e commit c1528db
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 2 deletions.
Expand Up @@ -36,18 +36,20 @@ namespace NLog.LayoutRenderers.Wrappers
using System;
using System.Text;
using NLog.Config;
using NLog.Internal;
using NLog.Layouts;

/// <summary>
/// Outputs alternative layout when the inner layout produces empty result.
/// </summary>
[LayoutRenderer("whenEmpty")]
[AmbientProperty("WhenEmpty")]
[AppDomainFixedOutput]
[ThreadAgnostic]
[ThreadSafe]
public sealed class WhenEmptyLayoutRendererWrapper : WrapperLayoutRendererBuilderBase
public sealed class WhenEmptyLayoutRendererWrapper : WrapperLayoutRendererBuilderBase, IRawValue, IStringValueRenderer
{
private bool _skipStringValueRenderer;

/// <summary>
/// Gets or sets the layout to be rendered when original layout produced empty result.
/// </summary>
Expand All @@ -60,6 +62,7 @@ protected override void InitializeLayoutRenderer()
{
base.InitializeLayoutRenderer();
WhenEmpty?.Initialize(LoggingConfiguration);
_skipStringValueRenderer = !TryGetStringValue(out _, out _);
}

/// <inheritdoc/>
Expand All @@ -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);
}

/// <inheritdoc/>
[Obsolete("Inherit from WrapperLayoutRendererBase and override RenderInnerAndTransform() instead. Marked obsolete in NLog 4.6")]
protected override void TransformFormattedMesssage(StringBuilder target)
Expand Down
43 changes: 43 additions & 0 deletions tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs
Expand Up @@ -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;
Expand Down Expand Up @@ -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);


}
}
}

0 comments on commit c1528db

Please sign in to comment.