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 <psmith@apache.org>
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 }