001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.log4j; 019 020import org.apache.log4j.spi.LoggingEvent; 021import org.apache.log4j.spi.LocationInfo; 022import org.apache.log4j.helpers.Transform; 023 024/** 025 * This layout outputs events in a HTML table. 026 * 027 * Appenders using this layout should have their encoding 028 * set to UTF-8 or UTF-16, otherwise events containing 029 * non ASCII characters could result in corrupted 030 * log files. 031 * 032 * @author Ceki Gülcü 033 */ 034public class HTMLLayout extends Layout { 035 036 protected final int BUF_SIZE = 256; 037 protected final int MAX_CAPACITY = 1024; 038 039 static String TRACE_PREFIX = "<br> "; 040 041 // output buffer appended to when format() is invoked 042 private StringBuffer sbuf = new StringBuffer(BUF_SIZE); 043 044 /** 045 A string constant used in naming the option for setting the the 046 location information flag. Current value of this string 047 constant is <b>LocationInfo</b>. 048 049 <p>Note that all option keys are case sensitive. 050 051 @deprecated Options are now handled using the JavaBeans paradigm. 052 This constant is not longer needed and will be removed in the 053 <em>near</em> term. 054 055 */ 056 public static final String LOCATION_INFO_OPTION = "LocationInfo"; 057 058 /** 059 A string constant used in naming the option for setting the the 060 HTML document title. Current value of this string 061 constant is <b>Title</b>. 062 */ 063 public static final String TITLE_OPTION = "Title"; 064 065 // Print no location info by default 066 boolean locationInfo = false; 067 068 String title = "Log4J Log Messages"; 069 070 /** 071 The <b>LocationInfo</b> option takes a boolean value. By 072 default, it is set to false which means there will be no location 073 information output by this layout. If the the option is set to 074 true, then the file name and line number of the statement 075 at the origin of the log statement will be output. 076 077 <p>If you are embedding this layout within an {@link 078 org.apache.log4j.net.SMTPAppender} then make sure to set the 079 <b>LocationInfo</b> option of that appender as well. 080 */ 081 public 082 void setLocationInfo(boolean flag) { 083 locationInfo = flag; 084 } 085 086 /** 087 Returns the current value of the <b>LocationInfo</b> option. 088 */ 089 public 090 boolean getLocationInfo() { 091 return locationInfo; 092 } 093 094 /** 095 The <b>Title</b> option takes a String value. This option sets the 096 document title of the generated HTML document. 097 098 <p>Defaults to 'Log4J Log Messages'. 099 */ 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}