I am trying to create the following XML via code:
<log4j:event logger="MyTools" level="WARN" timestamp="763">
<log4j:message>This is a log message.</log4j:message>
</log4j:event>
But I do not manage to get rid of the xmlns:log4j="http://my-project.org/log4j/"
added. At the moment I am testing the following code (see HERE):
XNamespace ns = "http://my-project.org/log4j/";
var root = new XElement("root", new XAttribute(XNamespace.Xmlns + "log4j", ns));
var eventElement = new XElement(ns + "event");
root.Add(eventElement);
eventElement.SetAttributeValue("logger", "MyTools");
eventElement.SetAttributeValue("level", "WARN");
eventElement.SetAttributeValue("timestamp", DateTime.Now.Millisecond);
eventElement.SetElementValue(ns + "message", "This is a log message.");
When I convert eventElement
to string I get the namespace added to the log4j:event
node and when converting root
it is added to the root element only (as it should be IMO for valid XML), but how to get the event node only WITHOUT the namespace declaration as an XML fragment/node only?
I have also tried using XmlWriter but same result there.
I am open for other aproaches also.
At the moment I use String.Replace(" xmlns:log4j=\"" + ns + "\"", string.Empty)
to get rid of it, but that is rather slow (making the complete method ~50% slower) and as this can happen at a high frequency (=logger) I would like to make it as fast as possible.
The reason I need it without the namespace declaration is that some log listeners do not like the namespace and crash if it is there (log is sent via UDP to log listeners).
What I am trying to improve is the following NLog renderer:
https://github.com/NLog/NLog/blob/master/src/NLog/LayoutRenderers/Log4JXmlEventLayoutRenderer.cs
Aside from the slow Replace
(see Ln 300) I also replaced the callsite / UserStackFrame
to use the [CallerMemberName]
parameters and such implemented by my own log wrapper. I could speed up the log generation from >100ms/1k logs on my test system to ~35ms/1k when just leaving the namespace in there (as it should be for valid XML), but as already said some log listeners (i.e. Sentinel) crash when it is there...
The snippet you have shown is well-formed in the world of XML without namespaces, if you really need to create such a snippet with .NET then a legacy XmlTextWriter
allows that as follows:
using (StringWriter sw = new StringWriter())
{
using (XmlTextWriter xtw = new XmlTextWriter(sw))
{
xtw.Namespaces = false;
xtw.Formatting = Formatting.Indented;
xtw.WriteStartElement("log4j:event");
xtw.WriteAttributeString("logger", "MyTools");
xtw.WriteAttributeString("level", "WARN");
xtw.WriteAttributeString("timestamp", "763");
xtw.WriteElementString("log4j:message", "This is a log message.");
xtw.WriteEndElement();
}
string result = sw.ToString();
Console.WriteLine(result);
}
Result:
<log4j:event logger="MyTools" level="WARN" timestamp="763">
<log4j:message>This is a log message.</log4j:message>
</log4j:event>
Be warned however that nowadays most XML parsers and APIs expect namespace well-formed XML (where a name with a colon is only allowed if the prefix before the colon is bound to a namespace) so the created snippet could not be processed with LINQ to XML for instance or the default XmlReader created with XmlReader.Create.