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  package org.apache.log4j.chainsaw.layout;
    19
    20  import org.apache.log4j.EnhancedPatternLayout;
    21  import org.apache.log4j.Layout;
    22  import org.apache.log4j.Logger;
    23  import org.apache.log4j.spi.LocationInfo;
    24  import org.apache.log4j.spi.LoggingEvent;
    25
    26  import java.util.Hashtable;
    27  import java.util.Set;
    28
    29
    30  /**
    31   * This layout is used for formatting HTML text for use inside
    32   * the Chainsaw Event Detail Panel, and the tooltip used
    33   * when mouse-over on a particular log event row.
    34   * <p>
    35   * It relies an an internal PatternLayout to accomplish this, but ensures HTML characters
    36   * from any LoggingEvent are escaped first.
    37   *
    38   * @author Paul Smith &lt;psmith@apache.org&gt;
    39   */
    40  public class EventDetailLayout extends Layout {
    41      private final EnhancedPatternLayout patternLayout = new EnhancedPatternLayout();
    42
    43      public EventDetailLayout() {
    44      }
    45
    46      public void setConversionPattern(String conversionPattern) {
    47          patternLayout.setConversionPattern(conversionPattern);
    48          patternLayout.activateOptions();
    49      }
    50
    51      public String getConversionPattern() {
    52          return patternLayout.getConversionPattern();
    53      }
    54
    55      /* (non-Javadoc)
    56       * @see org.apache.log4j.Layout#getFooter()
    57       */
    58      public String getFooter() {
    59          return "";
    60      }
    61
    62      /* (non-Javadoc)
    63       * @see org.apache.log4j.Layout#getHeader()
    64       */
    65      public String getHeader() {
    66          return "";
    67      }
    68
    69      //  /* (non-Javadoc)
    70      //   * @see org.apache.log4j.Layout#format(java.io.Writer, org.apache.log4j.spi.LoggingEvent)
    71      //   */
    72      //  public void format(Writer output, LoggingEvent event)
    73      //    throws IOException {
    74      //    boolean pastFirst = false;
    75      //    output.write("<html><body><table cellspacing=0 cellpadding=0>");
    76      //
    77      //    List columnNames = ChainsawColumns.getColumnsNames();
    78      //
    79      //    Vector v = ChainsawAppenderHandler.convert(event);
    80      //
    81      //    /**
    82      //     * we need to add the ID property from the event
    83      //     */
    84      //    v.add(event.getProperty(ChainsawConstants.LOG4J_ID_KEY));
    85      //
    86      //    //             ListIterator iter = displayFilter.getDetailColumns().listIterator();
    87      //    Iterator iter = columnNames.iterator();
    88      //    String column = null;
    89      //    int index = -1;
    90      //
    91      //    while (iter.hasNext()) {
    92      //      column = (String) iter.next();
    93      //      index = columnNames.indexOf(column);
    94      //
    95      //      if (index > -1) {
    96      //        if (pastFirst) {
    97      //          output.write("</td></tr>");
    98      //        }
    99      //
    100     //        output.write("<tr><td valign=\"top\"><b>");
    101     //        output.write(column);
    102     //        output.write(": </b></td><td>");
    103     //
    104     //
    105     //        if (index<v.size()) {
    106     //			Object o = v.get(index);
    107     //
    108     //			if (o != null) {
    109     //				output.write(escape(o.toString()));
    110     //			} else {
    111     //				output.write("{null}");
    112     //			}
    113     //
    114     //		}else {
    115     ////            output.write("Invalid column " + column + " (index=" + index + ")");
    116     //        }
    117     //
    118     //        pastFirst = true;
    119     //      }
    120     //    }
    121     //
    122     //    output.write("</table></body></html>");
    123     //  }
    124
    125     /**
    126      * Escape &lt;, &gt; &amp; and &quot; as their entities. It is very
    127      * dumb about &amp; handling.
    128      *
    129      * @param aStr the String to escape.
    130      * @return the escaped String
    131      */
    132     private static String escape(String string) {
    133         if (string == null) {
    134             return "";
    135         }
    136
    137         final StringBuilder buf = new StringBuilder();
    138
    139         for (int i = 0; i < string.length(); i++) {
    140             char c = string.charAt(i);
    141
    142             switch (c) {
    143                 case '<':
    144                     buf.append("&lt;");
    145
    146                     break;
    147
    148                 case '>':
    149                     buf.append("&gt;");
    150
    151                     break;
    152
    153                 case '\"':
    154                     buf.append("&quot;");
    155
    156                     break;
    157
    158                 case '&':
    159                     buf.append("&amp;");
    160
    161                     break;
    162
    163                 default:
    164                     buf.append(c);
    165
    166                     break;
    167             }
    168         }
    169
    170         return buf.toString();
    171     }
    172
    173     /**
    174      * Takes a source event and copies it into a new LoggingEvent object
    175      * and ensuring all the internal elements of the event are HTML safe
    176      *
    177      * @param event
    178      * @return new LoggingEvent
    179      */
    180     private static LoggingEvent copyForHTML(LoggingEvent event) {
    181         Logger logger = Logger.getLogger(event.getLoggerName());
    182         String threadName = event.getThreadName();
    183         Object msg = escape(event.getMessage().toString());
    184         String ndc = event.getNDC();
    185 //    Hashtable mdc = formatMDC(event);
    186         LocationInfo li = null;
    187         if (event.locationInformationExists()) {
    188             li = formatLocationInfo(event);
    189         }
    190         Hashtable<String, String> properties = formatProperties(event);
    191         LoggingEvent copy = new LoggingEvent(null,
    192             logger, event.getTimeStamp(),
    193             event.getLevel(),
    194             msg,
    195             threadName,
    196             event.getThrowableInformation(),
    197             ndc,
    198             li,
    199             properties);
    200
    201         return copy;
    202     }
    203
    204 //  /**
    205 //  * @param event
    206 //  * @return
    207 //  */
    208 //  private static Hashtable formatMDC(LoggingEvent event) {
    209 //    Set keySet = event.getMDCKeySet();
    210 //    Hashtable hashTable = new Hashtable();
    211 //
    212 //    for (Iterator iter = keySet.iterator(); iter.hasNext();) {
    213 //      Object key = (Object) iter.next();
    214 //      Object value = event.getMDC(key.toString());
    215 //      hashTable.put(escape(key.toString()), escape(value.toString()));
    216 //    }
    217 //
    218 //    return hashTable;
    219 //  }
    220
    221     /**
    222      * @param event
    223      * @return
    224      */
    225     private static LocationInfo formatLocationInfo(LoggingEvent event) {
    226         LocationInfo info = event.getLocationInformation();
    227         LocationInfo newInfo =
    228             new LocationInfo(
    229                 escape(info.getFileName()), escape(info.getClassName()),
    230                 escape(info.getMethodName()), escape(info.getLineNumber()));
    231
    232         return newInfo;
    233     }
    234
    235     /**
    236      * @param event
    237      * @return
    238      */
    239     private static Hashtable<String, String> formatProperties(LoggingEvent event) {
    240         Set keySet = event.getPropertyKeySet();
    241         Hashtable<String, String> hashTable = new Hashtable<>();
    242
    243         for (Object key : keySet) {
    244             Object value = event.getProperty(key.toString());
    245             hashTable.put(escape(key.toString()), escape(value.toString()));
    246         }
    247
    248         return hashTable;
    249     }
    250
    251     /* (non-Javadoc)
    252      * @see org.apache.log4j.Layout#ignoresThrowable()
    253      */
    254     public boolean ignoresThrowable() {
    255         return false;
    256     }
    257
    258     /* (non-Javadoc)
    259      * @see org.apache.log4j.spi.OptionHandler#activateOptions()
    260      */
    261     public void activateOptions() {
    262     }
    263
    264     /* (non-Javadoc)
    265      * @see org.apache.log4j.Layout#format(java.io.Writer, org.apache.log4j.spi.LoggingEvent)
    266      */
    267     public String format(final LoggingEvent event) {
    268         LoggingEvent newEvent = copyForHTML(event);
    269         /**
    270          * Layouts are not thread-safe, but are normally
    271          * protected by the fact that their Appender is thread-safe.
    272          *
    273          * But here in Chainsaw there is no such guarantees.
    274          */
    275         synchronized (patternLayout) {
    276             return patternLayout.format(newEvent);
    277         }
    278     }
    279 }