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 java.util.Hashtable;
    21  import java.util.Iterator;
    22  import java.util.Set;
    23
    24  import org.apache.log4j.EnhancedPatternLayout;
    25  import org.apache.log4j.Layout;
    26  import org.apache.log4j.Logger;
    27  import org.apache.log4j.spi.LocationInfo;
    28  import org.apache.log4j.spi.LoggingEvent;
    29
    30
    31  /**
    32   * This layout is used for formatting HTML text for use inside
    33   * the Chainsaw Event Detail Panel, and the tooltip used
    34   * when mouse-over on a particular log event row.
    35   *
    36   * It relies an an internal PatternLayout to accomplish this, but ensures HTML characters
    37   * from any LoggingEvent are escaped first.
    38   *
    39   * @author Paul Smith &lt;psmith@apache.org&gt;
    40   */
    41  public class EventDetailLayout extends Layout {
    42    private EnhancedPatternLayout patternLayout = new EnhancedPatternLayout();
    43
    44    public EventDetailLayout() {
    45    }
    46
    47    public void setConversionPattern(String conversionPattern) {
    48      patternLayout.setConversionPattern(conversionPattern);
    49      patternLayout.activateOptions();
    50    }
    51
    52    public String getConversionPattern() {
    53      return patternLayout.getConversionPattern();
    54    }
    55
    56    /* (non-Javadoc)
    57     * @see org.apache.log4j.Layout#getFooter()
    58     */
    59    public String getFooter() {
    60      return "";
    61    }
    62
    63    /* (non-Javadoc)
    64     * @see org.apache.log4j.Layout#getHeader()
    65     */
    66    public String getHeader() {
    67      return "";
    68    }
    69
    70    //  /* (non-Javadoc)
    71    //   * @see org.apache.log4j.Layout#format(java.io.Writer, org.apache.log4j.spi.LoggingEvent)
    72    //   */
    73    //  public void format(Writer output, LoggingEvent event)
    74    //    throws IOException {
    75    //    boolean pastFirst = false;
    76    //    output.write("<html><body><table cellspacing=0 cellpadding=0>");
    77    //
    78    //    List columnNames = ChainsawColumns.getColumnsNames();
    79    //
    80    //    Vector v = ChainsawAppenderHandler.convert(event);
    81    //
    82    //    /**
    83    //     * we need to add the ID property from the event
    84    //     */
    85    //    v.add(event.getProperty(ChainsawConstants.LOG4J_ID_KEY));
    86    //
    87    //    //             ListIterator iter = displayFilter.getDetailColumns().listIterator();
    88    //    Iterator iter = columnNames.iterator();
    89    //    String column = null;
    90    //    int index = -1;
    91    //
    92    //    while (iter.hasNext()) {
    93    //      column = (String) iter.next();
    94    //      index = columnNames.indexOf(column);
    95    //
    96    //      if (index > -1) {
    97    //        if (pastFirst) {
    98    //          output.write("</td></tr>");
    99    //        }
    100   //
    101   //        output.write("<tr><td valign=\"top\"><b>");
    102   //        output.write(column);
    103   //        output.write(": </b></td><td>");
    104   //
    105   //
    106   //        if (index<v.size()) {
    107   //			Object o = v.get(index);
    108   //
    109   //			if (o != null) {
    110   //				output.write(escape(o.toString()));
    111   //			} else {
    112   //				output.write("{null}");
    113   //			}
    114   //
    115   //		}else {
    116   ////            output.write("Invalid column " + column + " (index=" + index + ")");
    117   //        }
    118   //
    119   //        pastFirst = true;
    120   //      }
    121   //    }
    122   //
    123   //    output.write("</table></body></html>");
    124   //  }
    125
    126   /**
    127     * Escape &lt;, &gt; &amp; and &quot; as their entities. It is very
    128     * dumb about &amp; handling.
    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 StringBuffer buf = new StringBuffer();
    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    * @param event
    177    * @return new LoggingEvent
    178    */
    179   private static LoggingEvent copyForHTML(LoggingEvent event) {
    180     Logger logger = Logger.getLogger(event.getLoggerName());
    181     String threadName = event.getThreadName();
    182     Object msg = escape(event.getMessage().toString());
    183     String ndc = event.getNDC();
    184 //    Hashtable mdc = formatMDC(event);
    185     LocationInfo li = null;
    186     if (event.locationInformationExists()) {
    187         li = formatLocationInfo(event);
    188     }
    189     Hashtable properties = formatProperties(event);
    190     LoggingEvent copy = new LoggingEvent(null,
    191 	   logger, event.getTimeStamp(),
    192 	   event.getLevel(),
    193 	   msg,
    194 	   threadName,
    195 	   event.getThrowableInformation(),
    196 	   ndc,
    197 	   li,
    198 	   properties);
    199
    200     return copy;
    201   }
    202
    203 //  /**
    204 //  * @param event
    205 //  * @return
    206 //  */
    207 //  private static Hashtable formatMDC(LoggingEvent event) {
    208 //    Set keySet = event.getMDCKeySet();
    209 //    Hashtable hashTable = new Hashtable();
    210 //
    211 //    for (Iterator iter = keySet.iterator(); iter.hasNext();) {
    212 //      Object key = (Object) iter.next();
    213 //      Object value = event.getMDC(key.toString());
    214 //      hashTable.put(escape(key.toString()), escape(value.toString()));
    215 //    }
    216 //
    217 //    return hashTable;
    218 //  }
    219
    220   /**
    221    * @param event
    222    * @return
    223    */
    224   private static LocationInfo formatLocationInfo(LoggingEvent event) {
    225     LocationInfo info = event.getLocationInformation();
    226     LocationInfo newInfo =
    227       new LocationInfo(
    228         escape(info.getFileName()), escape(info.getClassName()),
    229         escape(info.getMethodName()), escape(info.getLineNumber()));
    230
    231     return newInfo;
    232   }
    233
    234   /**
    235    * @param event
    236    * @return
    237    */
    238   private static Hashtable formatProperties(LoggingEvent event) {
    239     Set keySet = event.getPropertyKeySet();
    240     Hashtable hashTable = new Hashtable();
    241
    242     for (Iterator iter = keySet.iterator(); iter.hasNext();) {
    243       Object key = iter.next();
    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 }