1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.log4j;
19
20 import org.apache.log4j.spi.LoggingEvent;
21 import org.apache.log4j.spi.LocationInfo;
22 import org.apache.log4j.helpers.Transform;
23
24 /***
25 * This layout outputs events in a HTML table.
26 *
27 * Appenders using this layout should have their encoding
28 * set to UTF-8 or UTF-16, otherwise events containing
29 * non ASCII characters could result in corrupted
30 * log files.
31 *
32 * @author Ceki Gülcü
33 */
34 public class HTMLLayout extends Layout {
35
36 protected final int BUF_SIZE = 256;
37 protected final int MAX_CAPACITY = 1024;
38
39 static String TRACE_PREFIX = "<br> ";
40
41
42 private StringBuffer sbuf = new StringBuffer(BUF_SIZE);
43
44 /***
45 A string constant used in naming the option for setting the the
46 location information flag. Current value of this string
47 constant is <b>LocationInfo</b>.
48
49 <p>Note that all option keys are case sensitive.
50
51 @deprecated Options are now handled using the JavaBeans paradigm.
52 This constant is not longer needed and will be removed in the
53 <em>near</em> term.
54
55 */
56 public static final String LOCATION_INFO_OPTION = "LocationInfo";
57
58 /***
59 A string constant used in naming the option for setting the the
60 HTML document title. Current value of this string
61 constant is <b>Title</b>.
62 */
63 public static final String TITLE_OPTION = "Title";
64
65
66 boolean locationInfo = false;
67
68 String title = "Log4J Log Messages";
69
70 /***
71 The <b>LocationInfo</b> option takes a boolean value. By
72 default, it is set to false which means there will be no location
73 information output by this layout. If the the option is set to
74 true, then the file name and line number of the statement
75 at the origin of the log statement will be output.
76
77 <p>If you are embedding this layout within an {@link
78 org.apache.log4j.net.SMTPAppender} then make sure to set the
79 <b>LocationInfo</b> option of that appender as well.
80 */
81 public
82 void setLocationInfo(boolean flag) {
83 locationInfo = flag;
84 }
85
86 /***
87 Returns the current value of the <b>LocationInfo</b> option.
88 */
89 public
90 boolean getLocationInfo() {
91 return locationInfo;
92 }
93
94 /***
95 The <b>Title</b> option takes a String value. This option sets the
96 document title of the generated HTML document.
97
98 <p>Defaults to 'Log4J Log Messages'.
99 */
100 public
101 void setTitle(String title) {
102 this.title = title;
103 }
104
105 /***
106 Returns the current value of the <b>Title</b> option.
107 */
108 public
109 String getTitle() {
110 return title;
111 }
112
113 /***
114 Returns the content type output by this layout, i.e "text/html".
115 */
116 public
117 String getContentType() {
118 return "text/html";
119 }
120
121 /***
122 No options to activate.
123 */
124 public
125 void activateOptions() {
126 }
127
128 public
129 String format(LoggingEvent event) {
130
131 if(sbuf.capacity() > MAX_CAPACITY) {
132 sbuf = new StringBuffer(BUF_SIZE);
133 } else {
134 sbuf.setLength(0);
135 }
136
137 sbuf.append(Layout.LINE_SEP + "<tr>" + Layout.LINE_SEP);
138
139 sbuf.append("<td>");
140 sbuf.append(event.timeStamp - LoggingEvent.getStartTime());
141 sbuf.append("</td>" + Layout.LINE_SEP);
142
143 String escapedThread = Transform.escapeTags(event.getThreadName());
144 sbuf.append("<td title=\"" + escapedThread + " thread\">");
145 sbuf.append(escapedThread);
146 sbuf.append("</td>" + Layout.LINE_SEP);
147
148 sbuf.append("<td title=\"Level\">");
149 if (event.getLevel().equals(Level.DEBUG)) {
150 sbuf.append("<font color=\"#339933\">");
151 sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
152 sbuf.append("</font>");
153 }
154 else if(event.getLevel().isGreaterOrEqual(Level.WARN)) {
155 sbuf.append("<font color=\"#993300\"><strong>");
156 sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
157 sbuf.append("</strong></font>");
158 } else {
159 sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
160 }
161 sbuf.append("</td>" + Layout.LINE_SEP);
162
163 String escapedLogger = Transform.escapeTags(event.getLoggerName());
164 sbuf.append("<td title=\"" + escapedLogger + " category\">");
165 sbuf.append(escapedLogger);
166 sbuf.append("</td>" + Layout.LINE_SEP);
167
168 if(locationInfo) {
169 LocationInfo locInfo = event.getLocationInformation();
170 sbuf.append("<td>");
171 sbuf.append(Transform.escapeTags(locInfo.getFileName()));
172 sbuf.append(':');
173 sbuf.append(locInfo.getLineNumber());
174 sbuf.append("</td>" + Layout.LINE_SEP);
175 }
176
177 sbuf.append("<td title=\"Message\">");
178 sbuf.append(Transform.escapeTags(event.getRenderedMessage()));
179 sbuf.append("</td>" + Layout.LINE_SEP);
180 sbuf.append("</tr>" + Layout.LINE_SEP);
181
182 if (event.getNDC() != null) {
183 sbuf.append("<tr><td bgcolor=\"#EEEEEE\" style=\"font-size : xx-small;\" colspan=\"6\" title=\"Nested Diagnostic Context\">");
184 sbuf.append("NDC: " + Transform.escapeTags(event.getNDC()));
185 sbuf.append("</td></tr>" + Layout.LINE_SEP);
186 }
187
188 String[] s = event.getThrowableStrRep();
189 if(s != null) {
190 sbuf.append("<tr><td bgcolor=\"#993300\" style=\"color:White; font-size : xx-small;\" colspan=\"6\">");
191 appendThrowableAsHTML(s, sbuf);
192 sbuf.append("</td></tr>" + Layout.LINE_SEP);
193 }
194
195 return sbuf.toString();
196 }
197
198 void appendThrowableAsHTML(String[] s, StringBuffer sbuf) {
199 if(s != null) {
200 int len = s.length;
201 if(len == 0)
202 return;
203 sbuf.append(Transform.escapeTags(s[0]));
204 sbuf.append(Layout.LINE_SEP);
205 for(int i = 1; i < len; i++) {
206 sbuf.append(TRACE_PREFIX);
207 sbuf.append(Transform.escapeTags(s[i]));
208 sbuf.append(Layout.LINE_SEP);
209 }
210 }
211 }
212
213 /***
214 Returns appropriate HTML headers.
215 */
216 public
217 String getHeader() {
218 StringBuffer sbuf = new StringBuffer();
219 sbuf.append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">" + Layout.LINE_SEP);
220 sbuf.append("<html>" + Layout.LINE_SEP);
221 sbuf.append("<head>" + Layout.LINE_SEP);
222 sbuf.append("<title>" + title + "</title>" + Layout.LINE_SEP);
223 sbuf.append("<style type=\"text/css\">" + Layout.LINE_SEP);
224 sbuf.append("<!--" + Layout.LINE_SEP);
225 sbuf.append("body, table {font-family: arial,sans-serif; font-size: x-small;}" + Layout.LINE_SEP);
226 sbuf.append("th {background: #336699; color: #FFFFFF; text-align: left;}" + Layout.LINE_SEP);
227 sbuf.append("-->" + Layout.LINE_SEP);
228 sbuf.append("</style>" + Layout.LINE_SEP);
229 sbuf.append("</head>" + Layout.LINE_SEP);
230 sbuf.append("<body bgcolor=\"#FFFFFF\" topmargin=\"6\" leftmargin=\"6\">" + Layout.LINE_SEP);
231 sbuf.append("<hr size=\"1\" noshade>" + Layout.LINE_SEP);
232 sbuf.append("Log session start time " + new java.util.Date() + "<br>" + Layout.LINE_SEP);
233 sbuf.append("<br>" + Layout.LINE_SEP);
234 sbuf.append("<table cellspacing=\"0\" cellpadding=\"4\" border=\"1\" bordercolor=\"#224466\" width=\"100%\">" + Layout.LINE_SEP);
235 sbuf.append("<tr>" + Layout.LINE_SEP);
236 sbuf.append("<th>Time</th>" + Layout.LINE_SEP);
237 sbuf.append("<th>Thread</th>" + Layout.LINE_SEP);
238 sbuf.append("<th>Level</th>" + Layout.LINE_SEP);
239 sbuf.append("<th>Category</th>" + Layout.LINE_SEP);
240 if(locationInfo) {
241 sbuf.append("<th>File:Line</th>" + Layout.LINE_SEP);
242 }
243 sbuf.append("<th>Message</th>" + Layout.LINE_SEP);
244 sbuf.append("</tr>" + Layout.LINE_SEP);
245 return sbuf.toString();
246 }
247
248 /***
249 Returns the appropriate HTML footers.
250 */
251 public
252 String getFooter() {
253 StringBuffer sbuf = new StringBuffer();
254 sbuf.append("</table>" + Layout.LINE_SEP);
255 sbuf.append("<br>" + Layout.LINE_SEP);
256 sbuf.append("</body></html>");
257 return sbuf.toString();
258 }
259
260 /***
261 The HTML layout handles the throwable contained in logging
262 events. Hence, this method return <code>false</code>. */
263 public
264 boolean ignoresThrowable() {
265 return false;
266 }
267 }