#region Copyright & License // // Copyright 2001-2005 The Apache Software Foundation // // Licensed 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 { /// /// Layout that formats the log events as XML elements compatible with the log4j schema /// /// /// /// Formats the log events according to the http://logging.apache.org/log4j schema. /// /// /// Nicko Cadell public class XmlLayoutSchemaLog4j : XmlLayoutBase { #region Static Members /// /// The 1st of January 1970 in UTC /// private static readonly DateTime s_date1970 = new DateTime(1970, 1, 1); #endregion #region Constructors /// /// Constructs an XMLLayoutSchemaLog4j /// public XmlLayoutSchemaLog4j() : base() { } /// /// Constructs an XMLLayoutSchemaLog4j. /// /// /// /// The LocationInfo 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. /// /// /// If you are embedding this layout within an SMTPAppender /// then make sure to set the LocationInfo option of that /// appender as well. /// /// public XmlLayoutSchemaLog4j(bool locationInfo) : base(locationInfo) { } #endregion #region Public Properties /// /// The version of the log4j schema to use. /// /// /// /// Only version 1.2 of the log4j schema is supported. /// /// 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"); } } } #endregion /* Example log4j schema event */ /* Since log4j 1.3 the log4j:MDC has been combined into the log4j:properties element */ /// /// Actually do the writing of the xml /// /// the writer to use /// the event to write /// /// /// Generate XML that is compatible with the log4j schema. /// /// 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("log4j:event"); 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.TimeStamp.ToUniversalTime() - 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("log4j:message"); Transform.WriteEscapedXmlString(writer, loggingEvent.RenderedMessage); 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("log4j:NDC"); Transform.WriteEscapedXmlString(writer, valueStr); writer.WriteEndElement(); } } // Append the properties text PropertiesDictionary properties = loggingEvent.GetProperties(); if (properties.Count > 0) { writer.WriteStartElement("log4j:properties"); foreach(System.Collections.DictionaryEntry entry in properties) { writer.WriteStartElement("log4j:data"); 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("log4j:throwable"); Transform.WriteEscapedXmlString(writer, exceptionStr); writer.WriteEndElement(); } if (LocationInfo) { LocationInfo locationInfo = loggingEvent.LocationInformation; writer.WriteStartElement("log4j:locationInfo"); writer.WriteAttributeString("class", locationInfo.ClassName); writer.WriteAttributeString("method", locationInfo.MethodName); writer.WriteAttributeString("file", locationInfo.FileName); writer.WriteAttributeString("line", locationInfo.LineNumber); writer.WriteEndElement(); } writer.WriteEndElement(); } } }