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 }