| #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 compatible with the log4j schema |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// Formats the log events according to the http://logging.apache.org/log4j schema. |
| /// </para> |
| /// </remarks> |
| /// <author>Nicko Cadell</author> |
| public class XmlLayoutSchemaLog4j : XmlLayoutBase |
| { |
| #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 XMLLayoutSchemaLog4j |
| /// </summary> |
| public XmlLayoutSchemaLog4j() : base() |
| { |
| } |
| |
| /// <summary> |
| /// Constructs an XMLLayoutSchemaLog4j. |
| /// </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 XmlLayoutSchemaLog4j(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"); |
| } |
| } |
| } |
| |
| #endregion |
| |
| /* Example log4j schema event |
| |
| <log4j:event logger="first logger" level="ERROR" thread="Thread-3" timestamp="1051494121460"> |
| <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> |
| protected override 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 |
| #if NETSTANDARD |
| writer.WriteStartElement("log4j", "event", "log4net"); |
| #else |
| writer.WriteStartElement("log4j:event"); |
| #endif |
| 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 |
| #if NETSTANDARD |
| writer.WriteStartElement("log4j", "message", "log4net"); |
| #else |
| writer.WriteStartElement("log4j:message"); |
| #endif |
| 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 |
| #if NETSTANDARD |
| writer.WriteStartElement("log4j", "NDC", "log4net"); |
| #else |
| writer.WriteStartElement("log4j:NDC"); |
| #endif |
| Transform.WriteEscapedXmlString(writer, valueStr, this.InvalidCharReplacement); |
| writer.WriteEndElement(); |
| } |
| } |
| |
| // Append the properties text |
| PropertiesDictionary properties = loggingEvent.GetProperties(); |
| if (properties.Count > 0) |
| { |
| #if NETSTANDARD |
| writer.WriteStartElement("log4j", "properties", "log4net"); |
| #else |
| writer.WriteStartElement("log4j:properties"); |
| #endif |
| foreach (System.Collections.DictionaryEntry entry in properties) |
| { |
| #if NETSTANDARD |
| writer.WriteStartElement("log4j", "data", "log4net"); |
| #else |
| writer.WriteStartElement("log4j:data"); |
| #endif |
| 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 |
| #if NETSTANDARD |
| writer.WriteStartElement("log4j", "throwable", "log4net"); |
| #else |
| writer.WriteStartElement("log4j:throwable"); |
| #endif |
| Transform.WriteEscapedXmlString(writer, exceptionStr, this.InvalidCharReplacement); |
| writer.WriteEndElement(); |
| } |
| |
| if (LocationInfo) |
| { |
| LocationInfo locationInfo = loggingEvent.LocationInformation; |
| |
| #if NETSTANDARD |
| writer.WriteStartElement("log4j", "locationInfo", "log4net"); |
| #else |
| writer.WriteStartElement("log4j:locationInfo"); |
| #endif |
| writer.WriteAttributeString("class", locationInfo.ClassName); |
| writer.WriteAttributeString("method", locationInfo.MethodName); |
| writer.WriteAttributeString("file", locationInfo.FileName); |
| writer.WriteAttributeString("line", locationInfo.LineNumber); |
| writer.WriteEndElement(); |
| } |
| |
| writer.WriteEndElement(); |
| } |
| } |
| } |
| |