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 SYSTEM "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      */
    103   public void setProperties(final boolean flag) {
    104       properties = flag;
    105   }
    106
    107     /***
    108      * Gets whether MDC key-value pairs should be output.
    109      * @return true if MDC key-value pairs are output.
    110      */
    111   public boolean getProperties() {
    112       return properties;
    113   }
    114
    115   /*** No options to activate. */
    116   public void activateOptions() {
    117   }
    118
    119
    120   /***
    121    * Formats a {@link org.apache.log4j.spi.LoggingEvent} in conformance with the log4j.dtd.
    122    * */
    123   public String format(final LoggingEvent event) {
    124
    125     // Reset working buffer. If the buffer is too large, then we need a new
    126     // one in order to avoid the penalty of creating a large array.
    127     if(buf.capacity() > UPPER_LIMIT) {
    128       buf = new StringBuffer(DEFAULT_SIZE);
    129     } else {
    130       buf.setLength(0);
    131     }
    132
    133     // We yield to the \r\n heresy.
    134
    135     buf.append("<log4j:event logger=\"");
    136     buf.append(Transform.escapeTags(event.getLoggerName()));
    137     buf.append("\" timestamp=\"");
    138     buf.append(event.timeStamp);
    139     buf.append("\" level=\"");
    140     buf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
    141     buf.append("\" thread=\"");
    142     buf.append(Transform.escapeTags(event.getThreadName()));
    143     buf.append("\">\r\n");
    144
    145     buf.append("<log4j:message><![CDATA[");
    146     // Append the rendered message. Also make sure to escape any
    147     // existing CDATA sections.
    148     Transform.appendEscapingCDATA(buf, event.getRenderedMessage());
    149     buf.append("]]></log4j:message>\r\n");
    150
    151     String ndc = event.getNDC();
    152     if(ndc != null) {
    153       buf.append("<log4j:NDC><![CDATA[");
    154       Transform.appendEscapingCDATA(buf, ndc);
    155       buf.append("]]></log4j:NDC>\r\n");
    156     }
    157
    158     String[] s = event.getThrowableStrRep();
    159     if(s != null) {
    160       buf.append("<log4j:throwable><![CDATA[");
    161       for(int i = 0; i < s.length; i++) {
    162           Transform.appendEscapingCDATA(buf, s[i]);
    163 	      buf.append("\r\n");
    164       }
    165       buf.append("]]></log4j:throwable>\r\n");
    166     }
    167
    168     if(locationInfo) {
    169       LocationInfo locationInfo = event.getLocationInformation();
    170       buf.append("<log4j:locationInfo class=\"");
    171       buf.append(Transform.escapeTags(locationInfo.getClassName()));
    172       buf.append("\" method=\"");
    173       buf.append(Transform.escapeTags(locationInfo.getMethodName()));
    174       buf.append("\" file=\"");
    175       buf.append(Transform.escapeTags(locationInfo.getFileName()));
    176       buf.append("\" line=\"");
    177       buf.append(locationInfo.getLineNumber());
    178       buf.append("\"/>\r\n");
    179     }
    180
    181     if (properties) {
    182         Set keySet = event.getPropertyKeySet();
    183         if (keySet.size() > 0) {
    184             buf.append("<log4j:properties>\r\n");
    185             Object[] keys = keySet.toArray();
    186             Arrays.sort(keys);
    187             for (int i = 0; i < keys.length; i++) {
    188                 String key = keys[i].toString();
    189                 Object val = event.getMDC(key);
    190                 if (val != null) {
    191                     buf.append("<log4j:data name=\"");
    192                     buf.append(Transform.escapeTags(key));
    193                     buf.append("\" value=\"");
    194                     buf.append(Transform.escapeTags(String.valueOf(val)));
    195                     buf.append("\"/>\r\n");
    196                 }
    197             }
    198             buf.append("</log4j:properties>\r\n");
    199         }
    200     }
    201
    202     buf.append("</log4j:event>\r\n\r\n");
    203
    204     return buf.toString();
    205   }
    206
    207   /***
    208      The XMLLayout prints and does not ignore exceptions. Hence the
    209      return value <code>false</code>.
    210   */
    211   public boolean ignoresThrowable() {
    212     return false;
    213   }
    214 }