View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17
    18  // Contributors:   Mathias Bogaert
    19
    20  package org.apache.log4j.xml;
    21
    22  import org.apache.log4j.Layout;
    23  import org.apache.log4j.helpers.Transform;
    24  import org.apache.log4j.spi.LocationInfo;
    25  import org.apache.log4j.spi.LoggingEvent;
    26
    27  import java.util.Set;
    28  import java.util.Arrays;
    29
    30  /**
    31   * The output of the XMLLayout consists of a series of log4j:event
    32   * elements as defined in the <a
    33   * href="log4j.dtd">log4j.dtd</a>. It does not output a
    34   * complete well-formed XML file. The output is designed to be
    35   * included as an <em>external entity</em> in a separate file to form
    36   * a correct XML file.
    37   *
    38   * <p>For example, if <code>abc</code> is the name of the file where
    39   * the XMLLayout ouput goes, then a well-formed XML file would be:
    40   *
    41    <pre>
    42     &lt;?xml version="1.0" ?&gt;
    43
    44    &lt;!DOCTYPE log4j:eventSet PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd" [&lt;!ENTITY data SYSTEM "abc"&gt;]&gt;
    45
    46    &lt;log4j:eventSet version="1.2" xmlns:log4j="http://jakarta.apache.org/log4j/"&gt;
    47   	&nbsp;&nbsp;&data;
    48    &lt;/log4j:eventSet&gt;
    49    </pre>
    50
    51   * <p>This approach enforces the independence of the XMLLayout and the
    52   * appender where it is embedded.
    53   *
    54   * <p>The <code>version</code> attribute helps components to correctly
    55   * intrepret output generated by XMLLayout. The value of this
    56   * attribute should be "1.1" for output generated by log4j versions
    57   * prior to log4j 1.2 (final release) and "1.2" for relase 1.2 and
    58   * later.
    59   *
    60   * Appenders using this layout should have their encoding
    61   * set to UTF-8 or UTF-16, otherwise events containing
    62   * non ASCII characters could result in corrupted
    63   * log files.
    64   *
    65   * @author Ceki  G&uuml;lc&uuml;
    66   * @since 0.9.0
    67   * */
    68  public class XMLLayout extends Layout {
    69
    70    private  final int DEFAULT_SIZE = 256;
    71    private final int UPPER_LIMIT = 2048;
    72
    73    private StringBuffer buf = new StringBuffer(DEFAULT_SIZE);
    74    private boolean locationInfo = false;
    75    private boolean properties = false;
    76
    77    /**
    78     * The <b>LocationInfo</b> option takes a boolean value. By default,
    79     * it is set to false which means there will be no location
    80     * information output by this layout. If the the option is set to
    81     * true, then the file name and line number of the statement at the
    82     * origin of the log statement will be output.
    83     *
    84     * <p>If you are embedding this layout within an {@link
    85     * org.apache.log4j.net.SMTPAppender} then make sure to set the
    86     * <b>LocationInfo</b> option of that appender as well.
    87     * */
    88    public void setLocationInfo(boolean flag) {
    89      locationInfo = flag;
    90    }
    91
    92    /**
    93       Returns the current value of the <b>LocationInfo</b> option.
    94     */
    95    public boolean getLocationInfo() {
    96      return locationInfo;
    97    }
    98
    99      /**
    100      * Sets whether MDC key-value pairs should be output, default false.
    101      * @param flag new value.
    102      * @since 1.2.15
    103      */
    104   public void setProperties(final boolean flag) {
    105       properties = flag;
    106   }
    107
    108     /**
    109      * Gets whether MDC key-value pairs should be output.
    110      * @return true if MDC key-value pairs are output.
    111      * @since 1.2.15
    112      */
    113   public boolean getProperties() {
    114       return properties;
    115   }
    116
    117   /** No options to activate. */
    118   public void activateOptions() {
    119   }
    120
    121
    122   /**
    123    * Formats a {@link org.apache.log4j.spi.LoggingEvent} in conformance with the log4j.dtd.
    124    * */
    125   public String format(final LoggingEvent event) {
    126
    127     // Reset working buffer. If the buffer is too large, then we need a new
    128     // one in order to avoid the penalty of creating a large array.
    129     if(buf.capacity() > UPPER_LIMIT) {
    130       buf = new StringBuffer(DEFAULT_SIZE);
    131     } else {
    132       buf.setLength(0);
    133     }
    134
    135     // We yield to the \r\n heresy.
    136
    137     buf.append("<log4j:event logger=\"");
    138     buf.append(Transform.escapeTags(event.getLoggerName()));
    139     buf.append("\" timestamp=\"");
    140     buf.append(event.timeStamp);
    141     buf.append("\" level=\"");
    142     buf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
    143     buf.append("\" thread=\"");
    144     buf.append(Transform.escapeTags(event.getThreadName()));
    145     buf.append("\">\r\n");
    146
    147     buf.append("<log4j:message><![CDATA[");
    148     // Append the rendered message. Also make sure to escape any
    149     // existing CDATA sections.
    150     Transform.appendEscapingCDATA(buf, event.getRenderedMessage());
    151     buf.append("]]></log4j:message>\r\n");
    152
    153     String ndc = event.getNDC();
    154     if(ndc != null) {
    155       buf.append("<log4j:NDC><![CDATA[");
    156       Transform.appendEscapingCDATA(buf, ndc);
    157       buf.append("]]></log4j:NDC>\r\n");
    158     }
    159
    160     String[] s = event.getThrowableStrRep();
    161     if(s != null) {
    162       buf.append("<log4j:throwable><![CDATA[");
    163       for(int i = 0; i < s.length; i++) {
    164           Transform.appendEscapingCDATA(buf, s[i]);
    165 	      buf.append("\r\n");
    166       }
    167       buf.append("]]></log4j:throwable>\r\n");
    168     }
    169
    170     if(locationInfo) {
    171       LocationInfo locationInfo = event.getLocationInformation();
    172       buf.append("<log4j:locationInfo class=\"");
    173       buf.append(Transform.escapeTags(locationInfo.getClassName()));
    174       buf.append("\" method=\"");
    175       buf.append(Transform.escapeTags(locationInfo.getMethodName()));
    176       buf.append("\" file=\"");
    177       buf.append(Transform.escapeTags(locationInfo.getFileName()));
    178       buf.append("\" line=\"");
    179       buf.append(locationInfo.getLineNumber());
    180       buf.append("\"/>\r\n");
    181     }
    182
    183     if (properties) {
    184         Set keySet = event.getPropertyKeySet();
    185         if (keySet.size() > 0) {
    186             buf.append("<log4j:properties>\r\n");
    187             Object[] keys = keySet.toArray();
    188             Arrays.sort(keys);
    189             for (int i = 0; i < keys.length; i++) {
    190                 String key = keys[i].toString();
    191                 Object val = event.getMDC(key);
    192                 if (val != null) {
    193                     buf.append("<log4j:data name=\"");
    194                     buf.append(Transform.escapeTags(key));
    195                     buf.append("\" value=\"");
    196                     buf.append(Transform.escapeTags(String.valueOf(val)));
    197                     buf.append("\"/>\r\n");
    198                 }
    199             }
    200             buf.append("</log4j:properties>\r\n");
    201         }
    202     }
    203
    204     buf.append("</log4j:event>\r\n\r\n");
    205
    206     return buf.toString();
    207   }
    208
    209   /**
    210      The XMLLayout prints and does not ignore exceptions. Hence the
    211      return value <code>false</code>.
    212   */
    213   public boolean ignoresThrowable() {
    214     return false;
    215   }
    216 }