XmlLayoutSchemaLog4j.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #region Apache License
  2. //
  3. // Licensed to the Apache Software Foundation (ASF) under one or more
  4. // contributor license agreements. See the NOTICE file distributed with
  5. // this work for additional information regarding copyright ownership.
  6. // The ASF licenses this file to you under the Apache License, Version 2.0
  7. // (the "License"); you may not use this file except in compliance with
  8. // the License. You may obtain a copy of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. //
  18. #endregion
  19. using System;
  20. using System.Text;
  21. using System.Xml;
  22. using System.IO;
  23. using log4net.Core;
  24. using log4net.Util;
  25. namespace log4net.Layout
  26. {
  27. /// <summary>
  28. /// Layout that formats the log events as XML elements compatible with the log4j schema
  29. /// </summary>
  30. /// <remarks>
  31. /// <para>
  32. /// Formats the log events according to the http://logging.apache.org/log4j schema.
  33. /// </para>
  34. /// </remarks>
  35. /// <author>Nicko Cadell</author>
  36. public class XmlLayoutSchemaLog4j : XmlLayoutBase
  37. {
  38. #region Static Members
  39. /// <summary>
  40. /// The 1st of January 1970 in UTC
  41. /// </summary>
  42. private static readonly DateTime s_date1970 = new DateTime(1970, 1, 1);
  43. #endregion
  44. #region Constructors
  45. /// <summary>
  46. /// Constructs an XMLLayoutSchemaLog4j
  47. /// </summary>
  48. public XmlLayoutSchemaLog4j() : base()
  49. {
  50. }
  51. /// <summary>
  52. /// Constructs an XMLLayoutSchemaLog4j.
  53. /// </summary>
  54. /// <remarks>
  55. /// <para>
  56. /// The <b>LocationInfo</b> option takes a boolean value. By
  57. /// default, it is set to false which means there will be no location
  58. /// information output by this layout. If the the option is set to
  59. /// true, then the file name and line number of the statement
  60. /// at the origin of the log statement will be output.
  61. /// </para>
  62. /// <para>
  63. /// If you are embedding this layout within an SMTPAppender
  64. /// then make sure to set the <b>LocationInfo</b> option of that
  65. /// appender as well.
  66. /// </para>
  67. /// </remarks>
  68. public XmlLayoutSchemaLog4j(bool locationInfo) : base(locationInfo)
  69. {
  70. }
  71. #endregion
  72. #region Public Properties
  73. /// <summary>
  74. /// The version of the log4j schema to use.
  75. /// </summary>
  76. /// <remarks>
  77. /// <para>
  78. /// Only version 1.2 of the log4j schema is supported.
  79. /// </para>
  80. /// </remarks>
  81. public string Version
  82. {
  83. get { return "1.2"; }
  84. set
  85. {
  86. if (value != "1.2")
  87. {
  88. throw new ArgumentException("Only version 1.2 of the log4j schema is currently supported");
  89. }
  90. }
  91. }
  92. #endregion
  93. /* Example log4j schema event
  94. <log4j:event logger="first logger" level="ERROR" thread="Thread-3" timestamp="1051494121460">
  95. <log4j:message><![CDATA[errormsg 3]]></log4j:message>
  96. <log4j:NDC><![CDATA[third]]></log4j:NDC>
  97. <log4j:MDC>
  98. <log4j:data name="some string" value="some valuethird"/>
  99. </log4j:MDC>
  100. <log4j:throwable><![CDATA[java.lang.Exception: someexception-third
  101. at org.apache.log4j.chainsaw.Generator.run(Generator.java:94)
  102. ]]></log4j:throwable>
  103. <log4j:locationInfo class="org.apache.log4j.chainsaw.Generator"
  104. method="run" file="Generator.java" line="94"/>
  105. <log4j:properties>
  106. <log4j:data name="log4jmachinename" value="windows"/>
  107. <log4j:data name="log4japp" value="udp-generator"/>
  108. </log4j:properties>
  109. </log4j:event>
  110. */
  111. /* Since log4j 1.3 the log4j:MDC has been combined into the log4j:properties element */
  112. /// <summary>
  113. /// Actually do the writing of the xml
  114. /// </summary>
  115. /// <param name="writer">the writer to use</param>
  116. /// <param name="loggingEvent">the event to write</param>
  117. /// <remarks>
  118. /// <para>
  119. /// Generate XML that is compatible with the log4j schema.
  120. /// </para>
  121. /// </remarks>
  122. override protected void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
  123. {
  124. // Translate logging events for log4j
  125. // Translate hostname property
  126. if (loggingEvent.LookupProperty(LoggingEvent.HostNameProperty) != null &&
  127. loggingEvent.LookupProperty("log4jmachinename") == null)
  128. {
  129. loggingEvent.GetProperties()["log4jmachinename"] = loggingEvent.LookupProperty(LoggingEvent.HostNameProperty);
  130. }
  131. // translate appdomain name
  132. if (loggingEvent.LookupProperty("log4japp") == null &&
  133. loggingEvent.Domain != null &&
  134. loggingEvent.Domain.Length > 0)
  135. {
  136. loggingEvent.GetProperties()["log4japp"] = loggingEvent.Domain;
  137. }
  138. // translate identity name
  139. if (loggingEvent.Identity != null &&
  140. loggingEvent.Identity.Length > 0 &&
  141. loggingEvent.LookupProperty(LoggingEvent.IdentityProperty) == null)
  142. {
  143. loggingEvent.GetProperties()[LoggingEvent.IdentityProperty] = loggingEvent.Identity;
  144. }
  145. // translate user name
  146. if (loggingEvent.UserName != null &&
  147. loggingEvent.UserName.Length > 0 &&
  148. loggingEvent.LookupProperty(LoggingEvent.UserNameProperty) == null)
  149. {
  150. loggingEvent.GetProperties()[LoggingEvent.UserNameProperty] = loggingEvent.UserName;
  151. }
  152. // Write the start element
  153. writer.WriteStartElement("log4j:event");
  154. writer.WriteAttributeString("logger", loggingEvent.LoggerName);
  155. // Calculate the timestamp as the number of milliseconds since january 1970
  156. //
  157. // We must convert the TimeStamp to UTC before performing any mathematical
  158. // operations. This allows use to take into account discontinuities
  159. // caused by daylight savings time transitions.
  160. TimeSpan timeSince1970 = loggingEvent.TimeStampUtc - s_date1970;
  161. writer.WriteAttributeString("timestamp", XmlConvert.ToString((long)timeSince1970.TotalMilliseconds));
  162. writer.WriteAttributeString("level", loggingEvent.Level.DisplayName);
  163. writer.WriteAttributeString("thread", loggingEvent.ThreadName);
  164. // Append the message text
  165. writer.WriteStartElement("log4j:message");
  166. Transform.WriteEscapedXmlString(writer, loggingEvent.RenderedMessage,this.InvalidCharReplacement);
  167. writer.WriteEndElement();
  168. object ndcObj = loggingEvent.LookupProperty("NDC");
  169. if (ndcObj != null)
  170. {
  171. string valueStr = loggingEvent.Repository.RendererMap.FindAndRender(ndcObj);
  172. if (valueStr != null && valueStr.Length > 0)
  173. {
  174. // Append the NDC text
  175. writer.WriteStartElement("log4j:NDC");
  176. Transform.WriteEscapedXmlString(writer, valueStr,this.InvalidCharReplacement);
  177. writer.WriteEndElement();
  178. }
  179. }
  180. // Append the properties text
  181. PropertiesDictionary properties = loggingEvent.GetProperties();
  182. if (properties.Count > 0)
  183. {
  184. writer.WriteStartElement("log4j:properties");
  185. foreach(System.Collections.DictionaryEntry entry in properties)
  186. {
  187. writer.WriteStartElement("log4j:data");
  188. writer.WriteAttributeString("name", (string)entry.Key);
  189. // Use an ObjectRenderer to convert the object to a string
  190. string valueStr = loggingEvent.Repository.RendererMap.FindAndRender(entry.Value);
  191. writer.WriteAttributeString("value", valueStr);
  192. writer.WriteEndElement();
  193. }
  194. writer.WriteEndElement();
  195. }
  196. string exceptionStr = loggingEvent.GetExceptionString();
  197. if (exceptionStr != null && exceptionStr.Length > 0)
  198. {
  199. // Append the stack trace line
  200. writer.WriteStartElement("log4j:throwable");
  201. Transform.WriteEscapedXmlString(writer, exceptionStr,this.InvalidCharReplacement);
  202. writer.WriteEndElement();
  203. }
  204. if (LocationInfo)
  205. {
  206. LocationInfo locationInfo = loggingEvent.LocationInformation;
  207. writer.WriteStartElement("log4j:locationInfo");
  208. writer.WriteAttributeString("class", locationInfo.ClassName);
  209. writer.WriteAttributeString("method", locationInfo.MethodName);
  210. writer.WriteAttributeString("file", locationInfo.FileName);
  211. writer.WriteAttributeString("line", locationInfo.LineNumber);
  212. writer.WriteEndElement();
  213. }
  214. writer.WriteEndElement();
  215. }
  216. }
  217. }