View Javadoc

    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one
    3    *  or more contributor license agreements.  See the NOTICE file
    4    *  distributed with this work for additional information
    5    *  regarding copyright ownership.  The ASF licenses this file
    6    *  to you under the Apache License, Version 2.0 (the
    7    *  "License"); you may not use this file except in compliance
    8    *  with 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,
    13   *  software distributed under the License is distributed on an
    14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   *  KIND, either express or implied.  See the License for the
    16   *  specific language governing permissions and limitations
    17   *  under the License.
    18   *
    19   */
    20  package org.apache.mina.util;
    21
    22  import java.util.Arrays;
    23  import java.util.Map;
    24  import java.util.Set;
    25  import java.util.logging.Formatter;
    26  import java.util.logging.LogRecord;
    27
    28  import org.slf4j.MDC;
    29
    30  /**
    31   * Implementation of {@link java.util.logging.Formatter} that generates xml in the log4j format.
    32   * <p>
    33   * The generated xml corresponds 100% with what is generated by
    34   * log4j's <a href=http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/XMLLayout.html">XMLLayout</a>
    35   * <p>
    36   * The MDC properties will only be correct when <code>format</code> is called from the same thread
    37   * that generated the LogRecord.
    38   * <p>
    39   * The file and line attributes in the locationInfo element will always be "?"
    40   * since java.util.logging.LogRecord does not provide that info.
    41   * <p>
    42   * The implementation is heavily based on org.apache.log4j.xml.XMLLayout
    43   * </p>
    44   *
    45   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    46   */
    47  public class Log4jXmlFormatter extends Formatter {
    48
    49      private final int DEFAULT_SIZE = 256;
    50
    51      private final int UPPER_LIMIT = 2048;
    52
    53      private StringBuffer buf = new StringBuffer(DEFAULT_SIZE);
    54
    55      private boolean locationInfo = false;
    56
    57      private boolean properties = false;
    58
    59      /**
    60       * The <b>LocationInfo</b> option takes a boolean value. By default,
    61       * it is set to false which means there will be no location
    62       * information output by this layout. If the the option is set to
    63       * true, then the file name and line number of the statement at the
    64       * origin of the log statement will be output.
    65       *
    66       * @param flag whether locationInfo should be output by this layout
    67       */
    68      public void setLocationInfo(boolean flag) {
    69          locationInfo = flag;
    70      }
    71
    72      /**
    73       * Returns the current value of the <b>LocationInfo</b> option.
    74       *
    75       * @return whether locationInfo will be output by this layout
    76       */
    77      public boolean getLocationInfo() {
    78          return locationInfo;
    79      }
    80
    81      /**
    82       * Sets whether MDC key-value pairs should be output, default false.
    83       *
    84       * @param flag new value.
    85       */
    86      public void setProperties(final boolean flag) {
    87          properties = flag;
    88      }
    89
    90      /**
    91       * Gets whether MDC key-value pairs should be output.
    92       *
    93       * @return true if MDC key-value pairs are output.
    94       */
    95      public boolean getProperties() {
    96          return properties;
    97      }
    98
    99      @Override
    100     @SuppressWarnings("unchecked")
    101     public String format(final LogRecord record) {
    102         // Reset working buffer. If the buffer is too large, then we need a new
    103         // one in order to avoid the penalty of creating a large array.
    104         if (buf.capacity() > UPPER_LIMIT) {
    105             buf = new StringBuffer(DEFAULT_SIZE);
    106         } else {
    107             buf.setLength(0);
    108         }
    109         buf.append("<log4j:event logger=\"");
    110         buf.append(Transform.escapeTags(record.getLoggerName()));
    111         buf.append("\" timestamp=\"");
    112         buf.append(record.getMillis());
    113         buf.append("\" level=\"");
    114
    115         buf.append(Transform.escapeTags(record.getLevel().getName()));
    116         buf.append("\" thread=\"");
    117         buf.append(String.valueOf(record.getThreadID()));
    118         buf.append("\">\r\n");
    119
    120         buf.append("<log4j:message><![CDATA[");
    121         // Append the rendered message. Also make sure to escape any
    122         // existing CDATA sections.
    123         Transform.appendEscapingCDATA(buf, record.getMessage());
    124         buf.append("]]></log4j:message>\r\n");
    125
    126         if (record.getThrown() != null) {
    127             String[] s = Transform.getThrowableStrRep(record.getThrown());
    128             if (s != null) {
    129                 buf.append("<log4j:throwable><![CDATA[");
    130                 for (String value : s) {
    131                     Transform.appendEscapingCDATA(buf, value);
    132                     buf.append("\r\n");
    133                 }
    134                 buf.append("]]></log4j:throwable>\r\n");
    135             }
    136         }
    137
    138         if (locationInfo) {
    139             buf.append("<log4j:locationInfo class=\"");
    140             buf.append(Transform.escapeTags(record.getSourceClassName()));
    141             buf.append("\" method=\"");
    142             buf.append(Transform.escapeTags(record.getSourceMethodName()));
    143             buf.append("\" file=\"?\" line=\"?\"/>\r\n");
    144         }
    145
    146         if (properties) {
    147             Map contextMap = MDC.getCopyOfContextMap();
    148             if (contextMap != null) {
    149                 Set keySet = contextMap.keySet();
    150                 if ((keySet != null) && (keySet.size() > 0)) {
    151                     buf.append("<log4j:properties>\r\n");
    152                     Object[] keys = keySet.toArray();
    153                     Arrays.sort(keys);
    154                     for (Object key1 : keys) {
    155                         String key = (key1 == null ? "" : key1.toString());
    156                         Object val = contextMap.get(key);
    157                         if (val != null) {
    158                             buf.append("<log4j:data name=\"");
    159                             buf.append(Transform.escapeTags(key));
    160                             buf.append("\" value=\"");
    161                             buf.append(Transform.escapeTags(String.valueOf(val)));
    162                             buf.append("\"/>\r\n");
    163                         }
    164                     }
    165                     buf.append("</log4j:properties>\r\n");
    166                 }
    167             }
    168
    169         }
    170         buf.append("</log4j:event>\r\n\r\n");
    171
    172         return buf.toString();
    173     }
    174
    175 }