1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
57
58
59 public String getFooter() {
60 return "";
61 }
62
63
64
65
66 public String getHeader() {
67 return "";
68 }
69
70
71
72
73
74
75
76
77
78
79
80
81
82 /***
83 // * we need to add the ID property from the event
84 // */
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126 /***
127 * Escape <, > & and " as their entities. It is very
128 * dumb about & 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("<");
145
146 break;
147
148 case '>':
149 buf.append(">");
150
151 break;
152
153 case '\"':
154 buf.append(""");
155
156 break;
157
158 case '&':
159 buf.append("&");
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
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
209
210
211
212
213
214
215
216
217
218
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
253
254
255 public boolean ignoresThrowable() {
256 return false;
257 }
258
259
260
261
262 public void activateOptions() {
263 }
264
265
266
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 }