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     String fqnCategory = escape(event.getFQNOfLoggerClass());
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 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 formatProperties(LoggingEvent event) {
240     Set keySet = event.getPropertyKeySet();
241     Hashtable hashTable = new Hashtable();
242 
243     for (Iterator iter = keySet.iterator(); iter.hasNext();) {
244       Object key = iter.next();
245       Object value = event.getProperty(key.toString());
246       hashTable.put(escape(key.toString()), escape(value.toString()));
247     }
248 
249     return hashTable;
250   }
251 
252   /* (non-Javadoc)
253      * @see org.apache.log4j.Layout#ignoresThrowable()
254      */
255   public boolean ignoresThrowable() {
256     return false;
257   }
258 
259   /* (non-Javadoc)
260    * @see org.apache.log4j.spi.OptionHandler#activateOptions()
261    */
262   public void activateOptions() {
263   }
264 
265   /* (non-Javadoc)
266    * @see org.apache.log4j.Layout#format(java.io.Writer, org.apache.log4j.spi.LoggingEvent)
267    */
268   public String format(final LoggingEvent event) {
269       LoggingEvent newEvent =  copyForHTML(event);
270       /***
271        * Layouts are not thread-safe, but are normally
272        * protected by the fact that their Appender is thread-safe.
273        * 
274        * But here in Chainsaw there is no such guarantees.
275        */ 
276       synchronized(patternLayout) {
277           return patternLayout.format(newEvent);
278       }
279   }
280 }