001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.html; 015 016import java.util.HashMap; 017import java.util.Map; 018 019import ch.qos.logback.core.Context; 020import ch.qos.logback.core.CoreConstants; 021import static ch.qos.logback.core.CoreConstants.LINE_SEPARATOR; 022import ch.qos.logback.core.LayoutBase; 023import ch.qos.logback.core.pattern.Converter; 024import ch.qos.logback.core.pattern.ConverterUtil; 025import ch.qos.logback.core.pattern.parser.Node; 026import ch.qos.logback.core.pattern.parser.Parser; 027import ch.qos.logback.core.spi.ScanException; 028 029/** 030 * This class is a base class for HTMLLayout classes part of other logback 031 * modules such as logback-classic and logback-access. 032 * 033 * 034 * @author Sébastien Pennec 035 */ 036public abstract class HTMLLayoutBase<E> extends LayoutBase<E> { 037 038 protected String pattern; 039 040 protected Converter<E> head; 041 042 protected String title = "Logback Log Messages"; 043 044 // It is the responsibility of derived classes to set 045 // this variable in their constructor to a default value. 046 protected CssBuilder cssBuilder; 047 048 // counter keeping track of the rows output 049 protected long counter = 0; 050 051 /** 052 * Set the <b>ConversionPattern </b> option. This is the string which controls 053 * formatting and consists of a mix of literal content and conversion 054 * specifiers. 055 */ 056 public void setPattern(String conversionPattern) { 057 pattern = conversionPattern; 058 } 059 060 /** 061 * Returns the value of the <b>ConversionPattern </b> option. 062 */ 063 public String getPattern() { 064 return pattern; 065 } 066 067 public CssBuilder getCssBuilder() { 068 return cssBuilder; 069 } 070 071 public void setCssBuilder(CssBuilder cssBuilder) { 072 this.cssBuilder = cssBuilder; 073 } 074 075 /** 076 * Parses the pattern and creates the Converter linked list. 077 */ 078 @Override 079 public void start() { 080 int errorCount = 0; 081 082 try { 083 Parser<E> p = new Parser<E>(pattern); 084 p.setContext(getContext()); 085 Node t = p.parse(); 086 this.head = p.compile(t, getEffectiveConverterMap()); 087 ConverterUtil.startConverters(this.head); 088 } catch (ScanException ex) { 089 addError("Incorrect pattern found", ex); 090 errorCount++; 091 } 092 093 if (errorCount == 0) { 094 super.started = true; 095 } 096 } 097 098 protected abstract Map<String, String> getDefaultConverterMap(); 099 100 /** 101 * Returns a map where the default converter map is merged with the map 102 * contained in the context. 103 */ 104 public Map<String, String> getEffectiveConverterMap() { 105 Map<String, String> effectiveMap = new HashMap<String, String>(); 106 107 // add the least specific map fist 108 Map<String, String> defaultMap = getDefaultConverterMap(); 109 if (defaultMap != null) { 110 effectiveMap.putAll(defaultMap); 111 } 112 113 // contextMap is more specific than the default map 114 Context context = getContext(); 115 if (context != null) { 116 @SuppressWarnings("unchecked") 117 Map<String, String> contextMap = (Map<String, String>) context 118 .getObject(CoreConstants.PATTERN_RULE_REGISTRY); 119 if (contextMap != null) { 120 effectiveMap.putAll(contextMap); 121 } 122 } 123 return effectiveMap; 124 } 125 126 /** 127 * The <b>Title </b> option takes a String value. This option sets the document 128 * title of the generated HTML document. 129 * 130 * <p> 131 * Defaults to 'Logback Log Messages'. 132 */ 133 public void setTitle(String title) { 134 this.title = title; 135 } 136 137 /** 138 * Returns the current value of the <b>Title </b> option. 139 */ 140 public String getTitle() { 141 return title; 142 } 143 144 /** 145 * Returns the content type output by this layout, i.e "text/html". 146 */ 147 @Override 148 public String getContentType() { 149 return "text/html"; 150 } 151 152 /** 153 * Returns appropriate HTML headers. 154 */ 155 @Override 156 public String getFileHeader() { 157 StringBuilder sbuf = new StringBuilder(); 158 sbuf.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""); 159 sbuf.append(" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"); 160 sbuf.append(LINE_SEPARATOR); 161 sbuf.append("<html>"); 162 sbuf.append(LINE_SEPARATOR); 163 sbuf.append(" <head>"); 164 sbuf.append(LINE_SEPARATOR); 165 sbuf.append(" <title>"); 166 sbuf.append(title); 167 sbuf.append("</title>"); 168 sbuf.append(LINE_SEPARATOR); 169 170 cssBuilder.addCss(sbuf); 171 172 sbuf.append(LINE_SEPARATOR); 173 sbuf.append(" </head>"); 174 sbuf.append(LINE_SEPARATOR); 175 sbuf.append("<body>"); 176 sbuf.append(LINE_SEPARATOR); 177 178 return sbuf.toString(); 179 } 180 181 public String getPresentationHeader() { 182 StringBuilder sbuf = new StringBuilder(); 183 sbuf.append("<hr/>"); 184 sbuf.append(LINE_SEPARATOR); 185 sbuf.append("<p>Log session start time "); 186 sbuf.append(new java.util.Date()); 187 sbuf.append("</p><p></p>"); 188 sbuf.append(LINE_SEPARATOR); 189 sbuf.append(LINE_SEPARATOR); 190 sbuf.append("<table cellspacing=\"0\">"); 191 sbuf.append(LINE_SEPARATOR); 192 193 buildHeaderRowForTable(sbuf); 194 195 return sbuf.toString(); 196 } 197 198 private void buildHeaderRowForTable(StringBuilder sbuf) { 199 Converter<E> c = head; 200 String name; 201 sbuf.append("<tr class=\"header\">"); 202 sbuf.append(LINE_SEPARATOR); 203 while (c != null) { 204 name = computeConverterName(c); 205 if (name == null) { 206 c = c.getNext(); 207 continue; 208 } 209 sbuf.append("<td class=\""); 210 sbuf.append(computeConverterName(c)); 211 sbuf.append("\">"); 212 sbuf.append(computeConverterName(c)); 213 sbuf.append("</td>"); 214 sbuf.append(LINE_SEPARATOR); 215 c = c.getNext(); 216 } 217 sbuf.append("</tr>"); 218 sbuf.append(LINE_SEPARATOR); 219 } 220 221 public String getPresentationFooter() { 222 StringBuilder sbuf = new StringBuilder(); 223 sbuf.append("</table>"); 224 return sbuf.toString(); 225 } 226 227 /** 228 * Returns the appropriate HTML footers. 229 */ 230 @Override 231 public String getFileFooter() { 232 StringBuilder sbuf = new StringBuilder(); 233 sbuf.append(LINE_SEPARATOR); 234 sbuf.append("</body></html>"); 235 return sbuf.toString(); 236 } 237 238 protected void startNewTableIfLimitReached(StringBuilder sbuf) { 239 if (this.counter >= CoreConstants.TABLE_ROW_LIMIT) { 240 counter = 0; 241 sbuf.append("</table>"); 242 sbuf.append(LINE_SEPARATOR); 243 sbuf.append("<p></p>"); 244 sbuf.append("<table cellspacing=\"0\">"); 245 sbuf.append(LINE_SEPARATOR); 246 buildHeaderRowForTable(sbuf); 247 } 248 } 249 250 protected String computeConverterName(Converter<E> c) { 251 String className = c.getClass().getSimpleName(); 252 int index = className.indexOf("Converter"); 253 if (index == -1) { 254 return className; 255 } else { 256 return className.substring(0, index); 257 } 258 } 259 260}