LOG4NET-586 first cut at a namespace aware log4j XML layout

Project: http://git-wip-us.apache.org/repos/asf/logging-log4net/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4net/commit/df068324
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4net/tree/df068324
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4net/diff/df068324

Branch: refs/heads/feature/LOG4NET-586
Commit: df0683247c03d3f09f9e29085d66dbe85350b20d
Parents: 403fd19
Author: Stefan Bodewig <[email protected]>
Authored: Tue May 29 21:01:59 2018 +0200
Committer: Stefan Bodewig <[email protected]>
Committed: Wed May 30 21:44:36 2018 +0200

----------------------------------------------------------------------
 src/Layout/XmlLayoutSchemaLog4jNS.cs | 282 ++++++++++++++++++++++++++++++
 src/log4net.csproj                   |   3 +
 2 files changed, 285 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4net/blob/df068324/src/Layout/XmlLayoutSchemaLog4jNS.cs
----------------------------------------------------------------------
diff --git a/src/Layout/XmlLayoutSchemaLog4jNS.cs 
b/src/Layout/XmlLayoutSchemaLog4jNS.cs
new file mode 100644
index 0000000..aa7c33b
--- /dev/null
+++ b/src/Layout/XmlLayoutSchemaLog4jNS.cs
@@ -0,0 +1,282 @@
+#region Apache License
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to you under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+using System;
+using System.Text;
+using System.Xml;
+using System.IO;
+
+using log4net.Core;
+using log4net.Util;
+
+namespace log4net.Layout
+{
+       /// <summary>
+       /// Layout that formats the log events as XML elements similar to the 
log4j 1.2 schema
+       /// </summary>
+       /// <remarks>
+       /// <para>
+       /// Formats the log events according to the
+       /// http://logging.apache.org/log4j schema and actually puts the
+       /// elements into a namespace. This may break tooling that doesn't
+       /// handle XML using namespaces.
+       /// </para>
+       /// </remarks>
+       /// <author>Nicko Cadell</author>
+       public class XmlLayoutSchemaLog4jNS : XmlLayoutBaseNS
+       {
+               #region Static Members
+
+               /// <summary>
+               /// The 1st of January 1970 in UTC
+               /// </summary>
+               private static readonly DateTime s_date1970 = new 
DateTime(1970, 1, 1);
+
+               #endregion
+
+               #region Constructors
+
+               /// <summary>
+               /// Constructs an XMLLayoutSchemaLog4jNS
+               /// </summary>
+               public XmlLayoutSchemaLog4jNS() : base()
+               {
+               }
+
+               /// <summary>
+               /// Constructs an XMLLayoutSchemaLog4jNS.
+               /// </summary>
+               /// <remarks>
+               /// <para>
+               /// The <b>LocationInfo</b> option takes a boolean value. By
+               /// default, it is set to false which means there will be no 
location
+               /// information output by this layout. If the the option is set 
to
+               /// true, then the file name and line number of the statement
+               /// at the origin of the log statement will be output.
+               /// </para>
+               /// <para>
+               /// If you are embedding this layout within an SMTPAppender
+               /// then make sure to set the <b>LocationInfo</b> option of that
+               /// appender as well.
+               /// </para>
+               /// </remarks>
+               public XmlLayoutSchemaLog4jNS(bool locationInfo) :  
base(locationInfo)
+               {
+               }
+
+               #endregion
+
+               #region Public Properties
+
+               /// <summary>
+               /// The version of the log4j schema to use.
+               /// </summary>
+               /// <remarks>
+               /// <para>
+               /// Only version 1.2 of the log4j schema is supported.
+               /// </para>
+               /// </remarks>
+               public string Version
+               {
+                       get { return "1.2"; }
+                       set
+                       {
+                               if (value != "1.2")
+                               {
+                                       throw new ArgumentException("Only 
version 1.2 of the log4j schema is currently supported");
+                               }
+                       }
+               }
+
+               /// <summary>
+               /// The namespace URI to use for the elements and attributes 
written by this layout.
+               /// </summary>
+               /// <remarks>
+               /// <para>
+               /// Unless configured explicitly 
http://logging.apache.org/log4j is used.
+               /// </para>
+               /// </remarks>
+               public override string NamespaceUri
+               {
+                       get { return base.NamespaceUri == null ? LOG4J_SCHEMA : 
base.NamespaceUri; }
+               }
+
+               /// <summary>
+               /// The prefix to use for the elements and attributes written 
by this layout.
+               /// </summary>
+               /// <remarks>
+               /// <para>
+               /// Unless configured explicitly log4j is used.
+               /// </para>
+               /// </remarks>
+               public override string Prefix
+               {
+                       get { return base.Prefix == null ? LOG4J_PREFIX : 
base.Prefix; }
+               }
+               #endregion
+
+               /* Example log4j schema event
+
+<log4j:event logger="first logger" level="ERROR" thread="Thread-3" 
timestamp="1051494121460" xmlns:log4j="http://logging.apache.org/log4j";>
+  <log4j:message><![CDATA[errormsg 3]]></log4j:message>
+  <log4j:NDC><![CDATA[third]]></log4j:NDC>
+  <log4j:MDC>
+       <log4j:data name="some string" value="some valuethird"/>
+  </log4j:MDC>
+  <log4j:throwable><![CDATA[java.lang.Exception: someexception-third
+       at org.apache.log4j.chainsaw.Generator.run(Generator.java:94)
+]]></log4j:throwable>
+  <log4j:locationInfo class="org.apache.log4j.chainsaw.Generator"
+method="run" file="Generator.java" line="94"/>
+  <log4j:properties>
+       <log4j:data name="log4jmachinename" value="windows"/>
+       <log4j:data name="log4japp" value="udp-generator"/>
+  </log4j:properties>
+</log4j:event>
+
+               */
+
+               /* Since log4j 1.3 the log4j:MDC has been combined into the 
log4j:properties element */
+
+               /// <summary>
+               /// Actually do the writing of the xml
+               /// </summary>
+               /// <param name="writer">the writer to use</param>
+               /// <param name="loggingEvent">the event to write</param>
+               /// <remarks>
+               /// <para>
+               /// Generate XML that is compatible with the log4j schema.
+               /// </para>
+               /// </remarks>
+               override protected void FormatXml(XmlWriter writer, 
LoggingEvent loggingEvent)
+               {
+                       // Translate logging events for log4j
+
+                       // Translate hostname property
+                       if 
(loggingEvent.LookupProperty(LoggingEvent.HostNameProperty) != null &&
+                               loggingEvent.LookupProperty("log4jmachinename") 
== null)
+                       {
+                               
loggingEvent.GetProperties()["log4jmachinename"] = 
loggingEvent.LookupProperty(LoggingEvent.HostNameProperty);
+                       }
+
+                       // translate appdomain name
+                       if (loggingEvent.LookupProperty("log4japp") == null &&
+                               loggingEvent.Domain != null &&
+                               loggingEvent.Domain.Length > 0)
+                       {
+                               loggingEvent.GetProperties()["log4japp"] = 
loggingEvent.Domain;
+                       }
+
+                       // translate identity name
+                       if (loggingEvent.Identity != null &&
+                               loggingEvent.Identity.Length > 0 &&
+                               
loggingEvent.LookupProperty(LoggingEvent.IdentityProperty) == null)
+                       {
+                               
loggingEvent.GetProperties()[LoggingEvent.IdentityProperty] = 
loggingEvent.Identity;
+                       }
+
+                       // translate user name
+                       if (loggingEvent.UserName != null &&
+                               loggingEvent.UserName.Length > 0 &&
+                               
loggingEvent.LookupProperty(LoggingEvent.UserNameProperty) == null)
+                       {
+                               
loggingEvent.GetProperties()[LoggingEvent.UserNameProperty] = 
loggingEvent.UserName;
+                       }
+
+                       // Write the start element
+                       writer.WriteStartElement(Prefix, "event", NamespaceUri);
+                       writer.WriteAttributeString("logger", 
loggingEvent.LoggerName);
+
+                       // Calculate the timestamp as the number of 
milliseconds since january 1970
+                       //
+                       // We must convert the TimeStamp to UTC before 
performing any mathematical
+                       // operations. This allows use to take into account 
discontinuities
+                       // caused by daylight savings time transitions.
+                       TimeSpan timeSince1970 = loggingEvent.TimeStampUtc - 
s_date1970;
+
+                       writer.WriteAttributeString("timestamp", 
XmlConvert.ToString((long)timeSince1970.TotalMilliseconds));
+                       writer.WriteAttributeString("level", 
loggingEvent.Level.DisplayName);
+                       writer.WriteAttributeString("thread", 
loggingEvent.ThreadName);
+
+                       // Append the message text
+                       writer.WriteStartElement(Prefix, "message", 
NamespaceUri);
+                       Transform.WriteEscapedXmlString(writer, 
loggingEvent.RenderedMessage,this.InvalidCharReplacement);
+                       writer.WriteEndElement();
+
+                       object ndcObj = loggingEvent.LookupProperty("NDC");
+                       if (ndcObj != null)
+                       {
+                               string valueStr = 
loggingEvent.Repository.RendererMap.FindAndRender(ndcObj);
+
+                               if (valueStr != null && valueStr.Length > 0)
+                               {
+                                       // Append the NDC text
+                                       writer.WriteStartElement(Prefix, "NDC", 
NamespaceUri);
+                                       Transform.WriteEscapedXmlString(writer, 
valueStr,this.InvalidCharReplacement);
+                                       writer.WriteEndElement();
+                               }
+                       }
+
+                       // Append the properties text
+                       PropertiesDictionary properties = 
loggingEvent.GetProperties();
+                       if (properties.Count > 0)
+                       {
+                               writer.WriteStartElement(Prefix, "properties", 
NamespaceUri);
+                               foreach(System.Collections.DictionaryEntry 
entry in properties)
+                               {
+                                       writer.WriteStartElement(Prefix, 
"data", NamespaceUri);
+                                       writer.WriteAttributeString("name", 
(string)entry.Key);
+
+                                       // Use an ObjectRenderer to convert the 
object to a string
+                                       string valueStr = 
loggingEvent.Repository.RendererMap.FindAndRender(entry.Value);
+                                       writer.WriteAttributeString("value", 
valueStr);
+
+                                       writer.WriteEndElement();
+                               }
+                               writer.WriteEndElement();
+                       }
+
+                       string exceptionStr = loggingEvent.GetExceptionString();
+                       if (exceptionStr != null && exceptionStr.Length > 0)
+                       {
+                               // Append the stack trace line
+                               writer.WriteStartElement(Prefix, "throwable", 
NamespaceUri);
+                               Transform.WriteEscapedXmlString(writer, 
exceptionStr,this.InvalidCharReplacement);
+                               writer.WriteEndElement();
+                       }
+
+                       if (LocationInfo)
+                       {
+                               LocationInfo locationInfo = 
loggingEvent.LocationInformation;
+
+                               writer.WriteStartElement(Prefix, 
"locationInfo", NamespaceUri);
+                               writer.WriteAttributeString("class", 
locationInfo.ClassName);
+                               writer.WriteAttributeString("method", 
locationInfo.MethodName);
+                               writer.WriteAttributeString("file", 
locationInfo.FileName);
+                               writer.WriteAttributeString("line", 
locationInfo.LineNumber);
+                               writer.WriteEndElement();
+                       }
+
+                       writer.WriteEndElement();
+               }
+
+               private const string LOG4J_SCHEMA = 
"http://logging.apache.org/log4j";;
+               private const string LOG4J_PREFIX = "log4j";
+       }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4net/blob/df068324/src/log4net.csproj
----------------------------------------------------------------------
diff --git a/src/log4net.csproj b/src/log4net.csproj
index b638481..2b09c54 100644
--- a/src/log4net.csproj
+++ b/src/log4net.csproj
@@ -515,6 +515,9 @@
     <Compile Include="Layout\XmlLayoutSchemaLog4j.cs">
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="Layout\XmlLayoutSchemaLog4jNS.cs">
+      <SubType>Code</SubType>
+    </Compile>
     <Compile Include="LogicalThreadContext.cs">
       <SubType>Code</SubType>
     </Compile>

Reply via email to