Skip to content

Commit

Permalink
XmlLayout - Render LogEventInfo.Properties as XML (Support for single…
Browse files Browse the repository at this point in the history
…ton tag)
  • Loading branch information
snakefoot committed Apr 24, 2018
1 parent c8bdbff commit 8dae8cf
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 26 deletions.
68 changes: 47 additions & 21 deletions src/NLog/Layouts/XmlLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,12 @@ public class XmlLayout : Layout
/// <remarks>
/// Support string-format where {0} means property-key-name
///
/// Skips closing element tag when having configured <see cref="PropertiesFormatValueAttribute"/>
/// Skips closing element tag when having configured <see cref="PropertiesNodeValueAttribute"/>
/// </remarks>
/// <docgen category='LogEvent Properties XML Options' order='10' />
public string PropertiesFormatElementName { get; set; } = "property";
public string PropertiesNodeName { get { return _propertiesNodeName; } set { _propertiesNodeName = value; _propertiesNodeNameFormat = value?.IndexOf('{') >= 0; } }
private string _propertiesNodeName = "property";
private bool _propertiesNodeNameFormat;

/// <summary>
/// XML attribute format to use when rendering property-key
Expand All @@ -138,7 +140,7 @@ public class XmlLayout : Layout
/// Replaces newlines with underscore (_)
/// </remarks>
/// <docgen category='LogEvent Properties XML Options' order='10' />
public string PropertiesFormatKeyAttribute { get; set; } = "key=\"{0}\"";
public string PropertiesNodeKeyAttribute { get; set; } = "key=\"{0}\"";

/// <summary>
/// XML attribute format to use when rendering property-value
Expand All @@ -153,7 +155,7 @@ public class XmlLayout : Layout
/// Skips closing element tag when using attribute for value
/// </remarks>
/// <docgen category='LogEvent Properties XML Options' order='10' />
public string PropertiesFormatValueAttribute { get; set; }
public string PropertiesNodeValueAttribute { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="XmlLayout"/> class.
Expand Down Expand Up @@ -219,8 +221,9 @@ protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuil
RenderXmlFormattedMessage(logEvent, target);
if (target.Length == orgLength && IncludeEmptyValue && !string.IsNullOrEmpty(NodeName))
{
BeginXmlDocument(target, NodeName);
EndXmlDocument(target, NodeName);
target.Append('<');
target.Append(NodeName);
target.Append("/>");
}
}

Expand Down Expand Up @@ -252,10 +255,20 @@ private void RenderXmlFormattedMessage(LogEventInfo logEvent, StringBuilder sb)
}
if (sb.Length != orgLength)
{
sb.Append('>');
if (IndentXml)
sb.AppendLine();
bool hasNodes = NodeValue != null || Nodes.Count > 0 || IncludeMdc || IncludeMdlc || (IncludeAllProperties && logEvent.HasProperties);
if (!hasNodes)
{
sb.Append(" />");
return;
}
else
{
sb.Append('>');
if (IndentXml)
sb.AppendLine();
}
}

if (NodeValue != null)
{
int beforeNodeLength = sb.Length;
Expand Down Expand Up @@ -331,12 +344,12 @@ private void RenderXmlFormattedMessage(LogEventInfo logEvent, StringBuilder sb)

private void AppendXmlPropertyValue(string propName, object propertyValue, StringBuilder sb, bool beginXmlDocument)
{
if (string.IsNullOrEmpty(PropertiesFormatElementName))
if (string.IsNullOrEmpty(PropertiesNodeName))
return; // Not supported

string xmlKeyString = XmlHelper.XmlConvertToElementName(propName?.Trim(), false);
if (string.IsNullOrEmpty(xmlKeyString))
return;
propName = propName?.Trim();
if (string.IsNullOrEmpty(propName))
return; // Not supported

if (beginXmlDocument && !string.IsNullOrEmpty(NodeName))
{
Expand All @@ -346,29 +359,42 @@ private void AppendXmlPropertyValue(string propName, object propertyValue, Strin
if (IndentXml && !string.IsNullOrEmpty(NodeName))
sb.Append(" ");

string xmlValueString = XmlHelper.XmlConvertToStringSafe(propertyValue);

sb.Append('<');
sb.AppendFormat(PropertiesFormatElementName, xmlKeyString);
if (!string.IsNullOrEmpty(PropertiesFormatKeyAttribute))
string propNameElement = null;
if (_propertiesNodeNameFormat)
{
propNameElement = XmlHelper.XmlConvertToStringSafe(propName);
sb.AppendFormat(PropertiesNodeName, propNameElement);
}
else
{
sb.Append(PropertiesNodeName);
}

if (!string.IsNullOrEmpty(PropertiesNodeKeyAttribute))
{
string propNameAttribute = ReferenceEquals(propName, propNameElement) ? propName : XmlHelper.EscapeXmlString(propName, true);
sb.Append(' ');
sb.AppendFormat(PropertiesFormatKeyAttribute, xmlKeyString);
sb.AppendFormat(PropertiesNodeKeyAttribute, propNameAttribute);
}

if (!string.IsNullOrEmpty(PropertiesFormatValueAttribute))
string xmlValueString = XmlHelper.XmlConvertToStringSafe(propertyValue);
if (!string.IsNullOrEmpty(PropertiesNodeValueAttribute))
{
xmlValueString = XmlHelper.EscapeXmlString(xmlValueString, true);
sb.Append(' ');
sb.AppendFormat(PropertiesFormatValueAttribute, xmlValueString);
sb.AppendFormat(PropertiesNodeValueAttribute, xmlValueString);
sb.Append(" />");
}
else
{
sb.Append('>');
XmlHelper.EscapeXmlString(xmlValueString, false, sb);
sb.Append("</");
sb.AppendFormat(PropertiesFormatElementName, xmlKeyString);
if (_propertiesNodeNameFormat)
sb.AppendFormat(PropertiesNodeName, propNameElement);
else
sb.AppendFormat(PropertiesNodeName, PropertiesNodeName);
sb.Append('>');
}
if (IndentXml)
Expand Down
11 changes: 6 additions & 5 deletions tests/NLog.UnitTests/Layouts/XmlLayoutTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,13 @@ public void XmlLayoutLog4j()
Nodes =
{
new XmlLayout("log4j:message", "${message}"),
new XmlLayout("log4j:throwable", "${exception:format=tostring}") { IncludeEmptyValue = false },
new XmlLayout("log4j:throwable", "${exception:format=tostring}"),
new XmlLayout("log4j:locationInfo", null) { Attributes = { new XmlAttribute("class", "${callsite:methodName=false}") { IncludeEmptyValue = true } } },
},
};
xmlLayout.PropertiesFormatElementName = "log4j:data";
xmlLayout.PropertiesFormatKeyAttribute = "name=\"{0}\"";
xmlLayout.PropertiesFormatValueAttribute = "value=\"{0}\"";
xmlLayout.PropertiesNodeName = "log4j:data";
xmlLayout.PropertiesNodeKeyAttribute = "name=\"{0}\"";
xmlLayout.PropertiesNodeValueAttribute = "value=\"{0}\"";
xmlLayout.IncludeAllProperties = true;
xmlLayout.IncludeMdc = true;
xmlLayout.IncludeMdlc = true;
Expand All @@ -97,7 +98,7 @@ public void XmlLayoutLog4j()
var logEventInfo = LogEventInfo.Create(LogLevel.Debug, "A", null, null, "some message");
logEventInfo.Properties["nlogPropertyKey"] = "<nlog\r\nPropertyValue>";

Assert.Equal(@"<log4j:event logger=""A"" level=""DEBUG""><log4j:message>some message</log4j:message><log4j:data name=""foo1"" value=""bar1"" /><log4j:data name=""foo2"" value=""bar2"" /><log4j:data name=""foo3"" value=""bar3"" /><log4j:data name=""nlogPropertyKey"" value=""&lt;nlog&#13;&#10;PropertyValue&gt;"" /></log4j:event>", xmlLayout.Render(logEventInfo));
Assert.Equal(@"<log4j:event logger=""A"" level=""DEBUG""><log4j:message>some message</log4j:message><log4j:locationInfo class="""" /><log4j:data name=""foo1"" value=""bar1"" /><log4j:data name=""foo2"" value=""bar2"" /><log4j:data name=""foo3"" value=""bar3"" /><log4j:data name=""nlogPropertyKey"" value=""&lt;nlog&#13;&#10;PropertyValue&gt;"" /></log4j:event>", xmlLayout.Render(logEventInfo));
}
}
}

0 comments on commit 8dae8cf

Please sign in to comment.