Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

${whenEmpty} implemented IRawValue and IStringValueRenderer #3398

Merged
merged 3 commits into from May 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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);


}
}
}