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

Make it possible to register a custom FuncLayoutRenderer #4326

Merged
merged 1 commit into from
Feb 27, 2021
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions src/NLog/LayoutRenderers/FuncLayoutRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,25 @@ namespace NLog.LayoutRenderers
/// </summary>
public class FuncLayoutRenderer : LayoutRenderer, IStringValueRenderer
{
private readonly Func<LogEventInfo, LoggingConfiguration, object> _renderMethod;

/// <summary>
/// Initializes a new instance of the <see cref="FuncLayoutRenderer"/> class.
/// </summary>
/// <param name="layoutRendererName">Name without ${}.</param>
protected FuncLayoutRenderer(string layoutRendererName)
{
LayoutRendererName = layoutRendererName;
}

/// <summary>
/// Initializes a new instance of the <see cref="FuncLayoutRenderer"/> class.
/// </summary>
/// <param name="layoutRendererName">Name without ${}.</param>
/// <param name="renderMethod">Method that renders the layout.</param>
public FuncLayoutRenderer(string layoutRendererName, Func<LogEventInfo, LoggingConfiguration, object> renderMethod)
{
RenderMethod = renderMethod ?? throw new ArgumentNullException(nameof(renderMethod));
_renderMethod = renderMethod ?? throw new ArgumentNullException(nameof(renderMethod));
LayoutRendererName = layoutRendererName;
}

Expand All @@ -60,9 +71,11 @@ public FuncLayoutRenderer(string layoutRendererName, Func<LogEventInfo, LoggingC
public string LayoutRendererName { get; set; }

/// <summary>
/// Method that renders the layout.
/// Method that renders the layout.
///
/// This public property will be removed in NLog 5.
/// </summary>
public Func<LogEventInfo, LoggingConfiguration, object> RenderMethod { get; }
public Func<LogEventInfo, LoggingConfiguration, object> RenderMethod => _renderMethod;

/// <summary>
/// Format string for conversion from object to string.
Expand All @@ -73,7 +86,7 @@ public FuncLayoutRenderer(string layoutRendererName, Func<LogEventInfo, LoggingC
/// <inheritdoc />
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
var value = GetValue(logEvent);
var value = RenderValue(logEvent);
var formatProvider = GetFormatProvider(logEvent, null);
builder.AppendFormattedValue(value, Format, formatProvider);
}
Expand All @@ -85,16 +98,21 @@ private string GetStringValue(LogEventInfo logEvent)
{
if (Format != MessageTemplates.ValueFormatter.FormatAsJson)
{
object value = GetValue(logEvent);
object value = RenderValue(logEvent);
string stringValue = FormatHelper.TryFormatToString(value, Format, GetFormatProvider(logEvent, null));
return stringValue;
}
return null;
}

private object GetValue(LogEventInfo logEvent)
/// <summary>
/// Render the value for this log event
/// </summary>
/// <param name="logEvent">The event info.</param>
/// <returns>The value.</returns>
protected virtual object RenderValue(LogEventInfo logEvent)
{
return RenderMethod.Invoke(logEvent, LoggingConfiguration);
return _renderMethod?.Invoke(logEvent, LoggingConfiguration);
}
}
}
15 changes: 12 additions & 3 deletions src/NLog/LayoutRenderers/LayoutRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ protected CultureInfo GetCulture(LogEventInfo logEvent, CultureInfo layoutCultur
/// <typeparam name="T"> Type of the layout renderer.</typeparam>
/// <param name="name"> Name of the layout renderer - without ${}.</param>
public static void Register<T>(string name)
where T: LayoutRenderer
where T : LayoutRenderer
{
var layoutRendererType = typeof(T);
Register(name, layoutRendererType);
Expand Down Expand Up @@ -280,8 +280,17 @@ public static void Register(string name, Func<LogEventInfo, object> func)
public static void Register(string name, Func<LogEventInfo, LoggingConfiguration, object> func)
{
var layoutRenderer = new FuncLayoutRenderer(name, func);

ConfigurationItemFactory.Default.GetLayoutRenderers().RegisterFuncLayout(name, layoutRenderer);

Register(layoutRenderer);
}

/// <summary>
/// Register a custom layout renderer with a callback function <paramref name="layoutRenderer"/>. The callback receives the logEvent and the current configuration.
/// </summary>
/// <param name="layoutRenderer">Renderer with callback func</param>
public static void Register(FuncLayoutRenderer layoutRenderer)
{
ConfigurationItemFactory.Default.GetLayoutRenderers().RegisterFuncLayout(layoutRenderer.LayoutRendererName, layoutRenderer);
}
}
}
85 changes: 85 additions & 0 deletions tests/NLog.UnitTests/LayoutRenderers/FuncLayoutRendererTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//
// Copyright (c) 2004-2020 Jaroslaw Kowalski <[email protected]>, Kim Christensen, Julian Verdurmen
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of Jaroslaw Kowalski nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//

using NLog.LayoutRenderers;
using System;
using NLog.Config;
using Xunit;

namespace NLog.UnitTests.LayoutRenderers
{
public class FuncLayoutRendererTests : NLogTestBase
{
[Fact]
public void RegisterCustomFuncLayoutRendererTest()
{
// Arrange
var funcLayoutRenderer = new MyFuncLayoutRenderer("the-answer-new");

// Act
LayoutRenderer.Register(funcLayoutRenderer);

LogManager.Configuration = XmlLoggingConfiguration.CreateFromXmlString(@"<nlog throwExceptions='true'>
<targets>
<target name='debug' type='Debug' layout= 'TheAnswer=${the-answer-new:Format=D3}' /></targets>
<rules>
<logger name='*' minlevel='Debug' writeTo='debug' />
</rules>
</nlog>");

var logger = LogManager.GetCurrentClassLogger();
logger.Debug("test1");

// Assert
AssertDebugLastMessage("debug", "TheAnswer=042");
}

private class MyFuncLayoutRenderer : FuncLayoutRenderer
{
/// <inheritdoc />
public MyFuncLayoutRenderer(string layoutRendererName) : base(layoutRendererName)
{
}

#region Overrides of FuncLayoutRenderer

/// <inheritdoc />
protected override object RenderValue(LogEventInfo logEvent)
{
return 42;
}

#endregion
}
}
}