diff --git a/src/NLog/LayoutRenderers/Log4JXmlEventLayoutRenderer.cs b/src/NLog/LayoutRenderers/Log4JXmlEventLayoutRenderer.cs index 03b74e205f..efae684bb5 100644 --- a/src/NLog/LayoutRenderers/Log4JXmlEventLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/Log4JXmlEventLayoutRenderer.cs @@ -370,15 +370,20 @@ private static void AppendException(LogEventInfo logEvent, XmlWriter xtw) private void AppendParameters(LogEventInfo logEvent, XmlWriter xtw) { - if (Parameters.Count > 0) + for (int i = 0; i < Parameters?.Count; ++i) { - foreach (NLogViewerParameterInfo parameter in Parameters) - { - xtw.WriteStartElement("log4j", "data", dummyNamespace); - xtw.WriteAttributeSafeString("name", parameter.Name); - xtw.WriteAttributeSafeString("value", parameter.Layout.Render(logEvent)); - xtw.WriteEndElement(); - } + var parameter = Parameters[i]; + if (string.IsNullOrEmpty(parameter?.Name)) + continue; + + var parameterValue = parameter.Layout?.Render(logEvent) ?? string.Empty; + if (!parameter.IncludeEmptyValue && string.IsNullOrEmpty(parameterValue)) + continue; + + xtw.WriteStartElement("log4j", "data", dummyNamespace); + xtw.WriteAttributeSafeString("name", parameter.Name); + xtw.WriteAttributeSafeString("value", parameterValue); + xtw.WriteEndElement(); } } diff --git a/src/NLog/Layouts/JsonAttribute.cs b/src/NLog/Layouts/JsonAttribute.cs index 44f7d0d5ac..599587eef4 100644 --- a/src/NLog/Layouts/JsonAttribute.cs +++ b/src/NLog/Layouts/JsonAttribute.cs @@ -41,6 +41,7 @@ namespace NLog.Layouts [NLogConfigurationItem] [ThreadAgnostic] [ThreadSafe] + [AppDomainFixedOutput] public class JsonAttribute { /// diff --git a/src/NLog/Layouts/Log4JXmlEventLayout.cs b/src/NLog/Layouts/Log4JXmlEventLayout.cs index f5a13d9644..83c06b47e0 100644 --- a/src/NLog/Layouts/Log4JXmlEventLayout.cs +++ b/src/NLog/Layouts/Log4JXmlEventLayout.cs @@ -33,9 +33,11 @@ namespace NLog.Layouts { + using System.Collections.Generic; using System.Text; using NLog.Config; using NLog.LayoutRenderers; + using NLog.Targets; /// /// A specialized layout that renders Log4j-compatible XML events. @@ -55,12 +57,22 @@ public class Log4JXmlEventLayout : Layout, IIncludeContext public Log4JXmlEventLayout() { Renderer = new Log4JXmlEventLayoutRenderer(); + Parameters = new List(); + Renderer.Parameters = Parameters; } /// /// Gets the instance that renders log events. /// - public Log4JXmlEventLayoutRenderer Renderer { get; private set; } + public Log4JXmlEventLayoutRenderer Renderer { get; } + + /// + /// Gets the collection of parameters. Each parameter contains a mapping + /// between NLog layout and a named parameter. + /// + /// + [ArrayParameter(typeof(NLogViewerParameterInfo), "parameter")] + public IList Parameters { get => Renderer.Parameters; set => Renderer.Parameters = value; } /// /// Gets or sets a value indicating whether to include contents of the dictionary. diff --git a/src/NLog/Targets/NLogViewerParameterInfo.cs b/src/NLog/Targets/NLogViewerParameterInfo.cs index 1fa7e52ec3..d4a5fe1335 100644 --- a/src/NLog/Targets/NLogViewerParameterInfo.cs +++ b/src/NLog/Targets/NLogViewerParameterInfo.cs @@ -40,6 +40,9 @@ namespace NLog.Targets /// Represents a parameter to a NLogViewer target. /// [NLogConfigurationItem] + [ThreadAgnostic] + [ThreadSafe] + [AppDomainFixedOutput] public class NLogViewerParameterInfo { /// @@ -62,5 +65,11 @@ public NLogViewerParameterInfo() /// [RequiredParameter] public Layout Layout { get; set; } + + /// + /// Gets or sets whether an attribute with empty value should be included in the output + /// + /// + public bool IncludeEmptyValue { get; set; } = true; } } diff --git a/tests/NLog.UnitTests/LayoutRenderers/Log4JXmlTests.cs b/tests/NLog.UnitTests/LayoutRenderers/Log4JXmlTests.cs index 6f2752b4dc..6038966887 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/Log4JXmlTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/Log4JXmlTests.cs @@ -31,19 +31,20 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System.Collections.Generic; -using NLog.Config; - namespace NLog.UnitTests.LayoutRenderers { - using System.Threading; - using System.Diagnostics; using System; - using System.Xml; - using System.Reflection; + using System.Collections.Generic; + using System.Diagnostics; using System.IO; - using Xunit; + using System.Reflection; + using System.Threading; + using System.Xml; + using NLog.Config; using NLog.Internal; + using NLog.Layouts; + using NLog.Targets; + using Xunit; public class Log4JXmlTests : NLogTestBase { @@ -53,8 +54,8 @@ public void Log4JXmlTest() LogManager.Configuration = XmlLoggingConfiguration.CreateFromXmlString(@" - - + + @@ -235,6 +236,35 @@ public void Log4JXmlTest() } } + [Fact] + void Log4JXmlEventLayoutParameterTest() + { + var log4jLayout = new Log4JXmlEventLayout() + { + Parameters = + { + new NLogViewerParameterInfo + { + Name = "mt", + Layout = "${message:raw=true}", + } + }, + }; + log4jLayout.Renderer.AppInfo = "MyApp"; + var logEventInfo = new LogEventInfo + { + LoggerName = "MyLOgger", + TimeStamp = new DateTime(2010, 01, 01, 12, 34, 56, DateTimeKind.Utc), + Level = LogLevel.Info, + Message = "hello, {0}", + Parameters = new[] { "world" }, + }; + + var threadid = Environment.CurrentManagedThreadId; + var machinename = Environment.MachineName; + Assert.Equal($"hello, world", log4jLayout.Render(logEventInfo)); + } + [Fact] void BadXmlValueTest() {